diff --git a/src/main/java/com/contentstack/cms/stack/GlobalField.java b/src/main/java/com/contentstack/cms/stack/GlobalField.java index 71e22c1..681e2e1 100644 --- a/src/main/java/com/contentstack/cms/stack/GlobalField.java +++ b/src/main/java/com/contentstack/cms/stack/GlobalField.java @@ -37,7 +37,7 @@ public class GlobalField implements BaseImplementation { protected HashMap headers; protected HashMap params; protected String globalFiledUid; - + protected String apiVersion; protected GlobalField(Retrofit retrofit,Map headers) { this.headers = new HashMap<>(); this.headers.putAll(headers); @@ -85,7 +85,11 @@ public GlobalField addParam(@NotNull String key, @NotNull Object value) { */ @Override public GlobalField addHeader(@NotNull String key, @NotNull String value) { - this.headers.put(key, value); + if ("api_version".equalsIgnoreCase(key)) { + this.apiVersion = value; + } else { + this.headers.put(key, value); + } return this; } @@ -122,6 +126,10 @@ public GlobalField addParams(@NotNull HashMap params) { */ @Override public GlobalField addHeaders(@NotNull HashMap headers) { + if (headers.containsKey("api_version")) { + this.apiVersion = headers.get("api_version"); + headers.remove("api_version"); + } this.headers.putAll(headers); return this; } @@ -146,6 +154,21 @@ protected GlobalField clearParams() { this.params.clear(); return this; } + /* + * For Nested Global Fields the api_version is set to 3.2 which needs + * to removed from the headers inorder for other modules to function correctly + */ + + /** + * Returns a copy of the headers with api_version set if needed. + */ + private Map getRequestHeaders() { + Map requestHeaders = new HashMap<>(this.headers); + if (this.apiVersion != null) { + requestHeaders.put("api_version", this.apiVersion); + } + return requestHeaders; + } /** * Get All Global Fields @@ -167,11 +190,10 @@ protected GlobalField clearParams() { * * @see #addHeader(String, String) to add headers * @see #addParam(String, Object) to add query parameters - * @see #removeHeader(String) to remove header * @since 0.1.0 */ public Call find() { - return this.service.fetch(this.headers, this.params); + return this.service.fetch(getRequestHeaders(), this.params); } /** @@ -197,12 +219,11 @@ public Call find() { * * @see #addHeader(String, String) to add headers * @see #addParam(String, Object) to add query parameters - * @see #removeHeader(String) to remove header * @since 0.1.0 */ public Call fetch() { validate(); - return this.service.single(this.headers, this.globalFiledUid, this.params); + return this.service.single(getRequestHeaders(), this.globalFiledUid, this.params); } /** @@ -230,11 +251,10 @@ public Call fetch() { * * * @see #addHeader(String, String) to add headers - * @see #removeHeader(String) to remove header * @since 0.1.0 */ public Call create(@NotNull JSONObject requestBody) { - return this.service.create(this.headers, requestBody); + return this.service.create(getRequestHeaders(), requestBody); } /** @@ -260,12 +280,11 @@ public Call create(@NotNull JSONObject requestBody) { * * * @see #addHeader(String, String) to add headers - * @see #removeHeader(String) to remove header * @since 0.1.0 */ public Call update(@NotNull JSONObject requestBody) { validate(); - return this.service.update(this.headers, this.globalFiledUid, requestBody); + return this.service.update(getRequestHeaders(), this.globalFiledUid, requestBody); } /** @@ -286,12 +305,11 @@ public Call update(@NotNull JSONObject requestBody) { * field * * @see #addHeader(String, String) to add headers - * @see #removeHeader(String) to remove header * @since 0.1.0 */ public Call delete() { validate(); - return this.service.delete(this.headers, this.globalFiledUid); + return this.service.delete(getRequestHeaders(), this.globalFiledUid); } /** @@ -315,11 +333,10 @@ public Call delete() { * * * @see #addHeader(String, String) to add headers - * @see #removeHeader(String) to remove header * @since 0.1.0 */ public Call imports(@NotNull JSONObject body) { - return this.service.imports(this.headers, body); + return this.service.imports(getRequestHeaders(), body); } /** @@ -336,15 +353,14 @@ public Call imports(@NotNull JSONObject body) { * * * @see #addHeader(String, String) to add headers - * @see #removeHeader(String) to remove header * @since 0.1.0 */ public Call export() { validate(); - return this.service.export(this.headers, this.globalFiledUid); + return this.service.export(getRequestHeaders(), this.globalFiledUid); } - /** + /** * Restore a global field *

* The Restore a global field request allows you to restore the schema of @@ -367,11 +383,10 @@ public Call export() { * * * @see #addHeader(String, String) to add headers - * @see #removeHeader(String) to remove header * @since 0.1.0 */ public Call restore(@NotNull JSONObject requestBody) { validate(); - return this.service.restore(this.headers, this.globalFiledUid, requestBody); + return this.service.restore(getRequestHeaders(), this.globalFiledUid, requestBody); } } diff --git a/src/test/java/com/contentstack/cms/stack/GlobalFieldAPITest.java b/src/test/java/com/contentstack/cms/stack/GlobalFieldAPITest.java index 150a3f9..04d089c 100644 --- a/src/test/java/com/contentstack/cms/stack/GlobalFieldAPITest.java +++ b/src/test/java/com/contentstack/cms/stack/GlobalFieldAPITest.java @@ -1,12 +1,23 @@ package com.contentstack.cms.stack; +import java.io.IOException; +import java.util.HashMap; + import com.contentstack.cms.TestClient; import com.contentstack.cms.Utils; import com.contentstack.cms.core.Util; + import okhttp3.Request; + import org.json.simple.JSONObject; import org.junit.jupiter.api.*; +import com.contentstack.cms.Contentstack; +import com.google.gson.JsonObject; + +import okhttp3.ResponseBody; +import retrofit2.Response; + @Tag("unit") @TestInstance(TestInstance.Lifecycle.PER_CLASS) class GlobalFieldAPITest { @@ -14,6 +25,7 @@ class GlobalFieldAPITest { public static GlobalField globalField; protected static String API_KEY = TestClient.API_KEY; protected static String MANAGEMENT_TOKEN = TestClient.MANAGEMENT_TOKEN; + protected static String AUTHTOKEN = TestClient.AUTHTOKEN; protected static String globalFieldUid = "global_field_1"; protected static String globalFieldUid2 = "nested_global_field"; protected static Stack stack; @@ -175,7 +187,7 @@ void testCreateNestedGlobalField() { globalField = stack.globalField().addHeader("api_version", "3.2"); Request request = globalField.create(requestBody).request(); globalField.removeHeader("api_version"); - + Assertions.assertEquals("POST", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(2, request.url().pathSegments().size()); @@ -189,7 +201,7 @@ void testCreateNestedGlobalField() { void testFetchSingleNestedGlobalField() { globalField = stack.globalField(globalFieldUid).addHeader("api_version", "3.2"); Request request = globalField.fetch().request(); - + Assertions.assertEquals("GET", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(3, request.url().pathSegments().size()); @@ -205,7 +217,7 @@ void testFetchSingleGlobalFieldWithParams() { globalField = stack.globalField(globalFieldUid).addHeader("api_version", "3.2"); globalField.addParam("include_schema", true); Request request = globalField.fetch().request(); - + Assertions.assertEquals("GET", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(3, request.url().pathSegments().size()); @@ -222,7 +234,7 @@ void testUpdateNestedGlobalField() { globalField = stack.globalField(globalFieldUid).addHeader("api_version", "3.2"); JSONObject requestBody = Utils.readJson("globalfield/nested_global_field_update.json"); Request request = globalField.update(requestBody).request(); - + Assertions.assertEquals("PUT", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(3, request.url().pathSegments().size()); @@ -237,7 +249,7 @@ void testUpdateNestedGlobalField() { void testFindAllNestedGlobalField() { globalField = stack.globalField().addHeader("api_version", "3.2"); Request request = globalField.find().request(); - + Assertions.assertEquals("GET", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(2, request.url().pathSegments().size()); @@ -254,7 +266,7 @@ void testFindWithParams() { globalField.addParam("include_global_field_schema", true); Request request = globalField.find().request(); - + Assertions.assertEquals("GET", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(2, request.url().pathSegments().size()); @@ -268,7 +280,7 @@ void testFindWithParams() { void testDeleteNestedGlobalField() { globalField = stack.globalField(globalFieldUid).addHeader("api_version", "3.2"); Request request = globalField.delete().request(); - + Assertions.assertEquals("DELETE", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(3, request.url().pathSegments().size()); @@ -285,7 +297,7 @@ void testImportNestedGlobalField() { globalField = stack.globalField().addHeader("api_version", "3.2"); JSONObject requestBody = Utils.readJson("globalfield/nested_global_field_import.json"); Request request = globalField.imports(requestBody).request(); - + Assertions.assertEquals("POST", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(3, request.url().pathSegments().size()); @@ -300,7 +312,7 @@ void testImportNestedGlobalField() { void testExportNestedGlobalField() { globalField = stack.globalField(globalFieldUid).addHeader("api_version", "3.2"); Request request = globalField.export().request(); - + Assertions.assertEquals("GET", request.method()); Assertions.assertTrue(request.url().isHttps()); Assertions.assertEquals(4, request.url().pathSegments().size()); @@ -310,7 +322,7 @@ void testExportNestedGlobalField() { Assertions.assertEquals("export", request.url().pathSegments().get(3)); Assertions.assertNull(request.url().encodedQuery()); } - + // @Order(19) // @Test // void testRestoreNestedGlobalField() { @@ -326,5 +338,134 @@ void testExportNestedGlobalField() { // Assertions.assertEquals("restore", request.url().pathSegments().get(3)); // Assertions.assertNull(request.url().encodedQuery()); // } + @Test + void testApiVersionHeaderIsSet() { + HashMap headers = new HashMap<>(); + headers.put(Util.API_KEY, API_KEY); + headers.put(Util.AUTHORIZATION, MANAGEMENT_TOKEN); + String apiVersion = "3.2"; + Stack stack = new Contentstack.Builder().setAuthtoken(AUTHTOKEN).build().stack(headers); + GlobalField gfWithApiVersion = new GlobalField(stack.client, stack.headers, "feature"); + gfWithApiVersion.addHeader("api_version", apiVersion); + Request request = gfWithApiVersion.fetch().request(); + Assertions.assertEquals(apiVersion, request.header("api_version")); + } + + // --- Nested Global Field Test Suite --- + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class NestedGlobalFieldTests { + + GlobalField nestedGlobalField; + String nestedUid = "nested_global_field"; + String apiVersion = "3.2"; + + @BeforeEach + void setupNested() { + HashMap headers = new HashMap<>(); + headers.put(Util.API_KEY, API_KEY); + headers.put(Util.AUTHORIZATION, MANAGEMENT_TOKEN); + Stack stack = new Contentstack.Builder().setAuthtoken(AUTHTOKEN).build().stack(headers); + nestedGlobalField = new GlobalField(stack.client, stack.headers, nestedUid); + nestedGlobalField.addHeader("api_version", apiVersion); + } + + @Test + @Order(1) + void testCreateNestedGlobalField() throws IOException { + JSONObject requestBody = Utils.readJson("globalfield/nested_global_field.json"); + Request request = nestedGlobalField.create(requestBody).request(); + Assertions.assertEquals("https://api.contentstack.io/v3/global_fields", request.url().toString()); + Assertions.assertEquals("/v3/global_fields", request.url().encodedPath()); + Assertions.assertEquals("https", request.url().scheme()); + Assertions.assertEquals("POST", request.method()); + Assertions.assertEquals(apiVersion, request.header("api_version")); + Response response = nestedGlobalField.create(requestBody).execute(); + Assertions.assertEquals(201, response.code()); + } + + @Test + @Order(2) + void testGetNestedGlobalField() throws IOException { + nestedGlobalField.addParam("include_global_fields", true); + nestedGlobalField.addParam("include_validation_keys", true); + Request request = nestedGlobalField.fetch().request(); + Assertions.assertEquals("https://api.contentstack.io/v3/global_fields/" + nestedUid + "?include_global_fields=true&include_validation_keys=true", request.url().toString()); + Assertions.assertEquals("https", request.url().scheme()); + Assertions.assertEquals("GET", request.method()); + Assertions.assertEquals(apiVersion, request.header("api_version")); + Response response = nestedGlobalField.fetch().execute(); + Assertions.assertEquals(200, response.code()); + JsonObject responseBody = Utils.toJson(response).getAsJsonObject(); + JsonObject globalField = responseBody.getAsJsonObject("global_field"); + Assertions.assertEquals("Nested Global Field", globalField.get("title").getAsString()); + Assertions.assertTrue(globalField.has("referred_global_fields")); + Assertions.assertTrue(globalField.has("validation_keys")); + } + + @Test + @Order(3) + void testUpdateNestedGlobalField() throws IOException { + JSONObject requestBody = Utils.readJson("globalfield/nested_global_field_update1.json"); + Request request = nestedGlobalField.update(requestBody).request(); + Assertions.assertEquals("https://api.contentstack.io/v3/global_fields/" + nestedUid, request.url().toString()); + Assertions.assertEquals("/v3/global_fields/" + nestedUid, request.url().encodedPath()); + Assertions.assertEquals("https", request.url().scheme()); + Assertions.assertEquals("PUT", request.method()); + Assertions.assertEquals(apiVersion, request.header("api_version")); + Response response = nestedGlobalField.update(requestBody).execute(); + Assertions.assertEquals(200, response.code()); + JsonObject responseBody = Utils.toJson(response).getAsJsonObject(); + JsonObject globalField = responseBody.getAsJsonObject("global_field"); + Assertions.assertEquals("Nested Global Field", globalField.get("title").getAsString()); + + } + + @Test + @Order(4) + void testDeleteNestedGlobalField() throws IOException { + Request request = nestedGlobalField.delete().request(); + Assertions.assertEquals("https://api.contentstack.io/v3/global_fields/" + nestedUid + "?force=true", request.url().toString()); + Assertions.assertEquals("https", request.url().scheme()); + Assertions.assertEquals("DELETE", request.method()); + Assertions.assertEquals(apiVersion, request.header("api_version")); + Response response = nestedGlobalField.delete().execute(); + Assertions.assertEquals(200, response.code()); + JsonObject responseBody = Utils.toJson(response).getAsJsonObject(); + Assertions.assertEquals("Global Field deleted successfully.", responseBody.get("notice").getAsString()); + } + + @Test + void testApiVersionHeaderIsolation() throws IOException { + // Set api_version via addHeader (should not pollute shared headers) + GlobalField gfWithApiVersion = new GlobalField(stack.client, stack.headers, "feature"); + gfWithApiVersion.addHeader("api_version", apiVersion); + // Before request, shared headers should NOT have api_version + Assertions.assertFalse(stack.headers.containsKey("api_version"), + "api_version should not be present in shared headers before GlobalField request"); + // Make a request (should use a copy of headers internally) + Request rq1 = gfWithApiVersion.find().request(); + + // After request, shared headers should STILL NOT have api_version + Assertions.assertFalse(stack.headers.containsKey("api_version"), + "api_version should not be present in shared headers after GlobalField request"); + // Now, create a ContentType and make a request + ContentType ct = stack.contentType("author"); + Request rq2 = ct.fetch().request(); + JSONObject requestBody = Utils.readJson("mockcontenttype/create.json"); + Response resp1 = stack.contentType().create(requestBody).execute(); + if (resp1.isSuccessful()) { + System.out.println(resp1.body().string()); + } else { + System.out.println(resp1.errorBody().string()); + } + // Again, shared headers should not have api_version + Assertions.assertFalse(stack.headers.containsKey("api_version"), + "api_version should not be present in shared headers after ContentType request"); + // Also, ContentType's request should not have api_version header + Assertions.assertNull(rq2.header("api_version"), + "api_version should not be present in ContentType request headers"); + } + } -} \ No newline at end of file +} diff --git a/src/test/resources/globalfield/nested_global_field.json b/src/test/resources/globalfield/nested_global_field.json new file mode 100644 index 0000000..b05957f --- /dev/null +++ b/src/test/resources/globalfield/nested_global_field.json @@ -0,0 +1,44 @@ +{ + "global_field": { + "title": "Nested Global Field", + "uid": "nested_global_field", + "description": "", + "schema": [ + { + "data_type": "text", + "display_name": "Single Line Textbox", + "uid": "single_line", + "field_metadata": { + "description": "", + "default_value": "", + "version": 3 + }, + "format": "", + "error_messages": { + "format": "" + }, + "mandatory": false, + "multiple": false, + "non_localizable": false, + "unique": false, + "indexed": false, + "inbuilt_model": false + }, + { + "data_type": "global_field", + "display_name": "SEO", + "reference_to": "seo", + "field_metadata": { + "description": "" + }, + "uid": "seo", + "mandatory": false, + "multiple": false, + "non_localizable": false, + "unique": false, + "indexed": false, + "inbuilt_model": false + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/globalfield/nested_global_field_update1.json b/src/test/resources/globalfield/nested_global_field_update1.json new file mode 100644 index 0000000..22631ab --- /dev/null +++ b/src/test/resources/globalfield/nested_global_field_update1.json @@ -0,0 +1,59 @@ +{ + "global_field": { + "title": "Nested Global Field", + "uid": "nested_global_field", + "description": "", + "schema": [ + { + "data_type": "text", + "display_name": "Single Line Textbox", + "uid": "single_line", + "field_metadata": { + "description": "", + "default_value": "", + "version": 3 + }, + "format": "", + "error_messages": { + "format": "" + }, + "mandatory": false, + "multiple": false, + "non_localizable": false, + "unique": false, + "indexed": false, + "inbuilt_model": false + }, + { + "data_type": "global_field", + "display_name": "SEO", + "reference_to": "seo", + "field_metadata": { + "description": "" + }, + "uid": "seo", + "mandatory": false, + "multiple": false, + "non_localizable": false, + "unique": false, + "indexed": false, + "inbuilt_model": false + }, + { + "data_type": "global_field", + "display_name": "feature", + "reference_to": "feature", + "field_metadata": { + "description": "" + }, + "uid": "feature", + "mandatory": false, + "multiple": false, + "non_localizable": false, + "unique": false, + "indexed": false, + "inbuilt_model": false + } + ] + } +} \ No newline at end of file