diff --git a/.gitignore b/.gitignore
index 65e37ad..e21cc92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -258,6 +258,8 @@ gradle-app.setting
### Gradle Patch ###
**/build/
+sample/
+
# End of https://www.toptal.com/developers/gitignore/api/macos,code-java,java-web,maven,gradle,intellij,visualstudiocode,eclipse
.idea/compiler.xml
.idea/encodings.xml
diff --git a/Changelog.md b/Changelog.md
index 550e08f..b9c0f02 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,6 +1,12 @@
# Changelog
A brief description of what changes project contains
+## Mar 23, 2026
+
+#### v1.3.0
+
+- Enhancement: Variant Utility
+
## Mar 17, 2025
#### v1.2.15
diff --git a/LICENSE b/LICENSE
index d78b6bc..c2a603d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2012 - 2025 Contentstack
+Copyright (c) 2012 - 2026 Contentstack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/pom.xml b/pom.xml
index ab83569..c6dd1fd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.contentstack.sdk
utils
- 1.2.15
+ 1.3.0
jar
Contentstack-utils
Java Utils SDK for Contentstack Content Delivery API, Contentstack is a headless CMS
@@ -115,6 +115,12 @@
json-simple
${json.simple.version}
provided
+
+
+ junit
+ junit
+
+
org.springframework
@@ -134,6 +140,14 @@
prepare-agent
+
+
+
+ sun/**
+ com/sun/**
+ jdk/**
+
+
report
diff --git a/src/main/java/com/contentstack/utils/Utils.java b/src/main/java/com/contentstack/utils/Utils.java
index c11bf05..1acb7ee 100644
--- a/src/main/java/com/contentstack/utils/Utils.java
+++ b/src/main/java/com/contentstack/utils/Utils.java
@@ -23,15 +23,19 @@
public class Utils {
-
/**
- * The `render` function takes a JSON object, an array of path strings, and an option object, and
- * renders the contents of the JSON object based on the provided paths and options.
+ * The `render` function takes a JSON object, an array of path strings, and an
+ * option object, and
+ * renders the contents of the JSON object based on the provided paths and
+ * options.
*
- * @param entryObj The entryObj parameter is a JSONObject that represents an entry or a data
+ * @param entryObj The entryObj parameter is a JSONObject that represents an
+ * entry or a data
* object. It contains various properties and values.
- * @param pathString An array of strings representing the paths to the content in the JSON object.
- * @param renderObject The `renderObject` parameter is an object of type `Option`. It is used to
+ * @param pathString An array of strings representing the paths to the content
+ * in the JSON object.
+ * @param renderObject The `renderObject` parameter is an object of type
+ * `Option`. It is used to
* specify rendering options for the content.
*/
public static void render(JSONObject entryObj, String[] pathString, Option renderObject) {
@@ -54,7 +58,8 @@ public static void render(JSONObject entryObj, String[] pathString, Option rende
findContent(entryObj, path, callback);
}
} else {
- // if pathString is not given, extract all available pathString from _embedded_items
+ // if pathString is not given, extract all available pathString from
+ // _embedded_items
JSONObject embedKeys = entryObj.getJSONObject("_embedded_items");
ArrayList pathKeys = new ArrayList<>(embedKeys.keySet());
for (String path : pathKeys) {
@@ -64,23 +69,31 @@ public static void render(JSONObject entryObj, String[] pathString, Option rende
}
}
-
/**
- * The function takes a string, a JSON object, and an option, and replaces certain elements in the
+ * The function takes a string, a JSON object, and an option, and replaces
+ * certain elements in the
* string with values from the JSON object based on the option.
*
- * @param rteStringify The `rteStringify` parameter is a string representation of the content to be
- * rendered. It is passed to the `Jsoup.parse()` method to create a `Document` object.
- * @param embedObject The `embedObject` parameter is a JSONObject that contains embedded items. It
- * may have a key "_embedded_items" which holds a JSONObject of embedded items.
- * @param option The "option" parameter is of type "Option". It is an object that represents a
- * specific option for rendering the content. The exact structure and properties of the "Option"
- * object are not provided in the code snippet, so it would be necessary to refer to the
+ * @param rteStringify The `rteStringify` parameter is a string representation
+ * of the content to be
+ * rendered. It is passed to the `Jsoup.parse()` method to
+ * create a `Document` object.
+ * @param embedObject The `embedObject` parameter is a JSONObject that contains
+ * embedded items. It
+ * may have a key "_embedded_items" which holds a JSONObject
+ * of embedded items.
+ * @param option The "option" parameter is of type "Option". It is an
+ * object that represents a
+ * specific option for rendering the content. The exact
+ * structure and properties of the "Option"
+ * object are not provided in the code snippet, so it would
+ * be necessary to refer to the
* documentation or other parts of the code
- * @return The method is returning the modified RTE (Rich Text Editor) content as a string.
+ * @return The method is returning the modified RTE (Rich Text Editor) content
+ * as a string.
*/
public static String renderContent(String rteStringify, JSONObject embedObject, Option option) {
- final String[] sReplaceRTE = {rteStringify};
+ final String[] sReplaceRTE = { rteStringify };
Document html = Jsoup.parse(rteStringify);
getEmbeddedObjects(html, metadata -> {
Optional filteredContent = Optional.empty();
@@ -99,14 +112,18 @@ public static String renderContent(String rteStringify, JSONObject embedObject,
}
/**
- * The function takes an array of strings, an object, and an option, and returns a new array with the
+ * The function takes an array of strings, an object, and an option, and returns
+ * a new array with the
* rendered contents of each string.
*
* @param rteArray An array of RTE (Rich Text Editor) content strings.
- * @param entryObject The `entryObject` parameter is a JSONObject that contains the data needed for
- * rendering the content. It likely contains key-value pairs representing different properties or
+ * @param entryObject The `entryObject` parameter is a JSONObject that contains
+ * the data needed for
+ * rendering the content. It likely contains key-value pairs
+ * representing different properties or
* attributes of the content.
- * @param option The "option" parameter is an object of type "Option". It is used as an argument in the
+ * @param option The "option" parameter is an object of type "Option". It
+ * is used as an argument in the
* "renderContent" method.
* @return The method is returning a JSONArray object.
*/
@@ -124,7 +141,9 @@ private static Optional findEmbeddedItems(JSONObject jsonObject, Met
Set allKeys = jsonObject.keySet();
for (String key : allKeys) {
JSONArray jsonArray = jsonObject.optJSONArray(key);
- Optional filteredContent = StreamSupport.stream(jsonArray.spliterator(), false).map(val -> (JSONObject) val).filter(val -> val.optString("uid").equalsIgnoreCase(metadata.getItemUid())).findFirst();
+ Optional filteredContent = StreamSupport.stream(jsonArray.spliterator(), false)
+ .map(val -> (JSONObject) val)
+ .filter(val -> val.optString("uid").equalsIgnoreCase(metadata.getItemUid())).findFirst();
if (filteredContent.isPresent()) {
return filteredContent;
}
@@ -133,33 +152,47 @@ private static Optional findEmbeddedItems(JSONObject jsonObject, Met
}
/**
- * The function converts a JSONArray to HTML using a specified key path and options.
+ * The function converts a JSONArray to HTML using a specified key path and
+ * options.
*
* @param entryArray A JSONArray containing JSON objects.
- * @param keyPath The keyPath parameter is an array of strings that represents the path to a specific
- * key in a JSON object. Each string in the array represents a key in the path. For example, if the
- * keyPath is ["person", "name"], it means that we want to access the value of the "
- * @param option The "option" parameter is an object of type "Option". It is used to specify additional
- * options or settings for the JSON to HTML conversion process.
+ * @param keyPath The keyPath parameter is an array of strings that
+ * represents the path to a specific
+ * key in a JSON object. Each string in the array represents a
+ * key in the path. For example, if the
+ * keyPath is ["person", "name"], it means that we want to
+ * access the value of the "
+ * @param option The "option" parameter is an object of type "Option". It is
+ * used to specify additional
+ * options or settings for the JSON to HTML conversion
+ * process.
*/
public static void jsonToHTML(@NotNull JSONArray entryArray, @NotNull String[] keyPath, @NotNull Option option) {
entryArray.forEach(jsonObj -> jsonToHTML((JSONObject) jsonObj, keyPath, option));
}
-
/**
- * The function `jsonToHTML` converts a JSON object to HTML using a specified key path and
+ * The function `jsonToHTML` converts a JSON object to HTML using a specified
+ * key path and
* rendering options.
*
- * @param entry The `entry` parameter is a `JSONObject` that represents the JSON data that you want
- * to convert to HTML. It contains the data that you want to render as HTML.
- * @param keyPath The keyPath parameter is an array of strings that represents the path to the
- * desired content in the JSON object. Each string in the array represents a key in the JSON object
- * hierarchy. The method will traverse the JSON object using the keys in the keyPath array to find
+ * @param entry The `entry` parameter is a `JSONObject` that represents
+ * the JSON data that you want
+ * to convert to HTML. It contains the data that you want to
+ * render as HTML.
+ * @param keyPath The keyPath parameter is an array of strings that
+ * represents the path to the
+ * desired content in the JSON object. Each string in the
+ * array represents a key in the JSON object
+ * hierarchy. The method will traverse the JSON object using
+ * the keys in the keyPath array to find
* the desired content.
- * @param renderOption The renderOption parameter is an option that determines how the content
- * should be rendered. It is of type Option, which is likely an enum or a class with different
- * rendering options. The specific options available and their meanings would depend on the
+ * @param renderOption The renderOption parameter is an option that determines
+ * how the content
+ * should be rendered. It is of type Option, which is likely
+ * an enum or a class with different
+ * rendering options. The specific options available and
+ * their meanings would depend on the
* implementation of the Option class.
*/
public static void jsonToHTML(@NotNull JSONObject entry, @NotNull String[] keyPath, Option renderOption) {
@@ -173,7 +206,6 @@ public static void jsonToHTML(@NotNull JSONObject entry, @NotNull String[] keyPa
return Optional.empty();
};
-
ContentCallback callback = content -> {
if (content instanceof JSONArray) {
JSONArray contentArray = (JSONArray) content;
@@ -191,16 +223,21 @@ public static void jsonToHTML(@NotNull JSONObject entry, @NotNull String[] keyPa
}
}
-
/**
- * The function converts a JSON object to HTML using a specified rendering option and optional embedded
+ * The function converts a JSON object to HTML using a specified rendering
+ * option and optional embedded
* items.
*
- * @param jsonRTE A JSONObject representing the JSON data to be converted to HTML.
- * @param renderOption The `renderOption` parameter is an option that determines how the JSON content
- * should be rendered as HTML. It could be an enum or a class that defines different rendering options.
- * @param embeddeditems The `embedded-items` parameter is a `JSONObject` that contains embedded items.
- * It is used to find and retrieve embedded items based on their metadata.
+ * @param jsonRTE A JSONObject representing the JSON data to be converted
+ * to HTML.
+ * @param renderOption The `renderOption` parameter is an option that
+ * determines how the JSON content
+ * should be rendered as HTML. It could be an enum or a
+ * class that defines different rendering options.
+ * @param embeddeditems The `embedded-items` parameter is a `JSONObject` that
+ * contains embedded items.
+ * It is used to find and retrieve embedded items based on
+ * their metadata.
* @return The method is returning a String.
*/
public static String jsonToHTML(@NotNull JSONObject jsonRTE, Option renderOption, JSONObject embeddeditems) {
@@ -213,17 +250,22 @@ public static String jsonToHTML(@NotNull JSONObject jsonRTE, Option renderOption
return enumerateContent(jsonRTE, renderOption, converter);
}
-
/**
- * The function `jsonToHTML` converts a JSON array to HTML using a specified rendering option and
+ * The function `jsonToHTML` converts a JSON array to HTML using a specified
+ * rendering option and
* optional embedded items.
*
- * @param jsonRTE A JSONArray object containing the JSON data to be converted to HTML.
- * @param renderOption The `renderOption` parameter is an option that determines how the JSON data
- * should be rendered as HTML. It could be an enum or a custom class that defines different rendering
+ * @param jsonRTE A JSONArray object containing the JSON data to be
+ * converted to HTML.
+ * @param renderOption The `renderOption` parameter is an option that
+ * determines how the JSON data
+ * should be rendered as HTML. It could be an enum or a
+ * custom class that defines different rendering
* options.
- * @param embeddeditems The `embedded-items` parameter is a `JSONObject` that contains embedded items.
- * It is used to find and retrieve embedded items based on the metadata provided.
+ * @param embeddeditems The `embedded-items` parameter is a `JSONObject` that
+ * contains embedded items.
+ * It is used to find and retrieve embedded items based on
+ * the metadata provided.
* @return The method is returning an Object.
*/
public static Object jsonToHTML(@NotNull JSONArray jsonRTE, Option renderOption, JSONObject embeddeditems) {
@@ -236,22 +278,118 @@ public static Object jsonToHTML(@NotNull JSONArray jsonRTE, Option renderOption,
return enumerateContents(jsonRTE, renderOption, converter);
}
-
/**
- * The function takes a JSONArray, a keyPath array, and an Option object, and iterates over each
+ * The function takes a JSONArray, a keyPath array, and an Option object, and
+ * iterates over each
* JSONObject in the JSONArray to call another render function.
*
- * @param jsonArray A JSONArray object that contains a collection of JSON objects.
- * @param keyPath The `keyPath` parameter is an array of strings that represents the path to a specific
- * key in a JSON object. Each string in the array represents a key in the path. For example, if the key
+ * @param jsonArray A JSONArray object that contains a collection of JSON
+ * objects.
+ * @param keyPath The `keyPath` parameter is an array of strings that
+ * represents the path to a specific
+ * key in a JSON object. Each string in the array represents
+ * a key in the path. For example, if the key
* path is `["foo", "bar", "baz"]`, it means that you want
- * @param renderObject The `renderObject` parameter is an object of type `Option`.
+ * @param renderObject The `renderObject` parameter is an object of type
+ * `Option`.
*/
public void render(@NotNull JSONArray jsonArray, @NotNull String[] keyPath, @NotNull Option renderObject) {
jsonArray.forEach(jsonObj -> render((JSONObject) jsonObj, keyPath, renderObject));
}
- //update assetURL in json of GQL response
+ public static JSONObject getVariantAliases(JSONObject entry, String contentTypeUid) {
+ if (contentTypeUid == null || contentTypeUid.isEmpty()) {
+ throw new IllegalArgumentException("ContentType is required.");
+ }
+ if (entry == null) {
+ throw new IllegalArgumentException("Entry must not be null.");
+ }
+
+ if (!entry.has("uid") || entry.isNull("uid")) {
+ throw new IllegalArgumentException("Entry must contain uid.");
+ }
+
+ String entryUid = entry.optString("uid", "");
+ JSONArray variantsArray = extractVariantAliasesFromEntry(entry);
+ JSONObject result = new JSONObject();
+ result.put("entry_uid", entryUid);
+ result.put("contenttype_uid", contentTypeUid);
+ result.put("variants", variantsArray);
+ return result;
+
+ }
+
+ public static JSONArray getVariantAliases(JSONArray entries, String contentTypeUid) {
+ if (contentTypeUid == null || contentTypeUid.isEmpty()) {
+ throw new IllegalArgumentException("ContentType is required.");
+ }
+ if (entries == null) {
+ return new JSONArray();
+ }
+ JSONArray variantResults = new JSONArray();
+ for (int i = 0; i < entries.length(); i++) {
+ JSONObject entry = entries.optJSONObject(i);
+ if (entry != null && entry.has("uid")) {
+ JSONObject singleResult = getVariantAliases(entry, contentTypeUid);
+ variantResults.put(singleResult);
+ }
+ }
+ return variantResults;
+ }
+
+ public static JSONObject getDataCsvariantsAttribute(JSONObject entry, String contentTypeUid) {
+
+ if (entry == null) {
+ JSONObject result = new JSONObject();
+ result.put("data-csvariants", "[]");
+ return result;
+ }
+ JSONArray entries = new JSONArray();
+ entries.put(entry);
+ return getDataCsvariantsAttribute(entries, contentTypeUid);
+ }
+
+ public static JSONObject getDataCsvariantsAttribute(JSONArray entries, String contentTypeUid) {
+
+ JSONObject result = new JSONObject();
+ if (entries == null) {
+ result.put("data-csvariants", "[]");
+ return result;
+ }
+ if (contentTypeUid == null || contentTypeUid.isEmpty()) {
+ throw new IllegalArgumentException("ContentType is required.");
+ }
+
+ JSONArray variantResults = getVariantAliases(entries, contentTypeUid);
+ String resultString = variantResults.toString();
+ result.put("data-csvariants", resultString);
+ return result;
+ }
+
+ private static JSONArray extractVariantAliasesFromEntry(JSONObject entry) {
+ JSONArray variantArray = new JSONArray();
+ JSONObject publishDetails = entry.optJSONObject("publish_details");
+ if (publishDetails == null) {
+ return new JSONArray();
+ }
+ JSONObject variants = publishDetails.optJSONObject("variants");
+ if (variants == null) {
+ return new JSONArray();
+ }
+
+ for (String key : variants.keySet()) {
+ Object value = variants.get(key);
+ if (value instanceof JSONObject) {
+ String alias = ((JSONObject) value).optString("alias", "");
+ if (alias != null && !alias.isEmpty()) {
+ variantArray.put(alias.trim());
+ }
+ }
+ }
+ return variantArray;
+ }
+
+ // update assetURL in json of GQL response
public static void UpdateAssetURLForGQL(JSONObject entryJson) {
Map assetUrls = new HashMap<>();
if (entryJson.has("data")) {
@@ -278,7 +416,8 @@ public static void UpdateAssetURLForGQL(JSONObject entryJson) {
String url = nodeList.getString("url");
if (nodeList.has("system")) {
JSONObject systemList = nodeList.getJSONObject("system");
- if ("sys_assets".equals(systemList.optString("content_type_uid")) && systemList.has("uid")) {
+ if ("sys_assets".equals(systemList.optString("content_type_uid"))
+ && systemList.has("uid")) {
String uid = systemList.getString("uid");
assetUrls.put(uid, url);
updateChildObjects(entryJson, assetUrls);
@@ -288,10 +427,9 @@ public static void UpdateAssetURLForGQL(JSONObject entryJson) {
}
}
}
- }
- else
- {
- throw new IllegalArgumentException("_embedded_items not present in entry. Call includeEmbeddedItems() before fetching entry.");
+ } else {
+ throw new IllegalArgumentException(
+ "_embedded_items not present in entry. Call includeEmbeddedItems() before fetching entry.");
}
}
}
@@ -342,5 +480,3 @@ private static void updateChildrenArray(JSONArray childrenArray, Map", assetLink.opt("assetlink").toString());
}
}
diff --git a/src/test/java/com/contentstack/utils/ReadResource.java b/src/test/java/com/contentstack/utils/ReadResource.java
index 607a294..c2cce7c 100644
--- a/src/test/java/com/contentstack/utils/ReadResource.java
+++ b/src/test/java/com/contentstack/utils/ReadResource.java
@@ -16,8 +16,6 @@ public class ReadResource {
public JSONObject readJson(String filename) throws IOException {
File file = new File(filename);
- String absolutePath = file.getAbsolutePath();
- System.out.println(absolutePath);
BufferedReader reader = new BufferedReader(new FileReader(file));
StringBuilder stringBuilder = new StringBuilder();
char[] buffer = new char[10];
diff --git a/src/test/java/com/contentstack/utils/TestRte.java b/src/test/java/com/contentstack/utils/TestRte.java
index cea24dd..a3bb259 100644
--- a/src/test/java/com/contentstack/utils/TestRte.java
+++ b/src/test/java/com/contentstack/utils/TestRte.java
@@ -120,7 +120,6 @@ public void testAffectedEntry() throws IOException {
final String rte = "src/test/resources/reports/wfs.json";
JSONObject theRTE = new ReadResource().readJson(rte);
String result = Utils.jsonToHTML(theRTE, new DefaultOption(), null);
- System.out.println(result);
Assert.assertEquals(kWFSAffectedHtml, result);
}
@@ -130,7 +129,6 @@ public void testOne() throws IOException {
final String rte = "src/test/resources/reports/one.json";
JSONObject theRTE = new ReadResource().readJson(rte);
String result = Utils.jsonToHTML(theRTE, new DefaultOption(), null);
- System.out.println(result);
Assert.assertEquals(kONEHtml, result);
}
@@ -139,7 +137,6 @@ public void testOCT7Issue() throws IOException {
final String rte = "src/test/resources/reports/oct_7.json";
JSONObject theRTE = new ReadResource().readJson(rte);
String result = Utils.jsonToHTML(theRTE, new DefaultOption(), null);
- System.out.println(result);
// Assert.assertEquals(kONEHtml, result);
}
@@ -148,7 +145,6 @@ public void testIssueOct() throws IOException {
final String rte = "src/test/resources/reports/issue_oct.json";
JSONObject theRTE = new ReadResource().readJson(rte);
String result = Utils.jsonToHTML(theRTE, new DefaultOption(), null);
- System.out.println(result);
// Assert.assertEquals(kONEHtml, result);
}
diff --git a/src/test/java/com/contentstack/utils/UtilTests.java b/src/test/java/com/contentstack/utils/UtilTests.java
index fa4359b..445b2e1 100644
--- a/src/test/java/com/contentstack/utils/UtilTests.java
+++ b/src/test/java/com/contentstack/utils/UtilTests.java
@@ -1,13 +1,18 @@
package com.contentstack.utils;
import com.contentstack.utils.render.DefaultOption;
+import org.json.JSONArray;
import org.json.JSONObject;
+import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -173,6 +178,129 @@ public void testUpdateAssetUrl() throws IOException{
// System.out.println(localJsonObj1);
}
+ private static Set jsonArrayToStringSet(JSONArray arr) {
+ Set set = new HashSet<>();
+ for (int i = 0; i < arr.length(); i++) {
+ set.add(arr.getString(i));
+ }
+ return set;
+ }
+
+ @Test
+ public void testGetVariantAliasesSingleEntry() throws IOException {
+ final String json = "src/test/resources/variant_entry_single.json";
+ JSONObject full = new ReadResource().readJson(json);
+ JSONObject entry = full.getJSONObject("entry");
+ String contentTypeUid = "movie";
+
+ JSONObject result = Utils.getVariantAliases(entry, contentTypeUid);
+
+ Assert.assertTrue(result.has("entry_uid") && !result.getString("entry_uid").isEmpty());
+ Assert.assertEquals(contentTypeUid, result.getString("contenttype_uid"));
+ JSONArray variants = result.getJSONArray("variants");
+ Assert.assertNotNull(variants);
+ Set aliasSet = jsonArrayToStringSet(variants);
+ Assert.assertEquals(
+ new HashSet<>(Arrays.asList("cs_personalize_0_0", "cs_personalize_0_3")),
+ aliasSet);
+ }
+
+ @Test
+ public void testGetDataCsvariantsAttributeSingleEntry() throws IOException {
+ final String json = "src/test/resources/variant_entry_single.json";
+ JSONObject full = new ReadResource().readJson(json);
+ JSONObject entry = full.getJSONObject("entry");
+ String contentTypeUid = "movie";
+
+ JSONObject result = Utils.getDataCsvariantsAttribute(entry, contentTypeUid);
+
+ Assert.assertTrue(result.has("data-csvariants"));
+ String dataCsvariantsStr = result.getString("data-csvariants");
+ JSONArray arr = new JSONArray(dataCsvariantsStr);
+ Assert.assertEquals(1, arr.length());
+ JSONObject first = arr.getJSONObject(0);
+ Assert.assertTrue(first.has("entry_uid") && !first.getString("entry_uid").isEmpty());
+ Assert.assertEquals(contentTypeUid, first.getString("contenttype_uid"));
+ Set aliasSet = jsonArrayToStringSet(first.getJSONArray("variants"));
+ Assert.assertEquals(
+ new HashSet<>(Arrays.asList("cs_personalize_0_0", "cs_personalize_0_3")),
+ aliasSet);
+ }
+
+ @Test
+ public void testGetVariantAliasesMultipleEntries() throws IOException {
+ final String json = "src/test/resources/variant_entries.json";
+ JSONObject full = new ReadResource().readJson(json);
+ JSONArray entries = full.getJSONArray("entries");
+ String contentTypeUid = "movie";
+
+ JSONArray result = Utils.getVariantAliases(entries, contentTypeUid);
+
+ Assert.assertNotNull(result);
+ Assert.assertEquals(3, result.length());
+ JSONObject first = result.getJSONObject(0);
+ Assert.assertTrue(first.has("entry_uid") && !first.getString("entry_uid").isEmpty());
+ Assert.assertEquals(contentTypeUid, first.getString("contenttype_uid"));
+ Set firstSet = jsonArrayToStringSet(first.getJSONArray("variants"));
+ Assert.assertEquals(new HashSet<>(Arrays.asList("cs_personalize_0_0", "cs_personalize_0_3")), firstSet);
+ JSONObject second = result.getJSONObject(1);
+ Assert.assertTrue(second.has("entry_uid") && !second.getString("entry_uid").isEmpty());
+ Assert.assertEquals(1, second.getJSONArray("variants").length());
+ Assert.assertEquals("cs_personalize_0_0", second.getJSONArray("variants").getString(0));
+ JSONObject third = result.getJSONObject(2);
+ Assert.assertTrue(third.has("entry_uid") && !third.getString("entry_uid").isEmpty());
+ Assert.assertEquals(0, third.getJSONArray("variants").length());
+ }
+
+ @Test
+ public void testGetDataCsvariantsAttributeMultipleEntries() throws IOException {
+ final String json = "src/test/resources/variant_entries.json";
+ JSONObject full = new ReadResource().readJson(json);
+ JSONArray entries = full.getJSONArray("entries");
+ String contentTypeUid = "movie";
+
+ JSONObject result = Utils.getDataCsvariantsAttribute(entries, contentTypeUid);
+
+ Assert.assertTrue(result.has("data-csvariants"));
+ String dataCsvariantsStr = result.getString("data-csvariants");
+ JSONArray arr = new JSONArray(dataCsvariantsStr);
+ Assert.assertEquals(3, arr.length());
+ Assert.assertTrue(arr.getJSONObject(0).has("entry_uid") && !arr.getJSONObject(0).getString("entry_uid").isEmpty());
+ Assert.assertEquals(2, arr.getJSONObject(0).getJSONArray("variants").length());
+ Assert.assertTrue(arr.getJSONObject(1).has("entry_uid") && !arr.getJSONObject(1).getString("entry_uid").isEmpty());
+ Assert.assertEquals(1, arr.getJSONObject(1).getJSONArray("variants").length());
+ Assert.assertTrue(arr.getJSONObject(2).has("entry_uid") && !arr.getJSONObject(2).getString("entry_uid").isEmpty());
+ Assert.assertEquals(0, arr.getJSONObject(2).getJSONArray("variants").length());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetVariantAliasesThrowsWhenEntryNull() {
+ Utils.getVariantAliases((JSONObject) null, "landing_page");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetVariantAliasesThrowsWhenContentTypeUidNull() throws IOException {
+ final String json = "src/test/resources/variant_entry_single.json";
+ JSONObject full = new ReadResource().readJson(json);
+ JSONObject entry = full.getJSONObject("entry");
+ Utils.getVariantAliases(entry, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetVariantAliasesThrowsWhenContentTypeUidEmpty() throws IOException {
+ final String json = "src/test/resources/variant_entry_single.json";
+ JSONObject full = new ReadResource().readJson(json);
+ JSONObject entry = full.getJSONObject("entry");
+ Utils.getVariantAliases(entry, "");
+ }
+
+ @Test
+ public void testGetDataCsvariantsAttributeWhenEntryNull() {
+ JSONObject result = Utils.getDataCsvariantsAttribute((JSONObject) null, "landing_page");
+ Assert.assertTrue(result.has("data-csvariants"));
+ Assert.assertEquals("[]", result.getString("data-csvariants"));
+ }
+
}
diff --git a/src/test/resources/variant_entries.json b/src/test/resources/variant_entries.json
new file mode 100644
index 0000000..34f6c9f
--- /dev/null
+++ b/src/test/resources/variant_entries.json
@@ -0,0 +1,71 @@
+{
+ "entries": [
+ {
+ "uid": "entry_uid_1",
+ "_metadata": {},
+ "locale": "en-us",
+ "_version": 1,
+ "title": "Sample Movie",
+ "publish_details": {
+ "time": "2025-12-11T07:56:17.574Z",
+ "user": "test_user",
+ "environment": "test_env",
+ "locale": "en-us",
+ "variants": {
+ "cs_variant_0_0": {
+ "alias": "cs_personalize_0_0",
+ "environment": "test_env",
+ "time": "2025-12-11T07:56:17.574Z",
+ "locale": "en-us",
+ "user": "test_user",
+ "version": 1
+ },
+ "cs_variant_0_3": {
+ "alias": "cs_personalize_0_3",
+ "environment": "test_env",
+ "time": "2025-12-11T07:56:17.582Z",
+ "locale": "en-us",
+ "user": "test_user",
+ "version": 1
+ }
+ }
+ }
+ },
+ {
+ "uid": "entry_uid_2",
+ "_metadata": {},
+ "locale": "en-us",
+ "_version": 2,
+ "title": "Another Movie",
+ "publish_details": {
+ "time": "2025-12-11T07:10:19.964Z",
+ "user": "test_user",
+ "environment": "test_env",
+ "locale": "en-us",
+ "variants": {
+ "cs_variant_0_0": {
+ "alias": "cs_personalize_0_0",
+ "environment": "test_env",
+ "time": "2025-12-11T07:10:19.964Z",
+ "locale": "en-us",
+ "user": "test_user",
+ "version": 2
+ }
+ }
+ }
+ },
+ {
+ "uid": "entry_uid_3",
+ "_metadata": {},
+ "locale": "en-us",
+ "_version": 1,
+ "title": "Movie No Variants",
+ "publish_details": {
+ "time": "2025-11-20T10:00:00.000Z",
+ "user": "test_user",
+ "environment": "test_env",
+ "locale": "en-us"
+ }
+ }
+ ]
+}
diff --git a/src/test/resources/variant_entry_single.json b/src/test/resources/variant_entry_single.json
new file mode 100644
index 0000000..57e92ff
--- /dev/null
+++ b/src/test/resources/variant_entry_single.json
@@ -0,0 +1,39 @@
+{
+ "entry": {
+ "uid": "entry_uid_single",
+ "_metadata": {},
+ "locale": "en-us",
+ "_version": 1,
+ "ACL": {},
+ "_in_progress": false,
+ "title": "Sample Movie",
+ "created_at": "2025-11-20T10:00:00.000Z",
+ "updated_at": "2025-12-11T07:56:17.574Z",
+ "created_by": "test_user",
+ "updated_by": "test_user",
+ "publish_details": {
+ "time": "2025-12-11T07:56:17.574Z",
+ "user": "test_user",
+ "environment": "test_env",
+ "locale": "en-us",
+ "variants": {
+ "cs_variant_0_0": {
+ "alias": "cs_personalize_0_0",
+ "environment": "test_env",
+ "time": "2025-12-11T07:56:17.574Z",
+ "locale": "en-us",
+ "user": "test_user",
+ "version": 1
+ },
+ "cs_variant_0_3": {
+ "alias": "cs_personalize_0_3",
+ "environment": "test_env",
+ "time": "2025-12-11T07:56:17.582Z",
+ "locale": "en-us",
+ "user": "test_user",
+ "version": 1
+ }
+ }
+ }
+ }
+}