Skip to content

Commit ecba1f2

Browse files
authored
Merge pull request #106 from contentstack/fix/DX-3134
Added the last used content-type and entry-uid for the timeline support and added the test cases
2 parents e6a58d3 + 425f220 commit ecba1f2

File tree

9 files changed

+335
-55
lines changed

9 files changed

+335
-55
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
### Version: 2.22.1
2+
#### Date: June-13-2025
3+
4+
##### Fix:
5+
- Fixed Timeline issue of Entry not getting updated
6+
17
### Version: 2.22.0
28
#### Date: March-03-2025
39

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// Contentstack.Core/ContentstackClientTest.cs
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Reflection;
5+
using System.Threading.Tasks;
6+
using Xunit;
7+
using Contentstack.Core.Configuration;
8+
9+
namespace Contentstack.Core.Tests
10+
{
11+
public class ContentstackClientTest
12+
{
13+
private ContentstackClient CreateClient()
14+
{
15+
var options = new ContentstackOptions()
16+
{
17+
ApiKey = "api_key",
18+
DeliveryToken = "delivery_token",
19+
Environment = "environment",
20+
Version = "1.2.3",
21+
LivePreview = new LivePreviewConfig()
22+
{
23+
Enable = true,
24+
Host = "https://preview.contentstack.com",
25+
ReleaseId = "rid",
26+
PreviewTimestamp = "ts",
27+
PreviewToken = "pt"
28+
29+
}
30+
};
31+
return new ContentstackClient(options);
32+
}
33+
34+
private string GetPrivateField(ContentstackClient client, string fieldName)
35+
{
36+
var field = typeof(ContentstackClient).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
37+
return (string)field.GetValue(client);
38+
}
39+
40+
[Fact]
41+
public void SetEntryUid_SetsValue_WhenNonEmpty()
42+
{
43+
var client = CreateClient();
44+
client.SetEntryUid("entry123");
45+
Assert.Equal("entry123", GetPrivateField(client, "currentEntryUid"));
46+
}
47+
48+
[Fact]
49+
public void SetEntryUid_DoesNotSet_WhenEmpty()
50+
{
51+
var client = CreateClient();
52+
client.SetEntryUid("entry123");
53+
client.SetEntryUid("");
54+
Assert.Equal("entry123", GetPrivateField(client, "currentEntryUid"));
55+
}
56+
57+
[Fact]
58+
public void SetEntryUid_DoesNotSet_WhenNull()
59+
{
60+
var client = CreateClient();
61+
client.SetEntryUid("entry123");
62+
client.SetEntryUid(null);
63+
Assert.Equal("entry123", GetPrivateField(client, "currentEntryUid"));
64+
}
65+
66+
[Fact]
67+
public void ContentType_SetscurrentContenttypeUid_WhenNonEmpty()
68+
{
69+
var client = CreateClient();
70+
client.ContentType("blog");
71+
Assert.Equal("blog", GetPrivateField(client, "currentContenttypeUid"));
72+
}
73+
74+
[Fact]
75+
public void ContentType_DoesNotSet_WhenEmpty()
76+
{
77+
var client = CreateClient();
78+
client.ContentType("blog");
79+
client.ContentType("");
80+
Assert.Equal("blog", GetPrivateField(client, "currentContenttypeUid"));
81+
}
82+
83+
[Fact]
84+
public void ContentType_DoesNotSet_WhenNull()
85+
{
86+
var client = CreateClient();
87+
client.ContentType("blog");
88+
client.ContentType(null);
89+
Assert.Equal("blog", GetPrivateField(client, "currentContenttypeUid"));
90+
}
91+
92+
[Fact]
93+
public void ContentType_ReturnsContentTypeInstance()
94+
{
95+
var client = CreateClient();
96+
var contentType = client.ContentType("blog");
97+
Assert.NotNull(contentType);
98+
Assert.Equal("blog", contentType.ContentTypeId);
99+
}
100+
101+
[Fact]
102+
public void GlobalField_ReturnsGlobalFieldInstance()
103+
{
104+
var client = CreateClient();
105+
var globalField = client.GlobalField("author");
106+
Assert.NotNull(globalField);
107+
Assert.Equal("author", globalField.GlobalFieldId);
108+
}
109+
110+
[Fact]
111+
public void Asset_ReturnsAssetInstance()
112+
{
113+
var client = CreateClient();
114+
var asset = client.Asset("asset_uid");
115+
Assert.NotNull(asset);
116+
Assert.Equal("asset_uid", asset.Uid);
117+
}
118+
119+
[Fact]
120+
public void AssetLibrary_ReturnsAssetLibraryInstance()
121+
{
122+
var client = CreateClient();
123+
var assetLibrary = client.AssetLibrary();
124+
Assert.NotNull(assetLibrary);
125+
}
126+
127+
[Fact]
128+
public void Taxonomies_ReturnsTaxonomyInstance()
129+
{
130+
var client = CreateClient();
131+
var taxonomy = client.Taxonomies();
132+
Assert.NotNull(taxonomy);
133+
}
134+
135+
[Fact]
136+
public void GetVersion_ReturnsVersion()
137+
{
138+
var client = CreateClient();
139+
var t = client.GetVersion();
140+
Assert.Equal("1.2.3", client.GetVersion());
141+
}
142+
143+
[Fact]
144+
public void GetApplicationKey_ReturnsApiKey()
145+
{
146+
var client = CreateClient();
147+
Assert.Equal("api_key", client.GetApplicationKey());
148+
}
149+
150+
[Fact]
151+
public void GetAccessToken_ReturnsDeliveryToken()
152+
{
153+
var client = CreateClient();
154+
Assert.Equal("delivery_token", client.GetAccessToken());
155+
}
156+
157+
[Fact]
158+
public void GetLivePreviewConfig_ReturnsConfig()
159+
{
160+
var client = CreateClient();
161+
Assert.NotNull(client.GetLivePreviewConfig());
162+
}
163+
164+
[Fact]
165+
public void RemoveHeader_RemovesHeader()
166+
{
167+
var client = CreateClient();
168+
client.SetHeader("custom", "value");
169+
client.RemoveHeader("custom");
170+
var localHeaders = typeof(ContentstackClient)
171+
.GetField("_LocalHeaders", BindingFlags.NonPublic | BindingFlags.Instance)
172+
.GetValue(client) as Dictionary<string, object>;
173+
Assert.False(localHeaders.ContainsKey("custom"));
174+
}
175+
176+
[Fact]
177+
public void SetHeader_AddsHeader()
178+
{
179+
var client = CreateClient();
180+
client.SetHeader("custom", "value");
181+
var localHeaders = typeof(ContentstackClient)
182+
.GetField("_LocalHeaders", BindingFlags.NonPublic | BindingFlags.Instance)
183+
.GetValue(client) as Dictionary<string, object>;
184+
Assert.True(localHeaders.ContainsKey("custom"));
185+
Assert.Equal("value", localHeaders["custom"]);
186+
}
187+
188+
[Fact]
189+
public async Task LivePreviewQueryAsync_SetsLivePreviewConfigFields()
190+
{
191+
var client = CreateClient();
192+
client.ContentType("ctuid");
193+
client.ContentType("ctuid").Entry("euid");
194+
// Mock GetLivePreviewData to avoid actual HTTP call
195+
var method = typeof(ContentstackClient).GetMethod("GetLivePreviewData", BindingFlags.NonPublic | BindingFlags.Instance);
196+
method.Invoke(client, null); // Just to ensure method exists
197+
198+
var query = new Dictionary<string, string>
199+
{
200+
{ "live_preview", "hash" },
201+
{ "release_id", "rid" },
202+
{ "preview_timestamp", "ts" }
203+
};
204+
205+
// Patch GetLivePreviewData to return a dummy JObject
206+
207+
// Since GetLivePreviewData is private and does a real HTTP call,
208+
// we only test the config fields are set correctly before the call
209+
await client.LivePreviewQueryAsync(query);
210+
var v = client.GetLivePreviewConfig();
211+
Assert.Equal("ctuid", GetPrivateField(client, "currentContenttypeUid"));
212+
Assert.Equal("euid", GetPrivateField(client, "currentEntryUid"));
213+
Assert.Equal(true, v.Enable );
214+
Assert.Equal("rid", v.ReleaseId);
215+
Assert.Equal("ts", v.PreviewTimestamp);
216+
Assert.Equal("pt", v.PreviewToken);
217+
}
218+
219+
// For SyncRecursive, SyncPaginationToken, SyncToken, you should mock HTTP calls.
220+
// Here we just check that the methods exist and can be called (will throw if not configured).
221+
[Fact]
222+
public async Task SyncRecursive_ThrowsOrReturns()
223+
{
224+
var client = CreateClient();
225+
await Assert.ThrowsAnyAsync<Exception>(() => client.SyncRecursive());
226+
}
227+
228+
[Fact]
229+
public async Task SyncPaginationToken_ThrowsOrReturns()
230+
{
231+
var client = CreateClient();
232+
await Assert.ThrowsAnyAsync<Exception>(() => client.SyncPaginationToken("pagetoken"));
233+
}
234+
235+
[Fact]
236+
public async Task SyncToken_ThrowsOrReturns()
237+
{
238+
var client = CreateClient();
239+
await Assert.ThrowsAnyAsync<Exception>(() => client.SyncToken("synctoken"));
240+
}
241+
}
242+
}

Contentstack.Core/Configuration/Config.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ internal string getBaseUrl (LivePreviewConfig livePreviewConfig, string contentT
101101
{
102102
if (livePreviewConfig != null
103103
&& livePreviewConfig.Enable
104-
&& livePreviewConfig.LivePreview != "init"
104+
&& livePreviewConfig.LivePreview != "init" && !string.IsNullOrEmpty(livePreviewConfig.LivePreview)
105105
&& livePreviewConfig.ContentTypeUID == contentTypeUID)
106106
{
107107
return getLivePreviewUrl(livePreviewConfig);

Contentstack.Core/ContentstackClient.cs

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ private string _Url
5656
}
5757
}
5858
private Dictionary<string, object> _StackHeaders = new Dictionary<string, object>();
59+
60+
// This is used to store the last content type UID for live preview
61+
private string currentContenttypeUid = null;
62+
private string currentEntryUid = null;
5963
public List<IContentstackPlugin> Plugins { get; set; } = new List<IContentstackPlugin>();
6064
/// <summary>
6165
/// Initializes a instance of the <see cref="ContentstackClient"/> class.
@@ -349,14 +353,14 @@ private async Task<JObject> GetLivePreviewData()
349353
foreach (var header in headers)
350354
{
351355
if (this.LivePreviewConfig.Enable == true
352-
&& header.Key == "access_token")
356+
&& header.Key == "access_token" && !string.IsNullOrEmpty(LivePreviewConfig.LivePreview))
353357
{
354358
continue;
355359
}
356360
headerAll.Add(header.Key, (String)header.Value);
357361
}
358362
}
359-
mainJson.Add("live_preview", this.LivePreviewConfig.LivePreview ?? "init");
363+
mainJson.Add("live_preview", string.IsNullOrEmpty(this.LivePreviewConfig.LivePreview) ? "init" : this.LivePreviewConfig.LivePreview);
360364

361365
if (!string.IsNullOrEmpty(this.LivePreviewConfig.ManagementToken))
362366
{
@@ -384,7 +388,8 @@ private async Task<JObject> GetLivePreviewData()
384388
{
385389
HttpRequestHandler RequestHandler = new HttpRequestHandler(this);
386390
//string branch = this.Config.Branch ? this.Config.Branch : "main";
387-
var outputResult = await RequestHandler.ProcessRequest(String.Format("{0}/content_types/{1}/entries/{2}", this.Config.getLivePreviewUrl(this.LivePreviewConfig), this.LivePreviewConfig.ContentTypeUID, this.LivePreviewConfig.EntryUID), headerAll, mainJson, Branch: this.Config.Branch, isLivePreview: true, timeout: this.Config.Timeout, proxy: this.Config.Proxy);
391+
string URL = String.Format("{0}/content_types/{1}/entries/{2}", this.Config.getBaseUrl(this.LivePreviewConfig, this.LivePreviewConfig.ContentTypeUID), this.LivePreviewConfig.ContentTypeUID, this.LivePreviewConfig.EntryUID);
392+
var outputResult = await RequestHandler.ProcessRequest(URL, headerAll, mainJson, Branch: this.Config.Branch, isLivePreview: true, timeout: this.Config.Timeout, proxy: this.Config.Proxy);
388393
JObject data = JsonConvert.DeserializeObject<JObject>(outputResult.Replace("\r\n", ""), this.SerializerSettings);
389394
return (JObject)data["entry"];
390395
}
@@ -394,6 +399,13 @@ private async Task<JObject> GetLivePreviewData()
394399
}
395400
}
396401

402+
public void SetEntryUid(string entryUid)
403+
{
404+
if (!string.IsNullOrEmpty(entryUid))
405+
{
406+
this.currentEntryUid = entryUid;
407+
}
408+
}
397409
/// <summary>
398410
/// Represents a ContentType. Creates ContenntType Instance.
399411
/// </summary>
@@ -407,6 +419,10 @@ private async Task<JObject> GetLivePreviewData()
407419
/// </example>
408420
public ContentType ContentType(String contentTypeName)
409421
{
422+
if (!string.IsNullOrEmpty(contentTypeName))
423+
{
424+
this.currentContenttypeUid = contentTypeName;
425+
}
410426
ContentType contentType = new ContentType(contentTypeName);
411427
contentType.SetStackInstance(this);
412428

@@ -488,7 +504,7 @@ public Taxonomy Taxonomies()
488504
/// </example>
489505
public string GetVersion()
490506
{
491-
return Version;
507+
return this.Config.Version;
492508
}
493509

494510
/// <summary>
@@ -595,27 +611,39 @@ public void SetHeader(string key, string value)
595611
public async Task LivePreviewQueryAsync(Dictionary<string, string> query)
596612
{
597613
this.LivePreviewConfig.LivePreview = null;
598-
this.LivePreviewConfig.ContentTypeUID = null;
599-
this.LivePreviewConfig.EntryUID = null;
600-
614+
this.LivePreviewConfig.PreviewTimestamp = null;
615+
this.LivePreviewConfig.ReleaseId = null;
601616
if (query.Keys.Contains("content_type_uid"))
602617
{
603618
string contentTypeUID = null;
604619
query.TryGetValue("content_type_uid", out contentTypeUID);
605620
this.LivePreviewConfig.ContentTypeUID = contentTypeUID;
606621
}
622+
else if (!string.IsNullOrEmpty(this.currentContenttypeUid))
623+
{
624+
this.LivePreviewConfig.ContentTypeUID = this.currentContenttypeUid;
625+
}
626+
627+
607628
if (query.Keys.Contains("entry_uid"))
608629
{
609-
string contentTypeUID = null;
610-
query.TryGetValue("entry_uid", out contentTypeUID);
611-
this.LivePreviewConfig.EntryUID = contentTypeUID;
630+
string entryUID = null;
631+
query.TryGetValue("entry_uid", out entryUID);
632+
this.LivePreviewConfig.EntryUID = entryUID;
612633
}
634+
else if (!string.IsNullOrEmpty(this.currentEntryUid))
635+
{
636+
this.LivePreviewConfig.EntryUID = this.currentEntryUid;
637+
}
638+
639+
613640
if (query.Keys.Contains("live_preview"))
614641
{
615642
string hash = null;
616643
query.TryGetValue("live_preview", out hash);
617644
this.LivePreviewConfig.LivePreview = hash;
618645
}
646+
619647
if (query.Keys.Contains("release_id"))
620648
{
621649
string ReleaseId = null;
@@ -628,7 +656,10 @@ public async Task LivePreviewQueryAsync(Dictionary<string, string> query)
628656
query.TryGetValue("preview_timestamp", out PreviewTimestamp);
629657
this.LivePreviewConfig.PreviewTimestamp = PreviewTimestamp;
630658
}
631-
this.LivePreviewConfig.PreviewResponse = await GetLivePreviewData();
659+
//if (!string.IsNullOrEmpty(this.LivePreviewConfig.LivePreview))
660+
//{
661+
// this.LivePreviewConfig.PreviewResponse = await GetLivePreviewData();
662+
//}
632663
}
633664

634665
/// <summary>

0 commit comments

Comments
 (0)