diff --git a/Kontent.Ai.Delivery.Abstractions/DeliveryClientExtensions.cs b/Kontent.Ai.Delivery.Abstractions/DeliveryClientExtensions.cs index 4c892041..ab38377b 100644 --- a/Kontent.Ai.Delivery.Abstractions/DeliveryClientExtensions.cs +++ b/Kontent.Ai.Delivery.Abstractions/DeliveryClientExtensions.cs @@ -76,5 +76,16 @@ public static Task GetLanguagesAsync(this IDel { return client.GetLanguagesAsync(parameters); } + + /// + /// Initializes synchronization of changes in content items based on the specified parameters. After the initialization, you'll get an X-Continuation token in the response. + /// + /// An instance of the + /// A collection of query parameters, for example, for filtering. + /// The instance that represents the sync init response that contains continuation token needed for further sync execution. + public static Task PostSyncInitAsync(this IDeliveryClient client, params IQueryParameter[] parameters) + { + return client.PostSyncInitAsync(parameters); + } } } diff --git a/Kontent.Ai.Delivery.Abstractions/IDeliveryClient.cs b/Kontent.Ai.Delivery.Abstractions/IDeliveryClient.cs index e425e03e..92981d4a 100644 --- a/Kontent.Ai.Delivery.Abstractions/IDeliveryClient.cs +++ b/Kontent.Ai.Delivery.Abstractions/IDeliveryClient.cs @@ -92,5 +92,18 @@ public interface IDeliveryClient /// A collection of query parameters, for example, for paging. /// The instance that represents the languages. If no query parameters are specified, all languages are returned. Task GetLanguagesAsync(IEnumerable parameters = null); + + /// + /// Initializes synchronization of changes in content items based on the specified parameters. After the initialization, you'll get an X-Continuation token in the response. + /// + /// A collection of query parameters, for example, for filtering. + /// The instance that represents the sync init response that contains continuation token needed for further sync execution. + Task PostSyncInitAsync(IEnumerable parameters = null); + + /// + /// Retrieve a list of delta updates to recently changed content items in the specified project. The types of items you get is determined by the X-Continuation token you use. + /// + /// The instance that represents the sync response that contains collection of delta updates and continuation token needed for further sync execution. + Task GetSyncAsync(string continuationToken); } } \ No newline at end of file diff --git a/Kontent.Ai.Delivery.Abstractions/Sync/IDeliverySyncInitResponse.cs b/Kontent.Ai.Delivery.Abstractions/Sync/IDeliverySyncInitResponse.cs new file mode 100644 index 00000000..709aac58 --- /dev/null +++ b/Kontent.Ai.Delivery.Abstractions/Sync/IDeliverySyncInitResponse.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Kontent.Ai.Delivery.Abstractions; + +/// +/// Represents a response from Kontent.ai Delivery API that contains a continuation token and . +/// +public interface IDeliverySyncInitResponse : IResponse +{ + /// + /// Gets list of delta update items. + /// + IList SyncItems { get; } +} \ No newline at end of file diff --git a/Kontent.Ai.Delivery.Abstractions/Sync/IDeliverySyncResponse.cs b/Kontent.Ai.Delivery.Abstractions/Sync/IDeliverySyncResponse.cs new file mode 100644 index 00000000..e5901236 --- /dev/null +++ b/Kontent.Ai.Delivery.Abstractions/Sync/IDeliverySyncResponse.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Kontent.Ai.Delivery.Abstractions; + +/// +/// Represents a response from Kontent.ai Delivery API that contains a taxonomy group. +/// +public interface IDeliverySyncResponse : IResponse +{ + /// + /// Gets list of delta update items. + /// + IList SyncItems { get; } +} \ No newline at end of file diff --git a/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItem.cs b/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItem.cs new file mode 100644 index 00000000..091c52b4 --- /dev/null +++ b/Kontent.Ai.Delivery.Abstractions/Sync/ISyncItem.cs @@ -0,0 +1,44 @@ +using System; + +namespace Kontent.Ai.Delivery.Abstractions; + +/// +/// Represents a delta update. +/// +public interface ISyncItem +{ + /// + /// Gets the content item's codename. + /// + string Codename { get; } + + /// + /// Gets the content item's internal ID. + /// + Guid Id { get; } + + /// + /// Gets the content item's type codename. + /// + string Type { get; } + + /// + /// Gets the codename of the language that the content is in. + /// + string Language { get; } + + /// + /// Gets the content item's collection codename. For projects without collections enabled, the value is default. + /// + string Collection { get; } + + /// + /// Gets the information whether the content item was modified or deleted since the last synchronization. + /// + string ChangeType { get; } + + /// + /// Gets the ISO-8601 formatted date and time in UTC of the last change to the content item. The timestamp identifies when the change occurred in Delivery API. + /// + DateTime Timestamp { get; } +} \ No newline at end of file diff --git a/Kontent.Ai.Delivery.Caching/DeliveryClientCache.cs b/Kontent.Ai.Delivery.Caching/DeliveryClientCache.cs index 9e773008..5ef354a2 100644 --- a/Kontent.Ai.Delivery.Caching/DeliveryClientCache.cs +++ b/Kontent.Ai.Delivery.Caching/DeliveryClientCache.cs @@ -186,6 +186,22 @@ public async Task GetUniversalItemsAsync( () => _deliveryClient.GetUniversalItemsAsync(queryParameters), response => response.Items.Any(), CacheHelpers.GetItemsDependencies); + /// Initializes synchronization of changes in content items based on the specified parameters. After the initialization, you'll get an X-Continuation token in the response. + /// + /// A collection of query parameters, for example, for filtering. + /// The instance that represents the sync init response that contains continuation token needed for further sync execution. + public Task PostSyncInitAsync(IEnumerable parameters = null) + { + return _deliveryClient.PostSyncInitAsync(parameters); + } + + /// + /// Retrieve a list of delta updates to recently changed content items in the specified project. The types of items you get is determined by the X-Continuation token you use. + /// + /// The instance that represents the sync response that contains collection of delta updates and continuation token needed for further sync execution. + public Task GetSyncAsync(string continuationToken) + { + return _deliveryClient.GetSyncAsync(continuationToken); } } } diff --git a/Kontent.Ai.Delivery.Tests/DeliveryClientTests.cs b/Kontent.Ai.Delivery.Tests/DeliveryClientTests.cs index 64224569..f95b9de8 100644 --- a/Kontent.Ai.Delivery.Tests/DeliveryClientTests.cs +++ b/Kontent.Ai.Delivery.Tests/DeliveryClientTests.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using System.Web; using AngleSharp.Html.Parser; using FakeItEasy; using Kontent.Ai.Delivery.Abstractions; @@ -13,6 +15,7 @@ using Kontent.Ai.Delivery.ContentItems.Elements; 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; @@ -1977,6 +1980,88 @@ public async Task RetrieveContentItem_GetLinkedItems_TypeItemsManually() Assert.Equal("2Ao5b6uqI40", (hostedVideoItem as HostedVideo).VideoId); } + [Fact] + public async Task SyncApi_PostSyncInitAsync_GetContinuationToken() + { + var mockedResponse = await File.ReadAllTextAsync(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}DeliveryClient{Path.DirectorySeparatorChar}sync_init.json")); + + _mockHttp + .When($"{_baseUrl}/sync/init") + .Respond(new[] { new KeyValuePair("X-Continuation", "token"), }, "application/json", mockedResponse); + + var client = InitializeDeliveryClientWithCustomModelProvider(_mockHttp); + + var syncInit = await client.PostSyncInitAsync(); + + Assert.NotNull(syncInit.ApiResponse.ContinuationToken); + Assert.Empty(syncInit.SyncItems); + } + + [Fact] + public async Task SyncApi_PostSyncInitAsync_WithParameters_GetContinuationToken() + { + var mockedResponse = await File.ReadAllTextAsync(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}DeliveryClient{Path.DirectorySeparatorChar}sync_init.json")); + + _mockHttp + .When($"{_baseUrl}/sync/init") + .Respond(new[] { new KeyValuePair("X-Continuation", "token"), }, "application/json", mockedResponse); + + var client = InitializeDeliveryClientWithCustomModelProvider(_mockHttp); + + var syncInit = await client.PostSyncInitAsync( + new LanguageParameter("cs"), + new EqualsFilter("system.type", "article"), + new NotEqualsFilter("system.collection", "default")); + + var requestUri = new Uri(syncInit.ApiResponse.RequestUrl); + + var requestQuery = HttpUtility.ParseQueryString(requestUri.Query); + + Assert.Equal(3, requestQuery.Count); + Assert.Equal("language", requestQuery.Keys[0]); + Assert.Equal("system.type[eq]", requestQuery.Keys[1]); + Assert.Equal("system.collection[neq]", requestQuery.Keys[2]); + Assert.NotNull(syncInit.ApiResponse.ContinuationToken); + Assert.Empty(syncInit.SyncItems); + } + + [Fact] + public async Task SyncApi_GetSyncAsync_GetSyncItems() + { + var mockedResponse = await File.ReadAllTextAsync(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}DeliveryClient{Path.DirectorySeparatorChar}sync.json")); + + var expectedValue = JObject.Parse(mockedResponse).SelectToken("items").ToObject>(); + + _mockHttp + .When($"{_baseUrl}/sync") + .WithHeaders("X-Continuation", "token") + .Respond(new[] { new KeyValuePair("X-Continuation", "token"), }, "application/json", mockedResponse); + + var client = InitializeDeliveryClientWithCustomModelProvider(_mockHttp); + + var sync = await client.GetSyncAsync("token"); + + Assert.NotNull(sync.ApiResponse.ContinuationToken); + + Assert.Equal(2, sync.SyncItems.Count); + + Assert.Equal(expectedValue[0].Codename, sync.SyncItems[0].Codename); + Assert.Equal(expectedValue[0].Id, sync.SyncItems[0].Id); + Assert.Equal(expectedValue[0].Type, sync.SyncItems[0].Type); + Assert.Equal(expectedValue[0].Language, sync.SyncItems[0].Language); + Assert.Equal(expectedValue[0].Collection, sync.SyncItems[0].Collection); + Assert.Equal(expectedValue[0].ChangeType, sync.SyncItems[0].ChangeType); + Assert.Equal(expectedValue[0].Timestamp, sync.SyncItems[0].Timestamp); + + Assert.Equal(expectedValue[1].Codename, sync.SyncItems[1].Codename); + Assert.Equal(expectedValue[1].Id, sync.SyncItems[1].Id); + Assert.Equal(expectedValue[1].Type, sync.SyncItems[1].Type); + Assert.Equal(expectedValue[1].Language, sync.SyncItems[1].Language); + Assert.Equal(expectedValue[1].Collection, sync.SyncItems[1].Collection); + Assert.Equal(expectedValue[1].ChangeType, sync.SyncItems[1].ChangeType); + Assert.Equal(expectedValue[1].Timestamp, sync.SyncItems[1].Timestamp); + } + private DeliveryClient InitializeDeliveryClientWithACustomTypeProvider(MockHttpMessageHandler handler) { var customTypeProvider = new CustomTypeProvider(); diff --git a/Kontent.Ai.Delivery.Tests/Fixtures/DeliveryClient/sync.json b/Kontent.Ai.Delivery.Tests/Fixtures/DeliveryClient/sync.json new file mode 100644 index 00000000..1b377b27 --- /dev/null +++ b/Kontent.Ai.Delivery.Tests/Fixtures/DeliveryClient/sync.json @@ -0,0 +1,22 @@ +{ + "items": [ + { + "codename": "hello_world", + "id": "7adfb82a-1386-4228-bcc2-45073a0355f6", + "type": "article", + "language": "default", + "collection": "default", + "change_type": "changed", + "timestamp": "2022-10-06T08:38:40.0088127Z" + }, + { + "codename": "bye__world", + "id": "42a3cfbd-4967-43e7-987b-e1e69c483e26", + "type": "article", + "language": "default", + "collection": "default", + "change_type": "deleted", + "timestamp": "2022-10-06T08:38:47.3613558Z" + } + ] +} \ No newline at end of file diff --git a/Kontent.Ai.Delivery.Tests/Fixtures/DeliveryClient/sync_init.json b/Kontent.Ai.Delivery.Tests/Fixtures/DeliveryClient/sync_init.json new file mode 100644 index 00000000..3b9e4745 --- /dev/null +++ b/Kontent.Ai.Delivery.Tests/Fixtures/DeliveryClient/sync_init.json @@ -0,0 +1,3 @@ +{ + "items": [] +} \ No newline at end of file diff --git a/Kontent.Ai.Delivery.Tests/Kontent.Ai.Delivery.Tests.csproj b/Kontent.Ai.Delivery.Tests/Kontent.Ai.Delivery.Tests.csproj index 7fcae3fa..08841329 100644 --- a/Kontent.Ai.Delivery.Tests/Kontent.Ai.Delivery.Tests.csproj +++ b/Kontent.Ai.Delivery.Tests/Kontent.Ai.Delivery.Tests.csproj @@ -150,6 +150,12 @@ Always + + Always + + + Always + diff --git a/Kontent.Ai.Delivery/DeliveryClient.cs b/Kontent.Ai.Delivery/DeliveryClient.cs index 57b99df1..ae571770 100644 --- a/Kontent.Ai.Delivery/DeliveryClient.cs +++ b/Kontent.Ai.Delivery/DeliveryClient.cs @@ -12,6 +12,7 @@ using Kontent.Ai.Delivery.Extensions; using Kontent.Ai.Delivery.Languages; using Kontent.Ai.Delivery.SharedModels; +using Kontent.Ai.Delivery.Sync; using Kontent.Ai.Delivery.TaxonomyGroups; using Kontent.Ai.Urls.Delivery; using Kontent.Ai.Urls.Delivery.QueryParameters; @@ -89,8 +90,8 @@ public async Task> GetItemAsync(string codename, IEn } var endpointUrl = UrlBuilder.GetItemUrl(codename, parameters); - var response = await GetDeliveryResponseAsync(endpointUrl); - + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get); + if (!response.IsSuccess) { return new DeliveryItemResponse(response); @@ -111,8 +112,9 @@ public async Task> GetItemsAsync(IEnumerable< { var enhancedParameters = EnsureContentTypeFilter(parameters).ToList(); var endpointUrl = UrlBuilder.GetItemsUrl(enhancedParameters); - var response = await GetDeliveryResponseAsync(endpointUrl); + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get); + if (!response.IsSuccess) { return new DeliveryItemListingResponse(response); @@ -140,8 +142,8 @@ public IDeliveryItemsFeed GetItemsFeed(IEnumerable parame async Task> GetItemsBatchAsync(string continuationToken) { - var response = await GetDeliveryResponseAsync(endpointUrl, continuationToken); - + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get, continuationToken); + if (!response.IsSuccess) { return new DeliveryItemsFeedResponse(response); @@ -173,7 +175,7 @@ public async Task GetTypeAsync(string codename) } var endpointUrl = UrlBuilder.GetTypeUrl(codename); - var response = await GetDeliveryResponseAsync(endpointUrl); + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get); if (!response.IsSuccess) { @@ -193,7 +195,7 @@ public async Task GetTypeAsync(string codename) public async Task GetTypesAsync(IEnumerable parameters = null) { var endpointUrl = UrlBuilder.GetTypesUrl(parameters); - var response = await GetDeliveryResponseAsync(endpointUrl); + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get); if (!response.IsSuccess) { @@ -235,7 +237,7 @@ public async Task GetContentElementAsync(string conten } var endpointUrl = UrlBuilder.GetContentElementUrl(contentTypeCodename, contentElementCodename); - var response = await GetDeliveryResponseAsync(endpointUrl); + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get); if (!response.IsSuccess) { @@ -265,7 +267,7 @@ public async Task GetTaxonomyAsync(string codename) } var endpointUrl = UrlBuilder.GetTaxonomyUrl(codename); - var response = await GetDeliveryResponseAsync(endpointUrl); + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get); if (!response.IsSuccess) { @@ -284,8 +286,8 @@ public async Task GetTaxonomyAsync(string codename) public async Task GetTaxonomiesAsync(IEnumerable parameters = null) { var endpointUrl = UrlBuilder.GetTaxonomiesUrl(parameters); - var response = await GetDeliveryResponseAsync(endpointUrl); - + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get); + if (!response.IsSuccess) { return new DeliveryTaxonomyListingResponse(response); @@ -305,7 +307,8 @@ public async Task GetTaxonomiesAsync(IEnumerab public async Task GetLanguagesAsync(IEnumerable parameters = null) { var endpointUrl = UrlBuilder.GetLanguagesUrl(parameters); - var response = await GetDeliveryResponseAsync(endpointUrl); + + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get); if (!response.IsSuccess) { @@ -318,7 +321,6 @@ public async Task GetLanguagesAsync(IEnumerabl return new DeliveryLanguageListingResponse(response, languages.ToList(), pagination); } - public async Task GetUniversalItemAsync(string codename, IEnumerable parameters = null) { if (string.IsNullOrEmpty(codename)) @@ -386,7 +388,37 @@ public async Task GetUniversalItemsAsync( ); } - private async Task GetDeliveryResponseAsync(string endpointUrl, string continuationToken = null) + public async Task PostSyncInitAsync(IEnumerable parameters = null) + { + var endpointUrl = UrlBuilder.GetSyncInitUrl(parameters); + var response = await GetDeliveryResponseAsync(endpointUrl, httpMethod: HttpMethod.Post); + + if (!response.IsSuccess) + { + return new DeliverySyncInitResponse(response); + } + + var content = await response.GetJsonContentAsync(); + var items = content["items"].ToObject>(Serializer); + return new DeliverySyncInitResponse(response, items.ToList()); + } + + public async Task GetSyncAsync(string continuationToken) + { + var endpointUrl = UrlBuilder.GetSyncUrl(); + var response = await GetDeliveryResponseAsync(endpointUrl, HttpMethod.Get, continuationToken); + + if (!response.IsSuccess) + { + return new DeliverySyncResponse(response); + } + + var content = await response.GetJsonContentAsync(); + var items = content["items"].ToObject>(Serializer); + return new DeliverySyncResponse(response, items.ToList()); + } + + private async Task GetDeliveryResponseAsync(string endpointUrl, HttpMethod httpMethod, string continuationToken = null) { if (DeliveryOptions.CurrentValue.UsePreviewApi && DeliveryOptions.CurrentValue.UseSecureAccess) { @@ -398,18 +430,18 @@ private async Task GetDeliveryResponseAsync(string endpointUrl, str var retryPolicy = RetryPolicyProvider.GetRetryPolicy(); if (retryPolicy != null) { - var response = await retryPolicy.ExecuteAsync(() => SendHttpMessageAsync(endpointUrl, continuationToken)); + var response = await retryPolicy.ExecuteAsync(() => SendHttpMessageAsync(endpointUrl, httpMethod, continuationToken)); return await GetResponseContentAsync(response, endpointUrl); } } // Omit using the resilience logic completely. - return await GetResponseContentAsync(await SendHttpMessageAsync(endpointUrl, continuationToken), endpointUrl); + return await GetResponseContentAsync(await SendHttpMessageAsync(endpointUrl, httpMethod, continuationToken), endpointUrl); } - private Task SendHttpMessageAsync(string endpointUrl, string continuationToken = null) + private Task SendHttpMessageAsync(string endpointUrl, HttpMethod httpMethod, string continuationToken = null) { - var message = new HttpRequestMessage(HttpMethod.Get, endpointUrl); + var message = new HttpRequestMessage(httpMethod, endpointUrl); if (DeliveryOptions.CurrentValue.WaitForLoadingNewContent) { diff --git a/Kontent.Ai.Delivery/Sync/DeliverySyncInitResponse.cs b/Kontent.Ai.Delivery/Sync/DeliverySyncInitResponse.cs new file mode 100644 index 00000000..41d325e2 --- /dev/null +++ b/Kontent.Ai.Delivery/Sync/DeliverySyncInitResponse.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Kontent.Ai.Delivery.Abstractions; +using Kontent.Ai.Delivery.SharedModels; +using Newtonsoft.Json; + +namespace Kontent.Ai.Delivery.Sync; + +internal sealed class DeliverySyncInitResponse : AbstractResponse, IDeliverySyncInitResponse +{ + /// + public IList SyncItems { get; } + + /// + /// Initializes a new instance of the + /// + /// + /// + [JsonConstructor] + public DeliverySyncInitResponse(IApiResponse response, IList syncItems) + : base(response) + { + SyncItems = syncItems; + } + + public DeliverySyncInitResponse(IApiResponse response) : base(response) + { + } +} \ No newline at end of file diff --git a/Kontent.Ai.Delivery/Sync/DeliverySyncResponse.cs b/Kontent.Ai.Delivery/Sync/DeliverySyncResponse.cs new file mode 100644 index 00000000..203dde69 --- /dev/null +++ b/Kontent.Ai.Delivery/Sync/DeliverySyncResponse.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Kontent.Ai.Delivery.Abstractions; +using Newtonsoft.Json; +using Kontent.Ai.Delivery.SharedModels; + +namespace Kontent.Ai.Delivery.Sync; + +internal sealed class DeliverySyncResponse : AbstractResponse, IDeliverySyncResponse +{ + /// + public IList SyncItems { get; } + + /// + /// Initializes a new instance of the + /// + /// + /// + [JsonConstructor] + internal DeliverySyncResponse(IApiResponse response, IList syncItems) + : base(response) + { + SyncItems = syncItems; + } + + internal DeliverySyncResponse(IApiResponse response) + : base(response) + { + } +} \ No newline at end of file diff --git a/Kontent.Ai.Delivery/Sync/SyncItem.cs b/Kontent.Ai.Delivery/Sync/SyncItem.cs new file mode 100644 index 00000000..319010bb --- /dev/null +++ b/Kontent.Ai.Delivery/Sync/SyncItem.cs @@ -0,0 +1,45 @@ +using System; +using Kontent.Ai.Delivery.Abstractions; +using Newtonsoft.Json; + +namespace Kontent.Ai.Delivery.Sync; + +/// +public class SyncItem : ISyncItem +{ + /// + [JsonProperty("codename")] + public string Codename { get; internal set; } + + /// + [JsonProperty("id")] + public Guid Id { get; internal set; } + + /// + [JsonProperty("type")] + public string Type { get; internal set; } + + /// + [JsonProperty("language")] + public string Language { get; internal set; } + + /// + [JsonProperty("collection")] + public string Collection { get; internal set; } + + /// + [JsonProperty("change_type")] + public string ChangeType { get; internal set; } + + /// + [JsonProperty("timestamp")] + public DateTime Timestamp { get; internal set; } + + /// + /// Constructor used for deserialization (e.g. for caching purposes), contains no logic. + /// + [JsonConstructor] + public SyncItem() + { + } +} \ No newline at end of file diff --git a/Kontent.Ai.Urls/Delivery/DeliveryEndpointUrlBuilder.cs b/Kontent.Ai.Urls/Delivery/DeliveryEndpointUrlBuilder.cs index 2a6fdf47..7182e732 100644 --- a/Kontent.Ai.Urls/Delivery/DeliveryEndpointUrlBuilder.cs +++ b/Kontent.Ai.Urls/Delivery/DeliveryEndpointUrlBuilder.cs @@ -22,6 +22,8 @@ public class DeliveryEndpointUrlBuilder private const string UrlTemplateTaxonomy = "/taxonomies/{0}"; private const string UrlTemplateTaxonomies = "/taxonomies"; private const string UrlTemplateLanguages = "/languages"; + private const string UrlTemplateSyncInit = "/sync/init"; + private const string UrlTemplateSync = "/sync"; private readonly IOptionsMonitor _deliveryOptionsMonitor; private DeliveryOptions deliveryOptions; @@ -142,6 +144,25 @@ public string GetLanguagesUrl(IEnumerable parameters) return GetUrl(UrlTemplateLanguages, parameters); } + /// + /// Generates an URL for sync initialization. + /// + /// Filtering parameters. + /// A valid URL containing correctly formatted parameters. + public string GetSyncInitUrl(IEnumerable parameters) + { + return GetUrl(UrlTemplateSyncInit, parameters); + } + + /// + /// Generates an URL for sync execution. + /// + /// A valid URL containing correctly formatted parameters. + public string GetSyncUrl() + { + return GetUrl(UrlTemplateSync); + } + private string GetUrl(string path, IEnumerable parameters) { if (parameters != null)