diff --git a/Kontent.Ai.Delivery.Abstractions/ContentItems/IContentItem.cs b/Kontent.Ai.Delivery.Abstractions/ContentItems/IContentItem.cs index e9523383..15949eab 100644 --- a/Kontent.Ai.Delivery.Abstractions/ContentItems/IContentItem.cs +++ b/Kontent.Ai.Delivery.Abstractions/ContentItems/IContentItem.cs @@ -8,5 +8,5 @@ public interface IContentItem /// /// Represents system attributes of a content item. /// - public IContentItemSystemAttributes System { get; set; } + public IContentItemSystemAttributes System { get; } } diff --git a/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItem.cs b/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItem.cs index 5cbb316e..c48dd80c 100644 --- a/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItem.cs +++ b/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItem.cs @@ -7,10 +7,15 @@ namespace Kontent.Ai.Delivery.Abstractions; /// public interface ISyncItem { + /// + /// Retrieves runtime strongly typed item if CustomTypeProvider is registered, otherwise null. + /// + object StronglyTypedData { get; } + /// /// Retrieves content item information and element values. /// - object Data { get; } + ISyncItemData Data { get; } /// /// Gets the information whether the content item was modified or deleted since the last synchronization. diff --git a/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItemData.cs b/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItemData.cs new file mode 100644 index 00000000..6f3990f8 --- /dev/null +++ b/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItemData.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Kontent.Ai.Delivery.Abstractions; + +/// +/// Represents a delta update. +/// +public interface ISyncItemData : IContentItem +{ + /// + /// Retrieves key:value pairs representing content item elements. + /// + Dictionary Elements { get; } +} \ No newline at end of file diff --git a/Kontent.Ai.Delivery.Tests/DeliveryClientTests.cs b/Kontent.Ai.Delivery.Tests/DeliveryClientTests.cs index 626bee26..baef9b17 100644 --- a/Kontent.Ai.Delivery.Tests/DeliveryClientTests.cs +++ b/Kontent.Ai.Delivery.Tests/DeliveryClientTests.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -14,7 +13,6 @@ using Kontent.Ai.Delivery.ContentItems; using Kontent.Ai.Delivery.ContentItems.RichText.Blocks; using Kontent.Ai.Delivery.SharedModels; -using Kontent.Ai.Delivery.Sync; using Kontent.Ai.Delivery.Tests.Factories; using Kontent.Ai.Delivery.Tests.Models; using Kontent.Ai.Delivery.Tests.Models.ContentTypes; @@ -1851,7 +1849,7 @@ public async Task SyncApi_PostSyncInitAsync_WithParameters_GetContinuationToken( } [Fact] - public async Task SyncApi_GetSyncAsync_GetSyncItems_WithTypeProvider() + public async Task SyncApi_GetSyncAsync_GetSyncItems_WithTypeProvider_ReturnsStronglyTypedData() { var mockedResponse = await File.ReadAllTextAsync(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}DeliveryClient{Path.DirectorySeparatorChar}sync.json")); @@ -1872,30 +1870,20 @@ public async Task SyncApi_GetSyncAsync_GetSyncItems_WithTypeProvider() for (int i = 0; i < expectedItems.Count; i++) { + var article = sync.SyncItems[i].StronglyTypedData as Article; var expectedItem = expectedItems[i]; - var syncItem = (Article)sync.SyncItems[i].Data; - - var expectedSystemValues = expectedItem["data"]["system"]; var expectedElementValues = expectedItem["data"]["elements"]; - var syncItemSystemValues = syncItem.System; - - Assert.Equal(expectedSystemValues["codename"].ToString(), syncItemSystemValues.Codename.ToString()); - Assert.Equal(expectedSystemValues["name"].ToString(), syncItemSystemValues.Name.ToString()); - Assert.Equal(expectedSystemValues["id"].ToString(), syncItemSystemValues.Id.ToString()); - Assert.Equal(expectedSystemValues["type"].ToString(), syncItemSystemValues.Type.ToString()); - Assert.Equal(expectedSystemValues["language"].ToString(), syncItemSystemValues.Language.ToString()); - Assert.Equal(expectedSystemValues["collection"].ToString(), syncItemSystemValues.Collection.ToString()); - Assert.Equal(expectedSystemValues["workflow_step"].ToString(), syncItemSystemValues.WorkflowStep.ToString()); - - Assert.Equal(expectedElementValues["title"]["value"].ToString(), syncItem.Title); - + + AssertSystemPropertiesEquality(expectedItem["data"]["system"].ToObject(), article.System); + Assert.NotNull(article); + Assert.Equal(expectedElementValues["title"]["value"].ToString(), article.Title); Assert.Equal(expectedItem["change_type"].ToString(), sync.SyncItems[i].ChangeType); Assert.Equal(DateTime.Parse(expectedItem["timestamp"].ToString()), DateTime.Parse(sync.SyncItems[i].Timestamp.ToString())); } } [Fact] - public async Task SyncApi_GetSyncAsync_GetSyncItems_WithoutTypeProvider() + public async Task SyncApi_GetSyncAsync_GetSyncItems_WithoutTypeProvider_ReturnsGenericData() { var mockedResponse = await File.ReadAllTextAsync(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}DeliveryClient{Path.DirectorySeparatorChar}sync.json")); @@ -1916,28 +1904,30 @@ public async Task SyncApi_GetSyncAsync_GetSyncItems_WithoutTypeProvider() for (int i = 0; i < expectedItems.Count; i++) { + var syncItemData = sync.SyncItems[i].Data; var expectedItem = expectedItems[i]; - var syncItem = (JObject)sync.SyncItems[i].Data; - - var expectedSystemValues = expectedItem["data"]["system"]; var expectedElementValues = expectedItem["data"]["elements"]; - var syncItemSystemValues = syncItem["system"]; - - Assert.Equal(expectedSystemValues["codename"].ToString(), syncItemSystemValues["codename"].ToString()); - Assert.Equal(expectedSystemValues["name"].ToString(), syncItemSystemValues["name"].ToString()); - Assert.Equal(expectedSystemValues["id"].ToString(), syncItemSystemValues["id"].ToString()); - Assert.Equal(expectedSystemValues["type"].ToString(), syncItemSystemValues["type"].ToString()); - Assert.Equal(expectedSystemValues["language"].ToString(), syncItemSystemValues["language"].ToString()); - Assert.Equal(expectedSystemValues["collection"].ToString(), syncItemSystemValues["collection"].ToString()); - Assert.Equal(expectedSystemValues["workflow_step"].ToString(), syncItemSystemValues["workflow_step"].ToString()); - - Assert.Equal(expectedElementValues["title"]["value"].ToString(), syncItem["elements"]["title"]["value"]); - + + AssertSystemPropertiesEquality(expectedItem["data"]["system"].ToObject(), sync.SyncItems[i].Data.System); + Assert.Null(sync.SyncItems[i].StronglyTypedData); + Assert.NotNull(syncItemData.Elements["title"]); + Assert.Equal(expectedElementValues["title"], syncItemData.Elements["title"]); Assert.Equal(expectedItem["change_type"].ToString(), sync.SyncItems[i].ChangeType); Assert.Equal(DateTime.Parse(expectedItem["timestamp"].ToString()), DateTime.Parse(sync.SyncItems[i].Timestamp.ToString())); } } + private void AssertSystemPropertiesEquality(JObject expectedSystemValues, IContentItemSystemAttributes system) + { + Assert.Equal(expectedSystemValues["codename"].ToString(), system.Codename.ToString()); + Assert.Equal(expectedSystemValues["name"].ToString(), system.Name.ToString()); + Assert.Equal(expectedSystemValues["id"].ToString(), system.Id.ToString()); + Assert.Equal(expectedSystemValues["type"].ToString(), system.Type.ToString()); + Assert.Equal(expectedSystemValues["language"].ToString(), system.Language.ToString()); + Assert.Equal(expectedSystemValues["collection"].ToString(), system.Collection.ToString()); + Assert.Equal(expectedSystemValues["workflow_step"].ToString(), system.WorkflowStep.ToString()); + } + private DeliveryClient InitializeDeliveryClientWithACustomTypeProvider(MockHttpMessageHandler handler) { diff --git a/Kontent.Ai.Delivery/DeliveryClient.cs b/Kontent.Ai.Delivery/DeliveryClient.cs index b4903fa0..9ee0a959 100644 --- a/Kontent.Ai.Delivery/DeliveryClient.cs +++ b/Kontent.Ai.Delivery/DeliveryClient.cs @@ -331,6 +331,7 @@ public async Task PostSyncInitAsync(IEnumerable>(Serializer); + return new DeliverySyncInitResponse(response, items.ToList()); } @@ -354,14 +355,11 @@ public async Task GetSyncAsync(string continuationToken) var itemModels = await Task.WhenAll(syncItems.Select(async syncItem => { // use TypeProvider from DI container to select a model - var mappedModel = await ModelProvider.GetContentItemModelAsync(syncItem.Data, new JObject()); - if (mappedModel == null) - { - // return JObject if no suitable model is found - return new SyncItem(syncItem.Data, syncItem.ChangeType, syncItem.Timestamp); - } - return new SyncItem(mappedModel, syncItem.ChangeType, syncItem.Timestamp); + var mappedModel = await ModelProvider.GetContentItemModelAsync(JToken.FromObject(syncItem.Data), new JObject()); + + return new SyncItem(mappedModel, syncItem.Data, syncItem.ChangeType, syncItem.Timestamp); })); + return new DeliverySyncResponse(response, itemModels); } diff --git a/Kontent.Ai.Delivery/Sync/SyncItem.cs b/Kontent.Ai.Delivery/Sync/SyncItem.cs index 589ec28d..4d4e6f13 100644 --- a/Kontent.Ai.Delivery/Sync/SyncItem.cs +++ b/Kontent.Ai.Delivery/Sync/SyncItem.cs @@ -1,17 +1,18 @@ using System; -using System.Collections.Generic; using Kontent.Ai.Delivery.Abstractions; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Kontent.Ai.Delivery.Sync; /// internal sealed class SyncItem : ISyncItem { + /// + public object StronglyTypedData { get; internal set; } + /// [JsonProperty("data")] - public object Data { get; internal set; } + public ISyncItemData Data { get; internal set; } /// [JsonProperty("change_type")] @@ -22,10 +23,12 @@ internal sealed class SyncItem : ISyncItem public DateTime Timestamp { get; internal set; } /// - /// Constructor used for deserialization (e.g. for caching purposes), contains no logic. + /// Initializes a new instance of class. /// [JsonConstructor] - public SyncItem(object data, string changeType, DateTime timestamp) { + public SyncItem(object stronglyTypedData, ISyncItemData data, string changeType, DateTime timestamp) + { + StronglyTypedData = stronglyTypedData; Data = data; ChangeType = changeType; Timestamp = timestamp; diff --git a/Kontent.Ai.Delivery/Sync/SyncItemData.cs b/Kontent.Ai.Delivery/Sync/SyncItemData.cs new file mode 100644 index 00000000..83193863 --- /dev/null +++ b/Kontent.Ai.Delivery/Sync/SyncItemData.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Kontent.Ai.Delivery.Abstractions; +using Newtonsoft.Json; + +namespace Kontent.Ai.Delivery.Sync +{ + internal sealed class SyncItemData : ISyncItemData + { + /// + [JsonProperty("system")] + public IContentItemSystemAttributes System { get; internal set; } + + /// + [JsonProperty("elements")] + public Dictionary Elements { get; internal set; } + + /// + /// Constructor used for deserialization. Contains no logic. + /// + [JsonConstructor] + public SyncItemData() + { + } + } +} \ No newline at end of file