diff --git a/e2e/src/test/groovy/io/kubernetes/client/e2e/dynamic/DynamicApiTest.groovy b/e2e/src/test/groovy/io/kubernetes/client/e2e/dynamic/DynamicApiTest.groovy
new file mode 100644
index 0000000000..551a3f761a
--- /dev/null
+++ b/e2e/src/test/groovy/io/kubernetes/client/e2e/dynamic/DynamicApiTest.groovy
@@ -0,0 +1,26 @@
+package io.kubernetes.client.e2e.dynamic
+
+
+import io.kubernetes.client.openapi.models.V1Namespace
+import io.kubernetes.client.openapi.models.V1ObjectMeta
+import io.kubernetes.client.util.ClientBuilder
+import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi
+import io.kubernetes.client.util.generic.dynamic.Dynamics
+import spock.lang.Specification
+
+class DynamicApiTest extends Specification {
+ def "Create Namespace then Delete should work"() {
+ given:
+ def apiClient = ClientBuilder.defaultClient()
+ def dynamicApi = new DynamicKubernetesApi("", "v1", "namespaces", apiClient)
+ def namespaceFoo = new V1Namespace().metadata(new V1ObjectMeta().name("e2e-dynamic"))
+ when:
+ def createdNamespace = dynamicApi.create(Dynamics.newFromJson(apiClient.getJSON().serialize(namespaceFoo)))
+ then:
+ createdNamespace != null
+ when:
+ def deleted = dynamicApi.delete("e2e-dynamic").throwsApiException().getObject()
+ then:
+ deleted != null
+ }
+}
diff --git a/examples/examples-release-12/pom.xml b/examples/examples-release-12/pom.xml
index 961892a6e1..85389435e4 100644
--- a/examples/examples-release-12/pom.xml
+++ b/examples/examples-release-12/pom.xml
@@ -5,7 +5,7 @@
io.kubernetes
client-java-examples-parent
11.0.1-SNAPSHOT
- ..
+ ../pom.xml
client-java-examples-release-12
diff --git a/examples/examples-release-12/src/main/java/io/kubernetes/client/examples/DynamicClientExample.java b/examples/examples-release-12/src/main/java/io/kubernetes/client/examples/DynamicClientExample.java
new file mode 100644
index 0000000000..7bb9a50cb1
--- /dev/null
+++ b/examples/examples-release-12/src/main/java/io/kubernetes/client/examples/DynamicClientExample.java
@@ -0,0 +1,47 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.examples;
+
+import java.io.FileReader;
+import java.io.IOException;
+
+import io.kubernetes.client.openapi.ApiClient;
+import io.kubernetes.client.openapi.ApiException;
+import io.kubernetes.client.util.ClientBuilder;
+import io.kubernetes.client.util.KubeConfig;
+import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesApi;
+import io.kubernetes.client.util.generic.dynamic.DynamicKubernetesObject;
+
+public class DynamicClientExample {
+
+ public static void main(String[] args) throws IOException, ApiException {
+
+ ApiClient apiClient = ClientBuilder.standard().build();
+
+ // retrieving the latest state of the default namespace
+ DynamicKubernetesApi dynamicApi = new DynamicKubernetesApi("", "v1", "namespaces", apiClient);
+ DynamicKubernetesObject defaultNamespace = dynamicApi.get("default")
+ .throwsApiException()
+ .getObject();
+
+ // attaching a "foo=bar" label to the default namespace
+ defaultNamespace.setMetadata(defaultNamespace.getMetadata().putLabelsItem("foo", "bar"));
+ DynamicKubernetesObject updatedDefaultNamespace = dynamicApi.update(defaultNamespace)
+ .throwsApiException()
+ .getObject();
+
+ System.out.println(updatedDefaultNamespace);
+
+ apiClient.getHttpClient().connectionPool().evictAll();
+ }
+}
diff --git a/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesApi.java b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesApi.java
new file mode 100644
index 0000000000..6dff9f97b4
--- /dev/null
+++ b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesApi.java
@@ -0,0 +1,77 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.util.generic.dynamic;
+
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import io.kubernetes.client.common.KubernetesListObject;
+import io.kubernetes.client.common.KubernetesObject;
+import io.kubernetes.client.openapi.ApiClient;
+import io.kubernetes.client.util.generic.GenericKubernetesApi;
+
+/**
+ * DynamicKubernetesApi can be used for reading and writing arbitrary resources without knowing it's
+ * java definitions.
+ */
+public class DynamicKubernetesApi
+ extends GenericKubernetesApi {
+
+ private final Gson gson;
+
+ /**
+ * Instantiates a new Dynamic kubernetes api.
+ *
+ * @param apiGroup the api group
+ * @param apiVersion the api version
+ * @param resourcePlural the resource plural
+ * @param apiClient the api client
+ */
+ public DynamicKubernetesApi(
+ String apiGroup, String apiVersion, String resourcePlural, ApiClient apiClient) {
+ super(
+ DynamicKubernetesObject.class,
+ DynamicKubernetesListObject.class,
+ apiGroup,
+ apiVersion,
+ resourcePlural,
+ apiClient);
+ TypeAdapter objAdapter =
+ apiClient.getJSON().getGson().getAdapter(KubernetesObject.class);
+ TypeAdapter objListAdapter =
+ apiClient.getJSON().getGson().getAdapter(KubernetesListObject.class);
+ if (!DynamicKubernetesTypeAdaptorFactory.GenericListObjectCreator.class.equals(
+ objListAdapter.getClass())
+ || !DynamicKubernetesTypeAdaptorFactory.GenericObjectCreator.class.equals(
+ objAdapter.getClass())) {
+ apiClient
+ .getJSON()
+ .setGson(
+ apiClient
+ .getJSON()
+ .getGson()
+ .newBuilder()
+ .registerTypeAdapterFactory(new DynamicKubernetesTypeAdaptorFactory())
+ .create());
+ }
+ gson = apiClient.getJSON().getGson();
+ }
+
+ /**
+ * Gets gson instance.
+ *
+ * @return the gson
+ */
+ public Gson getGson() {
+ return gson;
+ }
+}
diff --git a/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesListObject.java b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesListObject.java
new file mode 100644
index 0000000000..a11fb44221
--- /dev/null
+++ b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesListObject.java
@@ -0,0 +1,96 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.util.generic.dynamic;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import io.kubernetes.client.common.KubernetesListObject;
+import io.kubernetes.client.openapi.Configuration;
+import io.kubernetes.client.openapi.models.V1ListMeta;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * DynamicKubernetesListObject is a wrapper for {@link JsonObject} that fits the common kubernetes
+ * list type interface {@link KubernetesListObject}.
+ */
+public class DynamicKubernetesListObject implements KubernetesListObject {
+
+ public DynamicKubernetesListObject() {
+ this(new JsonObject());
+ }
+
+ public DynamicKubernetesListObject(JsonObject obj) {
+ this.raw = obj;
+ }
+
+ private final JsonObject raw;
+
+ @Override
+ public V1ListMeta getMetadata() {
+ return Configuration.getDefaultApiClient()
+ .getJSON()
+ .getGson()
+ .fromJson(this.raw.get("metadata"), V1ListMeta.class);
+ }
+
+ @Override
+ public String getApiVersion() {
+ return this.raw.get("apiVersion").getAsString();
+ }
+
+ @Override
+ public String getKind() {
+ return this.raw.get("kind").getAsString();
+ }
+
+ @Override
+ public List getItems() {
+ List list = new ArrayList<>();
+ for (JsonElement e : this.raw.get("items").getAsJsonArray()) {
+ list.add(new DynamicKubernetesObject(e.getAsJsonObject()));
+ }
+ return list;
+ }
+
+ public JsonObject getRaw() {
+ return raw;
+ }
+
+ public void setApiVersion(String apiVersion) {
+ this.raw.addProperty("apiVersion", apiVersion);
+ }
+
+ public void setKind(String kind) {
+ this.raw.addProperty("kind", kind);
+ }
+
+ public void setMetadata(V1ListMeta objectMeta) {
+ this.raw.add(
+ "metadata", Configuration.getDefaultApiClient().getJSON().getGson().toJsonTree(objectMeta));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DynamicKubernetesListObject that = (DynamicKubernetesListObject) o;
+ return Objects.equals(raw, that.raw);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(raw);
+ }
+}
diff --git a/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesObject.java b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesObject.java
new file mode 100644
index 0000000000..2133a23581
--- /dev/null
+++ b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesObject.java
@@ -0,0 +1,84 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.util.generic.dynamic;
+
+import com.google.gson.JsonObject;
+import io.kubernetes.client.common.KubernetesObject;
+import io.kubernetes.client.openapi.Configuration;
+import io.kubernetes.client.openapi.models.V1ObjectMeta;
+import java.util.Objects;
+
+/**
+ * DynamicKubernetesObject is a wrapper for {@link JsonObject} that fits the common kubernetes type
+ * interface {@link KubernetesObject}.
+ */
+public class DynamicKubernetesObject implements KubernetesObject {
+
+ public DynamicKubernetesObject() {
+ this(new JsonObject());
+ }
+
+ public DynamicKubernetesObject(JsonObject obj) {
+ this.raw = obj;
+ }
+
+ private final JsonObject raw;
+
+ @Override
+ public V1ObjectMeta getMetadata() {
+ return Configuration.getDefaultApiClient()
+ .getJSON()
+ .getGson()
+ .fromJson(this.raw.get("metadata"), V1ObjectMeta.class);
+ }
+
+ @Override
+ public String getApiVersion() {
+ return this.raw.get("apiVersion").getAsString();
+ }
+
+ @Override
+ public String getKind() {
+ return this.raw.get("kind").getAsString();
+ }
+
+ public JsonObject getRaw() {
+ return raw;
+ }
+
+ public void setApiVersion(String apiVersion) {
+ this.raw.addProperty("apiVersion", apiVersion);
+ }
+
+ public void setKind(String kind) {
+ this.raw.addProperty("kind", kind);
+ }
+
+ public void setMetadata(V1ObjectMeta objectMeta) {
+ this.raw.add(
+ "metadata", Configuration.getDefaultApiClient().getJSON().getGson().toJsonTree(objectMeta));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DynamicKubernetesObject that = (DynamicKubernetesObject) o;
+ return Objects.equals(raw, that.raw);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(raw);
+ }
+}
diff --git a/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesTypeAdaptorFactory.java b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesTypeAdaptorFactory.java
new file mode 100644
index 0000000000..9a67fa384e
--- /dev/null
+++ b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesTypeAdaptorFactory.java
@@ -0,0 +1,103 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.util.generic.dynamic;
+
+import com.google.gson.Gson;
+import com.google.gson.InstanceCreator;
+import com.google.gson.JsonObject;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import io.kubernetes.client.common.KubernetesListObject;
+import io.kubernetes.client.common.KubernetesObject;
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+public class DynamicKubernetesTypeAdaptorFactory implements TypeAdapterFactory {
+ @Override
+ public TypeAdapter create(Gson gson, TypeToken typeToken) {
+ if (shouldHandleAsSingleObject(typeToken)) {
+ return (TypeAdapter) (new GenericObjectCreator(gson));
+ }
+ if (shouldHandleAsListObject(typeToken)) {
+ return (TypeAdapter) (new GenericListObjectCreator(gson));
+ }
+ return gson.getDelegateAdapter(this, typeToken);
+ }
+
+ private boolean shouldHandleAsSingleObject(TypeToken typeToken) {
+ if (TypeToken.get(KubernetesObject.class).equals(typeToken)) {
+ return true;
+ }
+ return TypeToken.get(DynamicKubernetesObject.class).equals(typeToken);
+ }
+
+ private boolean shouldHandleAsListObject(TypeToken typeToken) {
+ if (TypeToken.get(KubernetesListObject.class).equals(typeToken)) {
+ return true;
+ }
+ return TypeToken.get(DynamicKubernetesListObject.class).equals(typeToken);
+ }
+
+ class GenericListObjectCreator extends TypeAdapter
+ implements InstanceCreator {
+
+ GenericListObjectCreator(Gson delegate) {
+ this.delegate = delegate;
+ }
+
+ private final Gson delegate;
+
+ @Override
+ public DynamicKubernetesListObject createInstance(Type type) {
+ return new DynamicKubernetesListObject();
+ }
+
+ @Override
+ public void write(JsonWriter jsonWriter, DynamicKubernetesListObject t) throws IOException {
+ jsonWriter.jsonValue(delegate.toJson(t.getRaw()));
+ }
+
+ @Override
+ public DynamicKubernetesListObject read(JsonReader jsonReader) throws IOException {
+ return new DynamicKubernetesListObject(delegate.fromJson(jsonReader, JsonObject.class));
+ }
+ }
+
+ class GenericObjectCreator extends TypeAdapter
+ implements InstanceCreator {
+
+ GenericObjectCreator(Gson delegate) {
+ this.delegate = delegate;
+ }
+
+ private final Gson delegate;
+
+ @Override
+ public DynamicKubernetesObject createInstance(Type type) {
+ return new DynamicKubernetesObject();
+ }
+
+ @Override
+ public void write(JsonWriter jsonWriter, DynamicKubernetesObject t) throws IOException {
+ jsonWriter.jsonValue(delegate.toJson(t.getRaw()));
+ }
+
+ @Override
+ public DynamicKubernetesObject read(JsonReader jsonReader) throws IOException {
+ return new DynamicKubernetesObject(delegate.fromJson(jsonReader, JsonObject.class));
+ }
+ }
+}
diff --git a/util/src/main/java/io/kubernetes/client/util/generic/dynamic/Dynamics.java b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/Dynamics.java
new file mode 100644
index 0000000000..6ea57c592f
--- /dev/null
+++ b/util/src/main/java/io/kubernetes/client/util/generic/dynamic/Dynamics.java
@@ -0,0 +1,38 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.util.generic.dynamic;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import io.kubernetes.client.openapi.Configuration;
+
+public class Dynamics {
+
+ public static DynamicKubernetesObject newFromJson(String jsonContent) {
+ return newFromJson(Configuration.getDefaultApiClient().getJSON().getGson(), jsonContent);
+ }
+
+ public static DynamicKubernetesObject newFromJson(Gson gson, String jsonContent) {
+ JsonElement raw = gson.fromJson(jsonContent, JsonElement.class);
+ return new DynamicKubernetesObject(raw.getAsJsonObject());
+ }
+
+ public static DynamicKubernetesListObject newListFromJson(String jsonContent) {
+ return newListFromJson(Configuration.getDefaultApiClient().getJSON().getGson(), jsonContent);
+ }
+
+ public static DynamicKubernetesListObject newListFromJson(Gson gson, String jsonContent) {
+ JsonElement raw = gson.fromJson(jsonContent, JsonElement.class);
+ return new DynamicKubernetesListObject(raw.getAsJsonObject());
+ }
+}
diff --git a/util/src/test/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesApiTest.java b/util/src/test/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesApiTest.java
new file mode 100644
index 0000000000..e10e9b12e6
--- /dev/null
+++ b/util/src/test/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesApiTest.java
@@ -0,0 +1,133 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.util.generic.dynamic;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.put;
+import static com.github.tomakehurst.wiremock.client.WireMock.putRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static org.junit.Assert.*;
+
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import io.kubernetes.client.openapi.ApiClient;
+import io.kubernetes.client.openapi.ApiException;
+import io.kubernetes.client.openapi.models.V1Namespace;
+import io.kubernetes.client.openapi.models.V1NamespaceList;
+import io.kubernetes.client.openapi.models.V1ObjectMeta;
+import io.kubernetes.client.util.ClientBuilder;
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+public class DynamicKubernetesApiTest {
+
+ private static final String jsonContent =
+ new StringBuilder()
+ .append("{")
+ .append("\"apiVersion\":\"v1\",")
+ .append("\"kind\":\"CustomResource\",")
+ .append("\"metadata\":{")
+ .append("\"namespace\":\"default\",")
+ .append("\"name\":\"foo\"")
+ .append("}")
+ .append("}")
+ .toString();
+
+ private ApiClient apiClient;
+
+ @Rule public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort(), false);
+
+ @Before
+ public void setup() throws IOException {
+ apiClient = new ClientBuilder().setBasePath("http://localhost:" + wireMockRule.port()).build();
+ }
+
+ @Test
+ public void testListNamespaceShouldWork() throws ApiException {
+ V1NamespaceList expectedList =
+ new V1NamespaceList()
+ .addItemsItem(new V1Namespace().metadata(new V1ObjectMeta().name("foo1")))
+ .addItemsItem(new V1Namespace().metadata(new V1ObjectMeta().name("foo2")));
+ wireMockRule.stubFor(
+ get(urlPathEqualTo("/api/v1/namespaces"))
+ .willReturn(
+ aResponse()
+ .withStatus(200)
+ .withHeader("Content-Type", "application/json")
+ .withBody(apiClient.getJSON().serialize(expectedList))));
+ DynamicKubernetesApi api = new DynamicKubernetesApi("", "v1", "namespaces", apiClient);
+ DynamicKubernetesListObject listObj = api.list().throwsApiException().getObject();
+ assertEquals(expectedList.getItems().size(), listObj.getItems().size());
+ wireMockRule.verify(getRequestedFor(urlPathEqualTo("/api/v1/namespaces")));
+ }
+
+ @Test
+ public void testUpdateNamespaceShouldWork() throws ApiException {
+ V1Namespace updating = new V1Namespace().metadata(new V1ObjectMeta().name("foo1"));
+ wireMockRule.stubFor(
+ put(urlPathEqualTo("/api/v1/namespaces/foo1"))
+ .willReturn(
+ aResponse()
+ .withStatus(200)
+ .withHeader("Content-Type", "application/json")
+ .withBody(apiClient.getJSON().serialize(updating))));
+ DynamicKubernetesApi api = new DynamicKubernetesApi("", "v1", "namespaces", apiClient);
+ DynamicKubernetesObject updatingObj =
+ Dynamics.newFromJson(apiClient.getJSON().serialize(updating));
+ DynamicKubernetesObject updatedObj = api.update(updatingObj).throwsApiException().getObject();
+ assertEquals(updatingObj, updatedObj);
+ wireMockRule.verify(putRequestedFor(urlPathEqualTo("/api/v1/namespaces/foo1")));
+ }
+
+ @Test
+ public void testListCustomResourceShouldWork() throws ApiException {
+ wireMockRule.stubFor(
+ get(urlPathEqualTo("/apis/mygroup.io/myversion/namespaces/default/customresources"))
+ .willReturn(
+ aResponse()
+ .withStatus(200)
+ .withHeader("Content-Type", "application/json")
+ .withBody("{}")));
+ DynamicKubernetesApi api =
+ new DynamicKubernetesApi("mygroup.io", "myversion", "customresources", apiClient);
+ DynamicKubernetesListObject listObj = api.list("default").throwsApiException().getObject();
+ assertNotNull(listObj);
+ wireMockRule.verify(
+ getRequestedFor(
+ urlPathEqualTo("/apis/mygroup.io/myversion/namespaces/default/customresources")));
+ }
+
+ @Test
+ public void testUpdateCustomResourceShouldWork() throws ApiException {
+ wireMockRule.stubFor(
+ put(urlPathEqualTo("/apis/mygroup.io/myversion/namespaces/default/customresources/foo"))
+ .willReturn(
+ aResponse()
+ .withStatus(200)
+ .withHeader("Content-Type", "application/json")
+ .withBody("{}")));
+ DynamicKubernetesApi api =
+ new DynamicKubernetesApi("mygroup.io", "myversion", "customresources", apiClient);
+ DynamicKubernetesObject updatingObj = Dynamics.newFromJson(jsonContent);
+ DynamicKubernetesObject updatedObj = api.update(updatingObj).throwsApiException().getObject();
+ assertNotNull(updatedObj);
+ assertEquals("{}", apiClient.getJSON().serialize(updatedObj));
+ wireMockRule.verify(
+ putRequestedFor(
+ urlPathEqualTo("/apis/mygroup.io/myversion/namespaces/default/customresources/foo")));
+ }
+}
diff --git a/util/src/test/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesTypeAdaptorFactoryTest.java b/util/src/test/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesTypeAdaptorFactoryTest.java
new file mode 100644
index 0000000000..4c90b6baf4
--- /dev/null
+++ b/util/src/test/java/io/kubernetes/client/util/generic/dynamic/DynamicKubernetesTypeAdaptorFactoryTest.java
@@ -0,0 +1,82 @@
+/*
+Copyright 2021 The Kubernetes Authors.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package io.kubernetes.client.util.generic.dynamic;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gson.Gson;
+import io.kubernetes.client.common.KubernetesListObject;
+import io.kubernetes.client.common.KubernetesObject;
+import io.kubernetes.client.openapi.models.V1ObjectMeta;
+import org.junit.Before;
+import org.junit.Test;
+
+public class DynamicKubernetesTypeAdaptorFactoryTest {
+
+ private Gson gson;
+
+ private static final String jsonContent =
+ new StringBuilder()
+ .append("{")
+ .append("\"apiVersion\":\"v1\",")
+ .append("\"kind\":\"Namespace\",")
+ .append("\"metadata\":{")
+ .append("\"name\":\"foo\"")
+ .append("}")
+ .append("}")
+ .toString();
+
+ @Before
+ public void setup() {
+ gson =
+ new Gson()
+ .newBuilder()
+ .registerTypeAdapterFactory(new DynamicKubernetesTypeAdaptorFactory())
+ .create();
+ }
+
+ @Test
+ public void testSingleDynamicObjectRoundTrip() {
+ KubernetesObject dynamicObj = gson.fromJson(jsonContent, KubernetesObject.class);
+ assertTrue(dynamicObj instanceof DynamicKubernetesObject);
+
+ assertEquals("v1", dynamicObj.getApiVersion());
+ assertEquals("Namespace", dynamicObj.getKind());
+ assertEquals(new V1ObjectMeta().name("foo"), dynamicObj.getMetadata());
+
+ String dumped = gson.toJson(dynamicObj);
+ assertEquals(jsonContent, dumped);
+ }
+
+ @Test
+ public void testListDynamicObjectRoundTrip() {
+ String listJsonContent =
+ new StringBuilder()
+ .append("{")
+ .append("\"items\":[")
+ .append(jsonContent)
+ .append("]")
+ .append("}")
+ .toString();
+
+ KubernetesListObject dynamicListObj =
+ gson.fromJson(listJsonContent, KubernetesListObject.class);
+ assertTrue(dynamicListObj instanceof DynamicKubernetesListObject);
+
+ assertEquals(1, dynamicListObj.getItems().size());
+
+ String dumped = gson.toJson(dynamicListObj);
+ assertEquals(listJsonContent, dumped);
+ }
+}