Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -274,3 +274,4 @@ src/main/resources/
/.vscode/
/docs/
INTEGRATION-TESTS-GUIDE.md
src/main/java/com/demo/*
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# CHANGELOG

## v2.5.0

### Feb 12, 2026
- Enhancement: assetFields method added

## v2.4.0

### Feb 02, 2026
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.contentstack.sdk</groupId>
<artifactId>java</artifactId>
<version>2.4.0</version>
<version>2.5.0</version>
<packaging>jar</packaging>
<name>contentstack-java</name>
<description>Java SDK for Contentstack Content Delivery API</description>
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/com/contentstack/sdk/Asset.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/com/contentstack/sdk/AssetLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
*
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/contentstack/sdk/CSHttpConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ public String setFormParamsGET(HashMap<String, Object> 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<String, Object> e : params.entrySet()) {
if (urlParams == null) {
Expand All @@ -124,7 +126,7 @@ private String getParams(HashMap<String, Object> 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;
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/contentstack/sdk/Entry.java
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,19 @@ public Entry exceptWithReferenceUid(@NotNull List<String> 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
*
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/contentstack/sdk/Query.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)
*
Expand Down
127 changes: 127 additions & 0 deletions src/test/java/com/contentstack/sdk/TestAsset.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading
Loading