diff --git a/.gitignore b/.gitignore index 80376521..a043cab6 100644 --- a/.gitignore +++ b/.gitignore @@ -274,3 +274,4 @@ src/main/resources/ /.vscode/ /docs/ INTEGRATION-TESTS-GUIDE.md +src/main/java/com/demo/* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 001a9d56..d2fa8498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## v2.5.0 + +### Feb 12, 2026 +- Enhancement: assetFields method added + ## v2.4.0 ### Feb 02, 2026 diff --git a/pom.xml b/pom.xml index 41ea6079..d8aa684b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.contentstack.sdk java - 2.4.0 + 2.5.0 jar contentstack-java Java SDK for Contentstack Content Delivery API diff --git a/src/main/java/com/contentstack/sdk/Asset.java b/src/main/java/com/contentstack/sdk/Asset.java index e281f53c..9f498245 100644 --- a/src/main/java/com/contentstack/sdk/Asset.java +++ b/src/main/java/com/contentstack/sdk/Asset.java @@ -1,6 +1,7 @@ package com.contentstack.sdk; import org.jetbrains.annotations.NotNull; +import org.json.JSONArray; import org.json.JSONObject; import retrofit2.Retrofit; import lombok.Getter; @@ -544,6 +545,19 @@ public Asset includeMetadata() { return this; } + public Asset assetFields(String... fields) { + if (fields != null && fields.length > 0) { + JSONArray array = new JSONArray(); + for (String field : fields) { + array.put(field); + } + if (!array.isEmpty()) { + urlQueries.put("asset_fields[]", array); + } + } + return this; + } + /** * Fetch. * diff --git a/src/main/java/com/contentstack/sdk/AssetLibrary.java b/src/main/java/com/contentstack/sdk/AssetLibrary.java index e005955f..29fd0c4b 100644 --- a/src/main/java/com/contentstack/sdk/AssetLibrary.java +++ b/src/main/java/com/contentstack/sdk/AssetLibrary.java @@ -2,6 +2,7 @@ import org.jetbrains.annotations.NotNull; import org.json.JSONObject; +import org.json.JSONArray; import java.util.*; import java.util.logging.Logger; @@ -33,7 +34,8 @@ protected void setStackInstance(@NotNull Stack stack) { //Sanitization of keys private boolean isValidKey(String key) { - return key.matches("^[a-zA-Z0-9_.]+$"); + // Fixed regex: allow alphanumeric, underscore, dot, and square brackets at the end, escaped properly + return key.matches("^[a-zA-Z0-9_.]+(\\[\\])?$"); } //Sanitization of values @@ -265,6 +267,19 @@ public AssetLibrary limit (@NotNull int number) { return this; } + public AssetLibrary assetFields(String... fields) { + if (fields != null && fields.length > 0) { + JSONArray array = new JSONArray(); + for (String field : fields) { + array.put(field); + } + if (!array.isEmpty()) { + urlQueries.put("asset_fields[]", array); + } + } + return this; + } + /** * Fetch all. * diff --git a/src/main/java/com/contentstack/sdk/CSHttpConnection.java b/src/main/java/com/contentstack/sdk/CSHttpConnection.java index 9635dbf5..b60532db 100644 --- a/src/main/java/com/contentstack/sdk/CSHttpConnection.java +++ b/src/main/java/com/contentstack/sdk/CSHttpConnection.java @@ -102,7 +102,9 @@ public String setFormParamsGET(HashMap params) { if (params != null && params.size() > 0) { String urlParams = null; urlParams = info.equalsIgnoreCase(Constants.REQUEST_CONTROLLER.QUERY.name()) - || info.equalsIgnoreCase(Constants.REQUEST_CONTROLLER.ENTRY.name()) ? getParams(params) : null; + || info.equalsIgnoreCase(Constants.REQUEST_CONTROLLER.ENTRY.name()) + || info.equalsIgnoreCase(Constants.REQUEST_CONTROLLER.ASSET.name()) + || info.equalsIgnoreCase(Constants.REQUEST_CONTROLLER.ASSETLIBRARY.name()) ? getParams(params) : null; if (urlParams == null) { for (Map.Entry e : params.entrySet()) { if (urlParams == null) { @@ -124,7 +126,7 @@ private String getParams(HashMap params) { Object value = e.getValue(); try { if (key.equalsIgnoreCase("include[]") || key.equalsIgnoreCase("only[BASE][]") - || key.equalsIgnoreCase("except[BASE][]")) { + || key.equalsIgnoreCase("except[BASE][]") || key.equalsIgnoreCase("asset_fields[]")) { urlParams = convertUrlParam(urlParams, value, key); } else if (key.equalsIgnoreCase("only")) { JSONObject onlyJSON = (JSONObject) value; diff --git a/src/main/java/com/contentstack/sdk/Entry.java b/src/main/java/com/contentstack/sdk/Entry.java index ab24592e..14cb47eb 100644 --- a/src/main/java/com/contentstack/sdk/Entry.java +++ b/src/main/java/com/contentstack/sdk/Entry.java @@ -904,6 +904,19 @@ public Entry exceptWithReferenceUid(@NotNull List fieldUid, @NotNull Str return this; } + + public Entry assetFields(String... fields) { + if (fields != null && fields.length > 0) { + JSONArray array = new JSONArray(); + for (String field : fields) { + array.put(field); + } + if (!array.isEmpty()) { + params.put("asset_fields[]", array); + } + } + return this; + } /** * Fetches the latest version of the entries from Contentstack.com content stack * diff --git a/src/main/java/com/contentstack/sdk/Query.java b/src/main/java/com/contentstack/sdk/Query.java index fdc1f521..52b6851c 100644 --- a/src/main/java/com/contentstack/sdk/Query.java +++ b/src/main/java/com/contentstack/sdk/Query.java @@ -1146,6 +1146,19 @@ public Query search(@NotNull String value) { return this; } + public Query assetFields(String... fields) { + if (fields != null && fields.length > 0) { + JSONArray array = new JSONArray(); + for (String field : fields) { + array.put(field); + } + if (!array.isEmpty()) { + urlQueries.put("asset_fields[]", array); + } + } + return this; + } + /** * Execute a Query and Caches its result (Optional) * diff --git a/src/test/java/com/contentstack/sdk/TestAsset.java b/src/test/java/com/contentstack/sdk/TestAsset.java index ac460463..715c3777 100644 --- a/src/test/java/com/contentstack/sdk/TestAsset.java +++ b/src/test/java/com/contentstack/sdk/TestAsset.java @@ -182,6 +182,133 @@ void testIncludeMetadata() { assertEquals(true, asset.urlQueries.get("include_metadata")); } + // ========== ASSET FIELDS TESTS (CDA asset_fields[] parameter) ========== + + @Test + void testAssetFieldsWithSupportedValues() { + Asset result = asset.assetFields("user_defined_fields", "embedded", "ai_suggested", "visual_markups"); + assertSame(asset, result); + assertTrue(asset.urlQueries.has("asset_fields[]")); + Object val = asset.urlQueries.get("asset_fields[]"); + assertTrue(val instanceof JSONArray); + JSONArray arr = (JSONArray) val; + assertEquals(4, arr.length()); + assertEquals("user_defined_fields", arr.get(0)); + assertEquals("embedded", arr.get(1)); + assertEquals("ai_suggested", arr.get(2)); + assertEquals("visual_markups", arr.get(3)); + } + + @Test + void testAssetFieldsReturnsThis() { + Asset result = asset.assetFields("user_defined_fields"); + assertSame(asset, result); + } + + @Test + void testAssetFieldsWithNoArgsDoesNotSetParam() { + asset.assetFields(); + assertFalse(asset.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsWithNullDoesNotSetParam() { + asset.assetFields((String[]) null); + assertFalse(asset.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsChainingWithOtherMethods() { + Asset result = asset.assetFields("embedded", "visual_markups") + .includeMetadata() + .includeDimension(); + assertSame(asset, result); + assertTrue(asset.urlQueries.has("asset_fields[]")); + assertTrue(asset.urlQueries.has("include_metadata")); + assertTrue(asset.urlQueries.has("include_dimension")); + JSONArray arr = asset.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("embedded", arr.get(0)); + assertEquals("visual_markups", arr.get(1)); + } + + /** + * Usage: stack.asset(assetUid).assetFields(...).fetch() + * Verifies the full chain sets asset_fields[] on the asset before fetch. + */ + @Test + void testUsageSingleAssetFetchWithAssetFields() throws IllegalAccessException { + Stack stack = Contentstack.stack("api_key", "delivery_token", "env"); + Asset asset = stack.asset("asset_uid_123") + .assetFields("embedded", "visual_markups"); + assertTrue(asset.urlQueries.has("asset_fields[]")); + JSONArray arr = asset.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("embedded", arr.get(0)); + assertEquals("visual_markups", arr.get(1)); + } + + + @Test + void testAssetFieldsSingleField() { + asset.assetFields("embedded"); + assertTrue(asset.urlQueries.has("asset_fields[]")); + JSONArray arr = asset.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(1, arr.length()); + assertEquals("embedded", arr.get(0)); + } + + @Test + void testAssetFieldsEmptyVarargsArrayDoesNotSetParam() { + asset.assetFields(new String[0]); + assertFalse(asset.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsDuplicateValuesAllowed() { + asset.assetFields("embedded", "embedded"); + JSONArray arr = asset.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("embedded", arr.get(0)); + assertEquals("embedded", arr.get(1)); + } + + @Test + void testAssetFieldsSecondCallOverwrites() { + asset.assetFields("user_defined_fields", "embedded"); + asset.assetFields("ai_suggested"); + JSONArray arr = asset.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(1, arr.length()); + assertEquals("ai_suggested", arr.get(0)); + } + + @Test + void testAssetFieldsWithEmptyStringInArray() { + asset.assetFields("valid", "", "embedded"); + JSONArray arr = asset.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(3, arr.length()); + assertEquals("valid", arr.get(0)); + assertEquals("", arr.get(1)); + assertEquals("embedded", arr.get(2)); + } + + @Test + void testAssetFieldsWithNullInArray() { + asset.assetFields("valid", null, "embedded"); + JSONArray arr = asset.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(3, arr.length()); + assertEquals("valid", arr.get(0)); + assertEquals("embedded", arr.get(2)); + } + + @Test + void testAssetFieldsSingleEmptyStringSetsParam() { + asset.assetFields(""); + assertTrue(asset.urlQueries.has("asset_fields[]")); + assertEquals(1, asset.urlQueries.getJSONArray("asset_fields[]").length()); + assertEquals("", asset.urlQueries.getJSONArray("asset_fields[]").get(0)); + } + // ========== CHAINING TESTS ========== @Test diff --git a/src/test/java/com/contentstack/sdk/TestAssetLibrary.java b/src/test/java/com/contentstack/sdk/TestAssetLibrary.java index c9e5348e..9092c4f1 100644 --- a/src/test/java/com/contentstack/sdk/TestAssetLibrary.java +++ b/src/test/java/com/contentstack/sdk/TestAssetLibrary.java @@ -1,5 +1,6 @@ package com.contentstack.sdk; +import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -135,6 +136,107 @@ void testIncludeMetadata() { assertEquals(true, assetLibrary.urlQueries.get("include_metadata")); } + // ========== ASSET FIELDS TESTS (CDA asset_fields[] parameter) ========== + + @Test + void testAssetFieldsWithSupportedValues() { + AssetLibrary result = assetLibrary.assetFields("user_defined_fields", "embedded", "ai_suggested", "visual_markups"); + assertSame(assetLibrary, result); + assertTrue(assetLibrary.urlQueries.has("asset_fields[]")); + Object val = assetLibrary.urlQueries.get("asset_fields[]"); + assertTrue(val instanceof JSONArray); + JSONArray arr = (JSONArray) val; + assertEquals(4, arr.length()); + assertEquals("user_defined_fields", arr.get(0)); + assertEquals("embedded", arr.get(1)); + assertEquals("ai_suggested", arr.get(2)); + assertEquals("visual_markups", arr.get(3)); + } + + @Test + void testAssetFieldsReturnsThis() { + AssetLibrary result = assetLibrary.assetFields("embedded"); + assertSame(assetLibrary, result); + } + + @Test + void testAssetFieldsWithNoArgsDoesNotSetParam() { + assetLibrary.assetFields(); + assertFalse(assetLibrary.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsWithNullDoesNotSetParam() { + assetLibrary.assetFields((String[]) null); + assertFalse(assetLibrary.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsChainingWithIncludeMetadata() { + AssetLibrary result = assetLibrary.assetFields("user_defined_fields").includeMetadata().includeCount(); + assertSame(assetLibrary, result); + assertTrue(assetLibrary.urlQueries.has("asset_fields[]")); + assertTrue(assetLibrary.urlQueries.has("include_metadata")); + assertTrue(assetLibrary.urlQueries.has("include_count")); + } + + /** + * Usage: stack.assetLibrary().assetFields(...).fetchAll() + * (AssetQuery / query assets - in this SDK use assetLibrary(), not asset().find()) + * Verifies the full chain sets asset_fields[] on the asset library before fetchAll. + */ + @Test + void testUsageAssetLibraryFetchAllWithAssetFields() throws IllegalAccessException { + Stack stack = Contentstack.stack("api_key", "delivery_token", "env"); + AssetLibrary lib = stack.assetLibrary() + .assetFields("user_defined_fields", "embedded"); + assertTrue(lib.urlQueries.has("asset_fields[]")); + JSONArray arr = lib.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("user_defined_fields", arr.get(0)); + assertEquals("embedded", arr.get(1)); + } + + @Test + void testAssetFieldsSingleField() { + assetLibrary.assetFields("visual_markups"); + JSONArray arr = assetLibrary.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(1, arr.length()); + assertEquals("visual_markups", arr.get(0)); + } + + @Test + void testAssetFieldsEmptyVarargsArrayDoesNotSetParam() { + assetLibrary.assetFields(new String[0]); + assertFalse(assetLibrary.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsSecondCallOverwrites() { + assetLibrary.assetFields("user_defined_fields", "embedded"); + assetLibrary.assetFields("ai_suggested"); + JSONArray arr = assetLibrary.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(1, arr.length()); + assertEquals("ai_suggested", arr.get(0)); + } + + @Test + void testAssetFieldsDuplicateValuesAllowed() { + assetLibrary.assetFields("embedded", "embedded"); + JSONArray arr = assetLibrary.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("embedded", arr.get(0)); + assertEquals("embedded", arr.get(1)); + } + + @Test + void testAssetFieldsWithEmptyStringInArray() { + assetLibrary.assetFields("valid", "", "visual_markups"); + JSONArray arr = assetLibrary.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(3, arr.length()); + assertEquals("", arr.get(1)); + } + @Test void testMultipleIncludes() { assetLibrary.includeCount() diff --git a/src/test/java/com/contentstack/sdk/TestCSHttpConnection.java b/src/test/java/com/contentstack/sdk/TestCSHttpConnection.java index 754e96f6..bf2a097e 100644 --- a/src/test/java/com/contentstack/sdk/TestCSHttpConnection.java +++ b/src/test/java/com/contentstack/sdk/TestCSHttpConnection.java @@ -399,6 +399,33 @@ void testGetParamsWithMultipleTypes() throws Exception { assertTrue(result.contains("environment=staging")); } + @Test + void testGetParamsWithAssetFieldsArray() throws Exception { + connection.setInfo("ENTRY"); + + HashMap params = new HashMap<>(); + params.put("environment", "production"); + + JSONArray assetFieldsArray = new JSONArray(); + assetFieldsArray.put("user_defined_fields"); + assetFieldsArray.put("embedded"); + assetFieldsArray.put("ai_suggested"); + assetFieldsArray.put("visual_markups"); + params.put("asset_fields[]", assetFieldsArray); + + Method getParamsMethod = CSHttpConnection.class.getDeclaredMethod("getParams", HashMap.class); + getParamsMethod.setAccessible(true); + + String result = (String) getParamsMethod.invoke(connection, params); + + assertNotNull(result); + assertTrue(result.contains("environment=production")); + assertTrue(result.contains("user_defined_fields")); + assertTrue(result.contains("embedded")); + assertTrue(result.contains("ai_suggested")); + assertTrue(result.contains("visual_markups")); + } + // ========== CONVERT URL PARAM TESTS ========== @Test diff --git a/src/test/java/com/contentstack/sdk/TestEntry.java b/src/test/java/com/contentstack/sdk/TestEntry.java index 4cf7ac2d..827cfabc 100644 --- a/src/test/java/com/contentstack/sdk/TestEntry.java +++ b/src/test/java/com/contentstack/sdk/TestEntry.java @@ -1,5 +1,6 @@ package com.contentstack.sdk; +import org.json.JSONArray; import org.json.JSONObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -251,6 +252,106 @@ void testIncludeMetadata() { assertEquals(true, entry.params.get("include_metadata")); } + // ========== ASSET FIELDS TESTS (CDA asset_fields[] parameter) ========== + + @Test + void testAssetFieldsWithSupportedValues() { + Entry result = entry.assetFields("user_defined_fields", "embedded", "ai_suggested", "visual_markups"); + assertSame(entry, result); + assertTrue(entry.params.has("asset_fields[]")); + Object val = entry.params.get("asset_fields[]"); + assertTrue(val instanceof JSONArray); + JSONArray arr = (JSONArray) val; + assertEquals(4, arr.length()); + assertEquals("user_defined_fields", arr.get(0)); + assertEquals("embedded", arr.get(1)); + assertEquals("ai_suggested", arr.get(2)); + assertEquals("visual_markups", arr.get(3)); + } + + @Test + void testAssetFieldsReturnsThis() { + Entry result = entry.assetFields("embedded"); + assertSame(entry, result); + } + + @Test + void testAssetFieldsWithNoArgsDoesNotSetParam() { + entry.assetFields(); + assertFalse(entry.params.has("asset_fields[]")); + } + + @Test + void testAssetFieldsWithNullDoesNotSetParam() { + entry.assetFields((String[]) null); + assertFalse(entry.params.has("asset_fields[]")); + } + + @Test + void testAssetFieldsChainingWithIncludeMetadata() { + Entry result = entry.assetFields("user_defined_fields", "visual_markups").includeMetadata().setLocale("en-us"); + assertSame(entry, result); + assertTrue(entry.params.has("asset_fields[]")); + assertTrue(entry.params.has("include_metadata")); + assertTrue(entry.params.has("locale")); + } + + /** + * Usage: stack.contentType(ctUid).entry(entryUid).assetFields(...).fetch() + * Verifies the full chain sets asset_fields[] on the entry before fetch. + */ + @Test + void testUsageSingleEntryFetchWithAssetFields() throws IllegalAccessException { + Stack stack = Contentstack.stack("api_key", "delivery_token", "env"); + Entry entry = stack.contentType("blog").entry("entry_123") + .assetFields("user_defined_fields", "embedded"); + assertTrue(entry.params.has("asset_fields[]")); + JSONArray arr = entry.params.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("user_defined_fields", arr.get(0)); + assertEquals("embedded", arr.get(1)); + } + + @Test + void testAssetFieldsSingleField() { + entry.assetFields("embedded"); + JSONArray arr = entry.params.getJSONArray("asset_fields[]"); + assertEquals(1, arr.length()); + assertEquals("embedded", arr.get(0)); + } + + @Test + void testAssetFieldsEmptyVarargsArrayDoesNotSetParam() { + entry.assetFields(new String[0]); + assertFalse(entry.params.has("asset_fields[]")); + } + + @Test + void testAssetFieldsSecondCallOverwrites() { + entry.assetFields("user_defined_fields", "embedded"); + entry.assetFields("ai_suggested"); + JSONArray arr = entry.params.getJSONArray("asset_fields[]"); + assertEquals(1, arr.length()); + assertEquals("ai_suggested", arr.get(0)); + } + + @Test + void testAssetFieldsDuplicateValuesAllowed() { + entry.assetFields("embedded", "embedded"); + JSONArray arr = entry.params.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("embedded", arr.get(0)); + assertEquals("embedded", arr.get(1)); + } + + @Test + void testAssetFieldsWithEmptyStringInArray() { + entry.assetFields("valid", "", "visual_markups"); + JSONArray arr = entry.params.getJSONArray("asset_fields[]"); + assertEquals(3, arr.length()); + assertEquals("", arr.get(1)); + } + // ========== ONLY/EXCEPT FIELD TESTS ========== @Test diff --git a/src/test/java/com/contentstack/sdk/TestQuery.java b/src/test/java/com/contentstack/sdk/TestQuery.java index 065ef3d1..667fdf1b 100644 --- a/src/test/java/com/contentstack/sdk/TestQuery.java +++ b/src/test/java/com/contentstack/sdk/TestQuery.java @@ -550,6 +550,109 @@ void testIncludeMetadata() { assertNotNull(query.urlQueries); } + // ========== ASSET FIELDS TESTS (CDA asset_fields[] parameter) ========== + + @Test + void testAssetFieldsWithSupportedValues() { + Query result = query.assetFields("user_defined_fields", "embedded", "ai_suggested", "visual_markups"); + assertSame(query, result); + assertTrue(query.urlQueries.has("asset_fields[]")); + Object val = query.urlQueries.get("asset_fields[]"); + assertTrue(val instanceof JSONArray); + JSONArray arr = (JSONArray) val; + assertEquals(4, arr.length()); + assertEquals("user_defined_fields", arr.get(0)); + assertEquals("embedded", arr.get(1)); + assertEquals("ai_suggested", arr.get(2)); + assertEquals("visual_markups", arr.get(3)); + } + + @Test + void testAssetFieldsReturnsThis() { + Query result = query.assetFields("embedded"); + assertSame(query, result); + } + + @Test + void testAssetFieldsWithNoArgsDoesNotSetParam() { + query.assetFields(); + assertFalse(query.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsWithNullDoesNotSetParam() { + query.assetFields((String[]) null); + assertFalse(query.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsChainingWithIncludeMetadata() { + Query result = query.assetFields("ai_suggested", "visual_markups").includeMetadata().includeCount(); + assertSame(query, result); + assertTrue(query.urlQueries.has("asset_fields[]")); + assertTrue(query.urlQueries.has("include_count")); + JSONArray arr = query.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("ai_suggested", arr.get(0)); + assertEquals("visual_markups", arr.get(1)); + } + + /** + * Usage: stack.contentType(ctUid).query().assetFields(...).find() + * Verifies the full chain sets asset_fields[] on the query before find. + */ + @Test + void testUsageQueryEntriesFindWithAssetFields() throws IllegalAccessException { + Stack stack = Contentstack.stack("api_key", "delivery_token", "env"); + Query query = stack.contentType("blog").query() + .assetFields("ai_suggested", "visual_markups"); + assertTrue(query.urlQueries.has("asset_fields[]")); + JSONArray arr = query.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("ai_suggested", arr.get(0)); + assertEquals("visual_markups", arr.get(1)); + } + + @Test + void testAssetFieldsSingleField() { + query.assetFields("user_defined_fields"); + JSONArray arr = query.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(1, arr.length()); + assertEquals("user_defined_fields", arr.get(0)); + } + + @Test + void testAssetFieldsEmptyVarargsArrayDoesNotSetParam() { + query.assetFields(new String[0]); + assertFalse(query.urlQueries.has("asset_fields[]")); + } + + @Test + void testAssetFieldsSecondCallOverwrites() { + query.assetFields("user_defined_fields", "embedded"); + query.assetFields("visual_markups"); + JSONArray arr = query.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(1, arr.length()); + assertEquals("visual_markups", arr.get(0)); + } + + @Test + void testAssetFieldsDuplicateValuesAllowed() { + query.assetFields("ai_suggested", "ai_suggested"); + JSONArray arr = query.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(2, arr.length()); + assertEquals("ai_suggested", arr.get(0)); + assertEquals("ai_suggested", arr.get(1)); + } + + @Test + void testAssetFieldsWithEmptyStringInArray() { + query.assetFields("valid", "", "embedded"); + JSONArray arr = query.urlQueries.getJSONArray("asset_fields[]"); + assertEquals(3, arr.length()); + assertEquals("", arr.get(1)); + } + @Test void testMultipleIncludeMethods() { query.includeFallback()