Skip to content

Commit e3bf7d9

Browse files
Merge pull request #46 from contentstack/development
staging PR
2 parents eb0e462 + 7f8911b commit e3bf7d9

File tree

6 files changed

+485
-1
lines changed

6 files changed

+485
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
### Version: 1.1.0
2+
#### Date: March-24-2026
3+
- Added `GetVariantAliases` and `GetDataCsvariantsAttribute` for variant alias extraction and `data-csvariants` serialization; Invalid arguments throw `ArgumentException`.
4+
5+
16
### Version: 1.0.7
27
#### Date: January-12-2026
38
- Improved error messages

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: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
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+
[Fact]
150+
public void GetVariantAliases_ThrowsWhenUidMissing()
151+
{
152+
var entry = new JObject { ["title"] = "no-uid" };
153+
Assert.Throws<ArgumentException>(() => Utils.GetVariantAliases(entry, "movie"));
154+
}
155+
156+
[Fact]
157+
public void GetVariantAliases_ThrowsWhenUidNull()
158+
{
159+
var entry = new JObject { ["uid"] = JValue.CreateNull() };
160+
Assert.Throws<ArgumentException>(() => Utils.GetVariantAliases(entry, "movie"));
161+
}
162+
163+
[Fact]
164+
public void GetVariantAliases_Batch_ThrowsWhenContentTypeUidNull()
165+
{
166+
var entries = new JArray { new JObject { ["uid"] = "a" } };
167+
Assert.Throws<ArgumentException>(() => Utils.GetVariantAliases(entries, null));
168+
}
169+
170+
[Fact]
171+
public void GetVariantAliases_Batch_ThrowsWhenContentTypeUidEmpty()
172+
{
173+
var entries = new JArray { new JObject { ["uid"] = "a" } };
174+
Assert.Throws<ArgumentException>(() => Utils.GetVariantAliases(entries, ""));
175+
}
176+
177+
[Fact]
178+
public void GetDataCsvariantsAttribute_WhenEntriesArrayNull_ReturnsEmptyArrayString()
179+
{
180+
JObject result = Utils.GetDataCsvariantsAttribute((JArray)null, "movie");
181+
Assert.Equal("[]", result["data-csvariants"].ToString());
182+
}
183+
184+
[Fact]
185+
public void GetDataCsvariantsAttribute_Batch_ThrowsWhenContentTypeUidNull()
186+
{
187+
var entries = new JArray { new JObject { ["uid"] = "a" } };
188+
Assert.Throws<ArgumentException>(() => Utils.GetDataCsvariantsAttribute(entries, null));
189+
}
190+
191+
[Fact]
192+
public void GetDataCsvariantsAttribute_Batch_ThrowsWhenContentTypeUidEmpty()
193+
{
194+
var entries = new JArray { new JObject { ["uid"] = "a" } };
195+
Assert.Throws<ArgumentException>(() => Utils.GetDataCsvariantsAttribute(entries, ""));
196+
}
197+
198+
[Fact]
199+
public void GetVariantAliases_ReturnsEmptyVariantsWhenPublishDetailsMissing()
200+
{
201+
var entry = new JObject { ["uid"] = "blt_no_pd" };
202+
JObject result = Utils.GetVariantAliases(entry, "movie");
203+
Assert.Equal("blt_no_pd", result["entry_uid"].ToString());
204+
Assert.Equal("movie", result["contenttype_uid"].ToString());
205+
Assert.Empty((JArray)result["variants"]);
206+
}
207+
208+
[Fact]
209+
public void GetVariantAliases_ReturnsEmptyVariantsWhenVariantsObjectEmpty()
210+
{
211+
var entry = new JObject
212+
{
213+
["uid"] = "blt_empty_v",
214+
["publish_details"] = new JObject
215+
{
216+
["variants"] = new JObject()
217+
}
218+
};
219+
JObject result = Utils.GetVariantAliases(entry, "movie");
220+
Assert.Empty((JArray)result["variants"]);
221+
}
222+
223+
[Fact]
224+
public void GetVariantAliases_ReturnsEmptyVariantsWhenVariantsKeyMissing()
225+
{
226+
var entry = new JObject
227+
{
228+
["uid"] = "blt_no_variants_key",
229+
["publish_details"] = new JObject { ["time"] = "2025-01-01T00:00:00.000Z" }
230+
};
231+
JObject result = Utils.GetVariantAliases(entry, "movie");
232+
Assert.Empty((JArray)result["variants"]);
233+
}
234+
235+
[Fact]
236+
public void GetVariantAliases_SkipsVariantWhenAliasMissingOrEmpty()
237+
{
238+
var entry = new JObject
239+
{
240+
["uid"] = "blt_skip",
241+
["publish_details"] = new JObject
242+
{
243+
["variants"] = new JObject
244+
{
245+
["v1"] = new JObject { ["alias"] = "keep_me" },
246+
["v2"] = new JObject(),
247+
["v3"] = new JObject { ["alias"] = "" }
248+
}
249+
}
250+
};
251+
JObject result = Utils.GetVariantAliases(entry, "page");
252+
var variants = (JArray)result["variants"];
253+
Assert.Single(variants);
254+
Assert.Equal("keep_me", variants[0].ToString());
255+
}
256+
}
257+
}

0 commit comments

Comments
 (0)