Skip to content

Commit 3ce7de5

Browse files
committed
feat: add variant alias utility helpers and tests
Add GetVariantAliases and GetDataCsvariantsAttribute to Utils (JObject/JArray), Include JSON fixtures, xUnit coverage.
1 parent e78c4f2 commit 3ce7de5

5 files changed

Lines changed: 372 additions & 1 deletion

File tree

Contentstack.Utils.Tests/Contentstack.Utils.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,7 @@
3737
<ItemGroup>
3838
<None Remove="contentstack.csharp" />
3939
</ItemGroup>
40+
<ItemGroup>
41+
<Content Include="Resources\**\*.json" CopyToOutputDirectory="PreserveNewest" />
42+
</ItemGroup>
4043
</Project>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"entries": [
3+
{
4+
"uid": "entry_uid_1",
5+
"_metadata": {},
6+
"locale": "en-us",
7+
"_version": 1,
8+
"title": "Sample Movie",
9+
"publish_details": {
10+
"time": "2025-12-11T07:56:17.574Z",
11+
"user": "test_user",
12+
"environment": "test_env",
13+
"locale": "en-us",
14+
"variants": {
15+
"cs_variant_0_0": {
16+
"alias": "cs_personalize_0_0",
17+
"environment": "test_env",
18+
"time": "2025-12-11T07:56:17.574Z",
19+
"locale": "en-us",
20+
"user": "test_user",
21+
"version": 1
22+
},
23+
"cs_variant_0_3": {
24+
"alias": "cs_personalize_0_3",
25+
"environment": "test_env",
26+
"time": "2025-12-11T07:56:17.582Z",
27+
"locale": "en-us",
28+
"user": "test_user",
29+
"version": 1
30+
}
31+
}
32+
}
33+
},
34+
{
35+
"uid": "entry_uid_2",
36+
"_metadata": {},
37+
"locale": "en-us",
38+
"_version": 2,
39+
"title": "Another Movie",
40+
"publish_details": {
41+
"time": "2025-12-11T07:10:19.964Z",
42+
"user": "test_user",
43+
"environment": "test_env",
44+
"locale": "en-us",
45+
"variants": {
46+
"cs_variant_0_0": {
47+
"alias": "cs_personalize_0_0",
48+
"environment": "test_env",
49+
"time": "2025-12-11T07:10:19.964Z",
50+
"locale": "en-us",
51+
"user": "test_user",
52+
"version": 2
53+
}
54+
}
55+
}
56+
},
57+
{
58+
"uid": "entry_uid_3",
59+
"_metadata": {},
60+
"locale": "en-us",
61+
"_version": 1,
62+
"title": "Movie No Variants",
63+
"publish_details": {
64+
"time": "2025-11-20T10:00:00.000Z",
65+
"user": "test_user",
66+
"environment": "test_env",
67+
"locale": "en-us"
68+
}
69+
}
70+
]
71+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"entry": {
3+
"uid": "entry_uid_single",
4+
"_metadata": {},
5+
"locale": "en-us",
6+
"_version": 1,
7+
"ACL": {},
8+
"_in_progress": false,
9+
"title": "Sample Movie",
10+
"created_at": "2025-11-20T10:00:00.000Z",
11+
"updated_at": "2025-12-11T07:56:17.574Z",
12+
"created_by": "test_user",
13+
"updated_by": "test_user",
14+
"publish_details": {
15+
"time": "2025-12-11T07:56:17.574Z",
16+
"user": "test_user",
17+
"environment": "test_env",
18+
"locale": "en-us",
19+
"variants": {
20+
"cs_variant_0_0": {
21+
"alias": "cs_personalize_0_0",
22+
"environment": "test_env",
23+
"time": "2025-12-11T07:56:17.574Z",
24+
"locale": "en-us",
25+
"user": "test_user",
26+
"version": 1
27+
},
28+
"cs_variant_0_3": {
29+
"alias": "cs_personalize_0_3",
30+
"environment": "test_env",
31+
"time": "2025-12-11T07:56:17.582Z",
32+
"locale": "en-us",
33+
"user": "test_user",
34+
"version": 1
35+
}
36+
}
37+
}
38+
}
39+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Newtonsoft.Json.Linq;
5+
using Xunit;
6+
7+
namespace Contentstack.Utils.Tests
8+
{
9+
public class VariantAliasesTest
10+
{
11+
private static JObject ReadJsonRoot(string fileName)
12+
{
13+
var path = Path.Combine(AppContext.BaseDirectory, "Resources", fileName);
14+
return JObject.Parse(File.ReadAllText(path));
15+
}
16+
17+
private static HashSet<string> JsonArrayToStringSet(JArray arr)
18+
{
19+
var set = new HashSet<string>();
20+
foreach (var t in arr)
21+
{
22+
set.Add(t.ToString());
23+
}
24+
return set;
25+
}
26+
27+
[Fact]
28+
public void GetVariantAliases_SingleEntry_ReturnsAliases()
29+
{
30+
JObject full = ReadJsonRoot("variantsSingleEntry.json");
31+
JObject entry = (JObject)full["entry"];
32+
const string contentTypeUid = "movie";
33+
34+
JObject result = Utils.GetVariantAliases(entry, contentTypeUid);
35+
36+
Assert.True(result["entry_uid"] != null && !string.IsNullOrEmpty(result["entry_uid"].ToString()));
37+
Assert.Equal(contentTypeUid, result["contenttype_uid"].ToString());
38+
JArray variants = (JArray)result["variants"];
39+
Assert.NotNull(variants);
40+
var aliasSet = JsonArrayToStringSet(variants);
41+
Assert.Equal(
42+
new HashSet<string> { "cs_personalize_0_0", "cs_personalize_0_3" },
43+
aliasSet);
44+
}
45+
46+
[Fact]
47+
public void GetDataCsvariantsAttribute_SingleEntry_ReturnsJsonArrayString()
48+
{
49+
JObject full = ReadJsonRoot("variantsSingleEntry.json");
50+
JObject entry = (JObject)full["entry"];
51+
const string contentTypeUid = "movie";
52+
53+
JObject result = Utils.GetDataCsvariantsAttribute(entry, contentTypeUid);
54+
55+
Assert.True(result["data-csvariants"] != null);
56+
string dataCsvariantsStr = result["data-csvariants"].ToString();
57+
JArray arr = JArray.Parse(dataCsvariantsStr);
58+
Assert.Single(arr);
59+
JObject first = (JObject)arr[0];
60+
Assert.True(first["entry_uid"] != null && !string.IsNullOrEmpty(first["entry_uid"].ToString()));
61+
Assert.Equal(contentTypeUid, first["contenttype_uid"].ToString());
62+
var aliasSet = JsonArrayToStringSet((JArray)first["variants"]);
63+
Assert.Equal(
64+
new HashSet<string> { "cs_personalize_0_0", "cs_personalize_0_3" },
65+
aliasSet);
66+
}
67+
68+
[Fact]
69+
public void GetVariantAliases_MultipleEntries_ReturnsOneResultPerEntryWithUid()
70+
{
71+
JObject full = ReadJsonRoot("variantsEntries.json");
72+
JArray entries = (JArray)full["entries"];
73+
const string contentTypeUid = "movie";
74+
75+
JArray result = Utils.GetVariantAliases(entries, contentTypeUid);
76+
77+
Assert.NotNull(result);
78+
Assert.Equal(3, result.Count);
79+
80+
JObject first = (JObject)result[0];
81+
Assert.True(first["entry_uid"] != null && !string.IsNullOrEmpty(first["entry_uid"].ToString()));
82+
Assert.Equal(contentTypeUid, first["contenttype_uid"].ToString());
83+
var firstSet = JsonArrayToStringSet((JArray)first["variants"]);
84+
Assert.Equal(
85+
new HashSet<string> { "cs_personalize_0_0", "cs_personalize_0_3" },
86+
firstSet);
87+
88+
JObject second = (JObject)result[1];
89+
Assert.True(second["entry_uid"] != null && !string.IsNullOrEmpty(second["entry_uid"].ToString()));
90+
Assert.Single((JArray)second["variants"]);
91+
Assert.Equal("cs_personalize_0_0", ((JArray)second["variants"])[0].ToString());
92+
93+
JObject third = (JObject)result[2];
94+
Assert.True(third["entry_uid"] != null && !string.IsNullOrEmpty(third["entry_uid"].ToString()));
95+
Assert.Empty((JArray)third["variants"]);
96+
}
97+
98+
[Fact]
99+
public void GetDataCsvariantsAttribute_MultipleEntries_ReturnsJsonArrayString()
100+
{
101+
JObject full = ReadJsonRoot("variantsEntries.json");
102+
JArray entries = (JArray)full["entries"];
103+
const string contentTypeUid = "movie";
104+
105+
JObject result = Utils.GetDataCsvariantsAttribute(entries, contentTypeUid);
106+
107+
Assert.True(result["data-csvariants"] != null);
108+
string dataCsvariantsStr = result["data-csvariants"].ToString();
109+
JArray arr = JArray.Parse(dataCsvariantsStr);
110+
Assert.Equal(3, arr.Count);
111+
Assert.True(((JObject)arr[0])["entry_uid"] != null && !string.IsNullOrEmpty(((JObject)arr[0])["entry_uid"].ToString()));
112+
Assert.Equal(2, ((JArray)((JObject)arr[0])["variants"]).Count);
113+
Assert.True(((JObject)arr[1])["entry_uid"] != null && !string.IsNullOrEmpty(((JObject)arr[1])["entry_uid"].ToString()));
114+
Assert.Single((JArray)((JObject)arr[1])["variants"]);
115+
Assert.True(((JObject)arr[2])["entry_uid"] != null && !string.IsNullOrEmpty(((JObject)arr[2])["entry_uid"].ToString()));
116+
Assert.Empty((JArray)((JObject)arr[2])["variants"]);
117+
}
118+
119+
[Fact]
120+
public void GetVariantAliases_ThrowsWhenEntryNull()
121+
{
122+
Assert.Throws<ArgumentException>(() => Utils.GetVariantAliases((JObject)null, "landing_page"));
123+
}
124+
125+
[Fact]
126+
public void GetVariantAliases_ThrowsWhenContentTypeUidNull()
127+
{
128+
JObject full = ReadJsonRoot("variantsSingleEntry.json");
129+
JObject entry = (JObject)full["entry"];
130+
Assert.Throws<ArgumentException>(() => Utils.GetVariantAliases(entry, null));
131+
}
132+
133+
[Fact]
134+
public void GetVariantAliases_ThrowsWhenContentTypeUidEmpty()
135+
{
136+
JObject full = ReadJsonRoot("variantsSingleEntry.json");
137+
JObject entry = (JObject)full["entry"];
138+
Assert.Throws<ArgumentException>(() => Utils.GetVariantAliases(entry, ""));
139+
}
140+
141+
[Fact]
142+
public void GetDataCsvariantsAttribute_WhenEntryNull_ReturnsEmptyArrayString()
143+
{
144+
JObject result = Utils.GetDataCsvariantsAttribute((JObject)null, "landing_page");
145+
Assert.True(result["data-csvariants"] != null);
146+
Assert.Equal("[]", result["data-csvariants"].ToString());
147+
}
148+
}
149+
}

Contentstack.Utils/Utils.cs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
using System.Collections.Generic;
1+
using System.Collections.Generic;
22
using Contentstack.Utils.Models;
33
using HtmlAgilityPack;
44
using Contentstack.Utils.Extensions;
55
using Contentstack.Utils.Interfaces;
66
using System;
77
using Contentstack.Utils.Enums;
8+
using Newtonsoft.Json;
9+
using Newtonsoft.Json.Linq;
810

911
namespace Contentstack.Utils
1012
{
@@ -286,5 +288,112 @@ private static object GetParentTagsValue(string dataValue, bool tagsAsObject)
286288
return $"data-cslp-parent-field={dataValue}";
287289
}
288290
}
291+
292+
public static JObject GetVariantAliases(JObject entry, string contentTypeUid)
293+
{
294+
if (string.IsNullOrEmpty(contentTypeUid))
295+
{
296+
throw new ArgumentException("ContentType is required.");
297+
}
298+
if (entry == null)
299+
{
300+
throw new ArgumentException("Entry must not be null.");
301+
}
302+
if (!entry.ContainsKey("uid") || entry["uid"] == null || entry["uid"].Type == JTokenType.Null)
303+
{
304+
throw new ArgumentException("Entry must contain uid.");
305+
}
306+
307+
string entryUid = entry["uid"]?.ToString() ?? "";
308+
JArray variantsArray = ExtractVariantAliasesFromEntry(entry);
309+
JObject result = new JObject
310+
{
311+
["entry_uid"] = entryUid,
312+
["contenttype_uid"] = contentTypeUid,
313+
["variants"] = variantsArray
314+
};
315+
return result;
316+
}
317+
318+
public static JArray GetVariantAliases(JArray entries, string contentTypeUid)
319+
{
320+
if (string.IsNullOrEmpty(contentTypeUid))
321+
{
322+
throw new ArgumentException("ContentType is required.");
323+
}
324+
if (entries == null)
325+
{
326+
return new JArray();
327+
}
328+
JArray variantResults = new JArray();
329+
foreach (JToken token in entries)
330+
{
331+
JObject entry = token as JObject;
332+
if (entry != null && entry.ContainsKey("uid") && entry["uid"] != null && entry["uid"].Type != JTokenType.Null)
333+
{
334+
variantResults.Add(GetVariantAliases(entry, contentTypeUid));
335+
}
336+
}
337+
return variantResults;
338+
}
339+
340+
public static JObject GetDataCsvariantsAttribute(JObject entry, string contentTypeUid)
341+
{
342+
if (entry == null)
343+
{
344+
JObject result = new JObject();
345+
result["data-csvariants"] = "[]";
346+
return result;
347+
}
348+
JArray entries = new JArray();
349+
entries.Add(entry);
350+
return GetDataCsvariantsAttribute(entries, contentTypeUid);
351+
}
352+
353+
public static JObject GetDataCsvariantsAttribute(JArray entries, string contentTypeUid)
354+
{
355+
JObject result = new JObject();
356+
if (entries == null)
357+
{
358+
result["data-csvariants"] = "[]";
359+
return result;
360+
}
361+
if (string.IsNullOrEmpty(contentTypeUid))
362+
{
363+
throw new ArgumentException("ContentType is required.");
364+
}
365+
366+
JArray variantResults = GetVariantAliases(entries, contentTypeUid);
367+
result["data-csvariants"] = variantResults.ToString(Formatting.None);
368+
return result;
369+
}
370+
371+
private static JArray ExtractVariantAliasesFromEntry(JObject entry)
372+
{
373+
JArray variantArray = new JArray();
374+
JObject publishDetails = entry["publish_details"] as JObject;
375+
if (publishDetails == null)
376+
{
377+
return variantArray;
378+
}
379+
JObject variants = publishDetails["variants"] as JObject;
380+
if (variants == null)
381+
{
382+
return variantArray;
383+
}
384+
385+
foreach (JProperty prop in variants.Properties())
386+
{
387+
if (prop.Value is JObject valueObj)
388+
{
389+
string alias = valueObj["alias"]?.ToString();
390+
if (!string.IsNullOrEmpty(alias))
391+
{
392+
variantArray.Add(alias.Trim());
393+
}
394+
}
395+
}
396+
return variantArray;
397+
}
289398
}
290399
}

0 commit comments

Comments
 (0)