From f87a4df02bf271e74f224ead86729edccd911153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Z=C3=A1hora?= Date: Thu, 4 Jul 2024 14:03:23 +0200 Subject: [PATCH 1/7] EN-238 Add WebSpotlight support via Management Api --- .../ActivationWebSpotlightResponse.json | 4 + ...otlightWithProvidedRootTypeIdResponse.json | 4 + .../DeactivationWebSpotlightResponse.json | 4 + .../GetStatusWebSpotlightResponse.json | 4 + .../Kontent.Ai.Management.Tests.csproj | 9 ++ .../WebSpotlightTests.cs | 88 +++++++++++++++++++ .../Modules/UrlBuilder/WebSpotlightTests.cs | 15 ++++ Kontent.Ai.Management/IManagementClient.cs | 26 ++++++ .../ManagementClient.WebSpotlight.cs | 42 +++++++++ .../WebSpotlight/WebSpotlightActivateModel.cs | 16 ++++ .../Models/WebSpotlight/WebSpotlightModel.cs | 22 +++++ .../Modules/UrlBuilder/EndpointUrlBuilder.cs | 4 + .../Templates/WebSpotlightTemplate.cs | 11 +++ 13 files changed, 249 insertions(+) create mode 100644 Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightResponse.json create mode 100644 Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightWithProvidedRootTypeIdResponse.json create mode 100644 Kontent.Ai.Management.Tests/Data/WebSpotlight/DeactivationWebSpotlightResponse.json create mode 100644 Kontent.Ai.Management.Tests/Data/WebSpotlight/GetStatusWebSpotlightResponse.json create mode 100644 Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs create mode 100644 Kontent.Ai.Management.Tests/Modules/UrlBuilder/WebSpotlightTests.cs create mode 100644 Kontent.Ai.Management/ManagementClient.WebSpotlight.cs create mode 100644 Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightActivateModel.cs create mode 100644 Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs create mode 100644 Kontent.Ai.Management/Modules/UrlBuilder/Templates/WebSpotlightTemplate.cs diff --git a/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightResponse.json b/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightResponse.json new file mode 100644 index 00000000..d9a55ca3 --- /dev/null +++ b/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightResponse.json @@ -0,0 +1,4 @@ +{ + "enabled": true, + "root_type_id": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightWithProvidedRootTypeIdResponse.json b/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightWithProvidedRootTypeIdResponse.json new file mode 100644 index 00000000..21214617 --- /dev/null +++ b/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightWithProvidedRootTypeIdResponse.json @@ -0,0 +1,4 @@ +{ + "enabled": true, + "root_type_id": "3660e894-bae8-4dcd-9d3e-5fc9205c2ece" +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Data/WebSpotlight/DeactivationWebSpotlightResponse.json b/Kontent.Ai.Management.Tests/Data/WebSpotlight/DeactivationWebSpotlightResponse.json new file mode 100644 index 00000000..e4fb263e --- /dev/null +++ b/Kontent.Ai.Management.Tests/Data/WebSpotlight/DeactivationWebSpotlightResponse.json @@ -0,0 +1,4 @@ +{ + "enabled": false, + "root_type_id": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Data/WebSpotlight/GetStatusWebSpotlightResponse.json b/Kontent.Ai.Management.Tests/Data/WebSpotlight/GetStatusWebSpotlightResponse.json new file mode 100644 index 00000000..d9a55ca3 --- /dev/null +++ b/Kontent.Ai.Management.Tests/Data/WebSpotlight/GetStatusWebSpotlightResponse.json @@ -0,0 +1,4 @@ +{ + "enabled": true, + "root_type_id": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Kontent.Ai.Management.Tests.csproj b/Kontent.Ai.Management.Tests/Kontent.Ai.Management.Tests.csproj index 5b1b1cda..7c20c8c1 100644 --- a/Kontent.Ai.Management.Tests/Kontent.Ai.Management.Tests.csproj +++ b/Kontent.Ai.Management.Tests/Kontent.Ai.Management.Tests.csproj @@ -63,6 +63,15 @@ Always + + Always + + + Always + + + Always + diff --git a/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs b/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs new file mode 100644 index 00000000..0aee3751 --- /dev/null +++ b/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs @@ -0,0 +1,88 @@ +using Kontent.Ai.Management.Models.WebSpotlight; +using Kontent.Ai.Management.Tests.Base; +using System; +using System.Net.Http; +using Xunit; +using static Kontent.Ai.Management.Tests.Base.Scenario; + +namespace Kontent.Ai.Management.Tests.ManagementClientTests; + +public class WebSpotlightTests : IClassFixture +{ + private static readonly string WebSpotlightBaseUrl = $"{Endpoint}/projects/{ENVIRONMENT_ID}/web-spotlight"; + private readonly Scenario _scenario = new(folder: "WebSpotlight"); + + [Fact] + public async void ActivateWebSpotlight_Returns_EnabledStatusAndRootTypeId() + { + var client = _scenario + .WithResponses("ActivationWebSpotlightResponse.json") + .CreateManagementClient(); + + var response = await client + .ActivateWebSpotlightAsync(); + + _scenario + .CreateExpectations() + .HttpMethod(HttpMethod.Put) + .Response(response) + .Url(WebSpotlightBaseUrl) + .Validate(); + } + + [Fact] + public async void ActivateWebSpotlight_WithProvidedValidRootType_Returns_EnabledStatusAndRootTypeId() + { + var client = _scenario + .WithResponses("ActivationWebSpotlightWithProvidedRootTypeIdResponse.json") + .CreateManagementClient(); + + var rootTypeId = Guid.Parse("3660e894-bae8-4dcd-9d3e-5fc9205c2ece"); + var webSpotlightActivateModel = new WebSpotlightActivateModel { RootTypeId = rootTypeId }; + + await client.ActivateWebSpotlightAsync(webSpotlightActivateModel); + + _scenario + .CreateExpectations() + .HttpMethod(HttpMethod.Put) + .RequestPayload(webSpotlightActivateModel) + .Url(WebSpotlightBaseUrl) + .Validate(); + } + + [Fact] + public async void DeactivateWebSpotlight_Returns_DisabledStatusAndRootTypeId() + { + var client = _scenario + .WithResponses("DeactivationWebSpotlightResponse.json") + .CreateManagementClient(); + + var response = await client + .DeactivateWebSpotlightAsync(); + + _scenario + .CreateExpectations() + .HttpMethod(HttpMethod.Put) + .Response(response) + .Url(WebSpotlightBaseUrl) + .Validate(); + } + + [Fact] + public async void GetWebSpotlightStatus_Returns_StatusAndRootTypeId() + { + var client = _scenario + .WithResponses("GetStatusWebSpotlightResponse.json") + .CreateManagementClient(); + + var response = await client + .GetWebSpotlightStatusAsync(); + + _scenario + .CreateExpectations() + .HttpMethod(HttpMethod.Get) + .Response(response) + .Url(WebSpotlightBaseUrl) + .Validate(); + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Modules/UrlBuilder/WebSpotlightTests.cs b/Kontent.Ai.Management.Tests/Modules/UrlBuilder/WebSpotlightTests.cs new file mode 100644 index 00000000..682391ea --- /dev/null +++ b/Kontent.Ai.Management.Tests/Modules/UrlBuilder/WebSpotlightTests.cs @@ -0,0 +1,15 @@ +using Xunit; + +namespace Kontent.Ai.Management.Tests.Modules.UrlBuilder; + +public partial class EndpointUrlBuilderTests +{ + [Fact] + public void BuildWebSpotlightUrl_ReturnsWebSpotlightUrl() + { + var expectedUrl = $"{ENDPOINT}/projects/{ENVIRONMENT_ID}/web-spotlight"; + var actualUrl = _builder.BuildWebSpotlightUrl(); + + Assert.Equal(expectedUrl, actualUrl); + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/IManagementClient.cs b/Kontent.Ai.Management/IManagementClient.cs index 3077f416..2dc61057 100644 --- a/Kontent.Ai.Management/IManagementClient.cs +++ b/Kontent.Ai.Management/IManagementClient.cs @@ -28,6 +28,7 @@ using Kontent.Ai.Management.Models.TypeSnippets.Patch; using Kontent.Ai.Management.Models.Users; using Kontent.Ai.Management.Models.Webhooks; +using Kontent.Ai.Management.Models.WebSpotlight; using Kontent.Ai.Management.Models.Workflow; using System; using System.Collections.Generic; @@ -803,4 +804,29 @@ public interface IManagementClient /// Represents configuration that will be used for project. /// The instance that represents the preview configuration. Task ModifyPreviewConfigurationAsync(PreviewConfigurationModel previewConfiguration); + + /// + /// Activates the web spotlight status with possibility to provide existing Root Type ID. + /// + /// Represents configuration that will be used for web spotlight activation. + /// The instance that represents the web spotlight status. + Task ActivateWebSpotlightAsync(WebSpotlightActivateModel webSpotlightActivateModel); + + /// + /// Activates the web spotlight status. + /// + /// The instance that represents the web spotlight status. + Task ActivateWebSpotlightAsync(); + + /// + /// Deactivates the web spotlight. + /// + /// The instance that represents the web spotlight status. + Task DeactivateWebSpotlightAsync(); + + /// + /// Returns the web spotlight status. + /// + /// The instance that represents the web spotlight status. + Task GetWebSpotlightStatusAsync(); } diff --git a/Kontent.Ai.Management/ManagementClient.WebSpotlight.cs b/Kontent.Ai.Management/ManagementClient.WebSpotlight.cs new file mode 100644 index 00000000..a2990a98 --- /dev/null +++ b/Kontent.Ai.Management/ManagementClient.WebSpotlight.cs @@ -0,0 +1,42 @@ +using Kontent.Ai.Management.Models.WebSpotlight; +using System; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Kontent.Ai.Management; + +/// +/// Executes requests against the Kontent.ai Management API. +/// +public partial class ManagementClient +{ + /// + public async Task ActivateWebSpotlightAsync(WebSpotlightActivateModel webSpotlightActivateModel) + { + ArgumentNullException.ThrowIfNull(webSpotlightActivateModel); + + var endpointUrl = _urlBuilder.BuildWebSpotlightUrl(); + return await _actionInvoker.InvokeMethodAsync(endpointUrl, HttpMethod.Put, webSpotlightActivateModel); + } + + /// + public async Task ActivateWebSpotlightAsync() + { + var endpointUrl = _urlBuilder.BuildWebSpotlightUrl(); + return await _actionInvoker.InvokeReadOnlyMethodAsync(endpointUrl, HttpMethod.Put); + } + + /// + public async Task DeactivateWebSpotlightAsync() + { + var endpointUrl = _urlBuilder.BuildWebSpotlightUrl(); + return await _actionInvoker.InvokeReadOnlyMethodAsync(endpointUrl, HttpMethod.Put); + } + + /// + public async Task GetWebSpotlightStatusAsync() + { + var endpointUrl = _urlBuilder.BuildWebSpotlightUrl(); + return await _actionInvoker.InvokeReadOnlyMethodAsync(endpointUrl, HttpMethod.Get); + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightActivateModel.cs b/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightActivateModel.cs new file mode 100644 index 00000000..93678c5d --- /dev/null +++ b/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightActivateModel.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; +using System; + +namespace Kontent.Ai.Management.Models.WebSpotlight; + +/// +/// Represents the web spotlight activation model. +/// +public class WebSpotlightActivateModel +{ + /// + /// Gets or sets the web spotlight's Root Type ID. + /// + [JsonProperty("root_type_id")] + public Guid? RootTypeId { get; set; } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs b/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs new file mode 100644 index 00000000..3ff0c22b --- /dev/null +++ b/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; +using System; + +namespace Kontent.Ai.Management.Models.WebSpotlight; + +/// +/// Represents the web spotlight model. +/// +public class WebSpotlightModel +{ + /// + /// Gets or sets the web spotlight's Enabled. + /// + [JsonProperty("enabled")] + public bool Enabled { get; set; } + + /// + /// Gets or sets the web spotlight's Root Type ID. + /// + [JsonProperty("root_type_id")] + public Guid? RootTypeId { get; set; } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Modules/UrlBuilder/EndpointUrlBuilder.cs b/Kontent.Ai.Management/Modules/UrlBuilder/EndpointUrlBuilder.cs index f5b6787f..063ea0fc 100644 --- a/Kontent.Ai.Management/Modules/UrlBuilder/EndpointUrlBuilder.cs +++ b/Kontent.Ai.Management/Modules/UrlBuilder/EndpointUrlBuilder.cs @@ -31,6 +31,7 @@ internal sealed class EndpointUrlBuilder private readonly ItemTemplate _itemTemplate; private readonly EnvironmentRolesTemplate _environmentRolesTemplate; private readonly UserTemplate _userTemplate; + private readonly WebSpotlightTemplate _webSpotlightTemplate; private readonly ManagementOptions _options; @@ -52,6 +53,7 @@ public EndpointUrlBuilder(ManagementOptions options) _itemTemplate = new ItemTemplate(); _environmentRolesTemplate = new EnvironmentRolesTemplate(); _userTemplate = new UserTemplate(); + _webSpotlightTemplate = new WebSpotlightTemplate(); _options = options; } @@ -223,6 +225,8 @@ public string BuildSubscriptionUserDeactivateDisableUrl(UserIdentifier identifie public string BuildMarkEnvironmentAsProductionUrl() => GetEnvironmentUrl("/mark-environment-as-production"); + public string BuildWebSpotlightUrl() => GetEnvironmentUrl(_webSpotlightTemplate.Url); + private string GetEnvironmentUrl(string path, params string[] parameters) => GetUrl(BuildEnvironmentUrl(), path, parameters); private string GetSubscriptionUrl(string path, params string[] parameters) => GetUrl(BuildSubscriptionUrl(), path, parameters); diff --git a/Kontent.Ai.Management/Modules/UrlBuilder/Templates/WebSpotlightTemplate.cs b/Kontent.Ai.Management/Modules/UrlBuilder/Templates/WebSpotlightTemplate.cs new file mode 100644 index 00000000..e2fe92b9 --- /dev/null +++ b/Kontent.Ai.Management/Modules/UrlBuilder/Templates/WebSpotlightTemplate.cs @@ -0,0 +1,11 @@ +using System; + +namespace Kontent.Ai.Management.Modules.UrlBuilder.Templates; + +internal class WebSpotlightTemplate : UrlTemplate +{ + public override string Url => "/web-spotlight"; + public override string UrlId => throw new InvalidOperationException("Web Spotlight does not have Id Url."); + public override string UrlCodename => throw new InvalidOperationException("Web Spotlight does not have Codename Url."); + public override string UrlExternalId => throw new InvalidOperationException("Web Spotlight does not have External Id Url."); +} \ No newline at end of file From 51cc78dc67228ea282a35a7ca6b1714454bdc6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Z=C3=A1hora?= Date: Wed, 10 Jul 2024 13:32:44 +0200 Subject: [PATCH 2/7] EN-238 Update web spotlight activation model --- .../WebSpotlightTests.cs | 29 +++++++++++++++++-- .../WebSpotlight/WebSpotlightActivateModel.cs | 8 ++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs b/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs index 0aee3751..adfd06c4 100644 --- a/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs +++ b/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs @@ -1,4 +1,5 @@ -using Kontent.Ai.Management.Models.WebSpotlight; +using Kontent.Ai.Management.Models.Shared; +using Kontent.Ai.Management.Models.WebSpotlight; using Kontent.Ai.Management.Tests.Base; using System; using System.Net.Http; @@ -31,14 +32,36 @@ public async void ActivateWebSpotlight_Returns_EnabledStatusAndRootTypeId() } [Fact] - public async void ActivateWebSpotlight_WithProvidedValidRootType_Returns_EnabledStatusAndRootTypeId() + public async void ActivateWebSpotlight_WithProvidedValidRootTypeById_Returns_EnabledStatusAndRootTypeId() { var client = _scenario .WithResponses("ActivationWebSpotlightWithProvidedRootTypeIdResponse.json") .CreateManagementClient(); var rootTypeId = Guid.Parse("3660e894-bae8-4dcd-9d3e-5fc9205c2ece"); - var webSpotlightActivateModel = new WebSpotlightActivateModel { RootTypeId = rootTypeId }; + var reference = Reference.ById(rootTypeId); + var webSpotlightActivateModel = new WebSpotlightActivateModel { RootType = reference }; + + await client.ActivateWebSpotlightAsync(webSpotlightActivateModel); + + _scenario + .CreateExpectations() + .HttpMethod(HttpMethod.Put) + .RequestPayload(webSpotlightActivateModel) + .Url(WebSpotlightBaseUrl) + .Validate(); + } + + [Fact] + public async void ActivateWebSpotlight_WithProvidedValidRootTypeByCodename_Returns_EnabledStatusAndRootTypeId() + { + var client = _scenario + .WithResponses("ActivationWebSpotlightWithProvidedRootTypeIdResponse.json") + .CreateManagementClient(); + + var rootTypeCodename = "root_type_codename"; + var reference = Reference.ByCodename(rootTypeCodename); + var webSpotlightActivateModel = new WebSpotlightActivateModel { RootType = reference }; await client.ActivateWebSpotlightAsync(webSpotlightActivateModel); diff --git a/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightActivateModel.cs b/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightActivateModel.cs index 93678c5d..e0c208c0 100644 --- a/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightActivateModel.cs +++ b/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightActivateModel.cs @@ -1,5 +1,5 @@ -using Newtonsoft.Json; -using System; +using Kontent.Ai.Management.Models.Shared; +using Newtonsoft.Json; namespace Kontent.Ai.Management.Models.WebSpotlight; @@ -11,6 +11,6 @@ public class WebSpotlightActivateModel /// /// Gets or sets the web spotlight's Root Type ID. /// - [JsonProperty("root_type_id")] - public Guid? RootTypeId { get; set; } + [JsonProperty("root_type")] + public Reference RootType { get; set; } } \ No newline at end of file From 88b513aecac8108585e86cea54840335c93a2a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Z=C3=A1hora?= Date: Sat, 13 Jul 2024 02:00:10 +0200 Subject: [PATCH 3/7] EN-238 Remove unnecessary activation method --- .../ManagementClientTests/WebSpotlightTests.cs | 4 +++- Kontent.Ai.Management/IManagementClient.cs | 6 ------ Kontent.Ai.Management/ManagementClient.WebSpotlight.cs | 9 +-------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs b/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs index adfd06c4..297038d4 100644 --- a/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs +++ b/Kontent.Ai.Management.Tests/ManagementClientTests/WebSpotlightTests.cs @@ -20,8 +20,10 @@ public async void ActivateWebSpotlight_Returns_EnabledStatusAndRootTypeId() .WithResponses("ActivationWebSpotlightResponse.json") .CreateManagementClient(); + var webSpotlightActivateModel = new WebSpotlightActivateModel { RootType = null }; + var response = await client - .ActivateWebSpotlightAsync(); + .ActivateWebSpotlightAsync(webSpotlightActivateModel); _scenario .CreateExpectations() diff --git a/Kontent.Ai.Management/IManagementClient.cs b/Kontent.Ai.Management/IManagementClient.cs index 2dc61057..71d7d8f7 100644 --- a/Kontent.Ai.Management/IManagementClient.cs +++ b/Kontent.Ai.Management/IManagementClient.cs @@ -812,12 +812,6 @@ public interface IManagementClient /// The instance that represents the web spotlight status. Task ActivateWebSpotlightAsync(WebSpotlightActivateModel webSpotlightActivateModel); - /// - /// Activates the web spotlight status. - /// - /// The instance that represents the web spotlight status. - Task ActivateWebSpotlightAsync(); - /// /// Deactivates the web spotlight. /// diff --git a/Kontent.Ai.Management/ManagementClient.WebSpotlight.cs b/Kontent.Ai.Management/ManagementClient.WebSpotlight.cs index a2990a98..885b7e19 100644 --- a/Kontent.Ai.Management/ManagementClient.WebSpotlight.cs +++ b/Kontent.Ai.Management/ManagementClient.WebSpotlight.cs @@ -18,14 +18,7 @@ public async Task ActivateWebSpotlightAsync(WebSpotlightActiv var endpointUrl = _urlBuilder.BuildWebSpotlightUrl(); return await _actionInvoker.InvokeMethodAsync(endpointUrl, HttpMethod.Put, webSpotlightActivateModel); } - - /// - public async Task ActivateWebSpotlightAsync() - { - var endpointUrl = _urlBuilder.BuildWebSpotlightUrl(); - return await _actionInvoker.InvokeReadOnlyMethodAsync(endpointUrl, HttpMethod.Put); - } - + /// public async Task DeactivateWebSpotlightAsync() { From a49d62a78565affc8f6348435a1b3fffb38c6279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Z=C3=A1hora?= Date: Thu, 25 Jul 2024 08:57:24 +0200 Subject: [PATCH 4/7] EN-238 Update Web Spotlight response model --- .../Data/WebSpotlight/ActivationWebSpotlightResponse.json | 2 +- .../ActivationWebSpotlightWithProvidedRootTypeIdResponse.json | 2 +- .../Data/WebSpotlight/DeactivationWebSpotlightResponse.json | 2 +- .../Data/WebSpotlight/GetStatusWebSpotlightResponse.json | 2 +- Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightResponse.json b/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightResponse.json index d9a55ca3..2f746298 100644 --- a/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightResponse.json +++ b/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightResponse.json @@ -1,4 +1,4 @@ { "enabled": true, - "root_type_id": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" + "root_type": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" } \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightWithProvidedRootTypeIdResponse.json b/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightWithProvidedRootTypeIdResponse.json index 21214617..18a240ae 100644 --- a/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightWithProvidedRootTypeIdResponse.json +++ b/Kontent.Ai.Management.Tests/Data/WebSpotlight/ActivationWebSpotlightWithProvidedRootTypeIdResponse.json @@ -1,4 +1,4 @@ { "enabled": true, - "root_type_id": "3660e894-bae8-4dcd-9d3e-5fc9205c2ece" + "root_type": "3660e894-bae8-4dcd-9d3e-5fc9205c2ece" } \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Data/WebSpotlight/DeactivationWebSpotlightResponse.json b/Kontent.Ai.Management.Tests/Data/WebSpotlight/DeactivationWebSpotlightResponse.json index e4fb263e..bf3fe36e 100644 --- a/Kontent.Ai.Management.Tests/Data/WebSpotlight/DeactivationWebSpotlightResponse.json +++ b/Kontent.Ai.Management.Tests/Data/WebSpotlight/DeactivationWebSpotlightResponse.json @@ -1,4 +1,4 @@ { "enabled": false, - "root_type_id": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" + "root_type": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" } \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Data/WebSpotlight/GetStatusWebSpotlightResponse.json b/Kontent.Ai.Management.Tests/Data/WebSpotlight/GetStatusWebSpotlightResponse.json index d9a55ca3..2f746298 100644 --- a/Kontent.Ai.Management.Tests/Data/WebSpotlight/GetStatusWebSpotlightResponse.json +++ b/Kontent.Ai.Management.Tests/Data/WebSpotlight/GetStatusWebSpotlightResponse.json @@ -1,4 +1,4 @@ { "enabled": true, - "root_type_id": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" + "root_type": "0689fcb3-24f1-49f4-b115-e7b816d59e9d" } \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs b/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs index 3ff0c22b..a6172e7d 100644 --- a/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs +++ b/Kontent.Ai.Management/Models/WebSpotlight/WebSpotlightModel.cs @@ -17,6 +17,6 @@ public class WebSpotlightModel /// /// Gets or sets the web spotlight's Root Type ID. /// - [JsonProperty("root_type_id")] + [JsonProperty("root_type")] public Guid? RootTypeId { get; set; } } \ No newline at end of file From a272aac36f231d9f709c687cf4e974f279be5ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franti=C5=A1ek=20Z=C3=A1hora?= Date: Tue, 30 Jul 2024 10:07:07 +0200 Subject: [PATCH 5/7] EN-238 Improve methods descriptions as suggested --- Kontent.Ai.Management/IManagementClient.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Kontent.Ai.Management/IManagementClient.cs b/Kontent.Ai.Management/IManagementClient.cs index 71d7d8f7..6025fb95 100644 --- a/Kontent.Ai.Management/IManagementClient.cs +++ b/Kontent.Ai.Management/IManagementClient.cs @@ -806,21 +806,21 @@ public interface IManagementClient Task ModifyPreviewConfigurationAsync(PreviewConfigurationModel previewConfiguration); /// - /// Activates the web spotlight status with possibility to provide existing Root Type ID. + /// Activates the web spotlight, allowing you to specify an existing Root Type ID. /// /// Represents configuration that will be used for web spotlight activation. - /// The instance that represents the web spotlight status. + /// A instance representing the web spotlight status. Task ActivateWebSpotlightAsync(WebSpotlightActivateModel webSpotlightActivateModel); /// /// Deactivates the web spotlight. /// - /// The instance that represents the web spotlight status. + /// A instance representing the web spotlight status. Task DeactivateWebSpotlightAsync(); /// /// Returns the web spotlight status. /// - /// The instance that represents the web spotlight status. + /// A instance representing the web spotlight status. Task GetWebSpotlightStatusAsync(); } From 308705e4088b422f1c3b8d4c9d56b25774858369 Mon Sep 17 00:00:00 2001 From: Matus Backor Date: Tue, 30 Jul 2024 15:38:32 +0200 Subject: [PATCH 6/7] EN-207 Add asset folder codenames --- .../Data/AssetFolder/Folder.json | 3 + .../ManagementClientTests/AssetFolderTests.cs | 2 + .../Extensions/AssetExtensions.cs | 59 +++++++++++++++++++ .../AssetFolders/AssetFolderHierarchy.cs | 6 ++ .../AssetFolderLinkingHierarchy.cs | 6 ++ 5 files changed, 76 insertions(+) diff --git a/Kontent.Ai.Management.Tests/Data/AssetFolder/Folder.json b/Kontent.Ai.Management.Tests/Data/AssetFolder/Folder.json index e2cb6bbc..ede68583 100644 --- a/Kontent.Ai.Management.Tests/Data/AssetFolder/Folder.json +++ b/Kontent.Ai.Management.Tests/Data/AssetFolder/Folder.json @@ -3,11 +3,13 @@ { "id": "958001d8-2228-4373-b966-5262b5b96f71", "name": "Downloads", + "codename": "downloads", "external_id": "folder-with-downloadable-assets", "folders": [ { "id": "9ca927b6-6e4d-4d6b-81e3-ec5e8f7772a0", "name": "Archives", + "codename": "archives", "external_id": "folder-with-downloadable-archives", "folders": [] } @@ -16,6 +18,7 @@ { "id": "9ca927b6-6e4d-4d6b-81e3-ec5e8f7772a0", "name": "Legal documents", + "codename": "legal_documents", "external_id": "folder-documents", "folders": [] } diff --git a/Kontent.Ai.Management.Tests/ManagementClientTests/AssetFolderTests.cs b/Kontent.Ai.Management.Tests/ManagementClientTests/AssetFolderTests.cs index 37120653..643fc1d6 100644 --- a/Kontent.Ai.Management.Tests/ManagementClientTests/AssetFolderTests.cs +++ b/Kontent.Ai.Management.Tests/ManagementClientTests/AssetFolderTests.cs @@ -55,6 +55,7 @@ public async Task CreateAssetFoldersAsync_CreatesFolder() { ExternalId = "external-id", Name= "name", + Codename = "codename", Folders = Enumerable.Empty() } } @@ -114,6 +115,7 @@ public async Task ModifyAssetFoldersAsync_ChangesAreNull_Throws() { ExternalId = "external-id", Name= "name", + Codename = "codename", Folders = Enumerable.Empty() }, Before = Reference.ByCodename("codename"), diff --git a/Kontent.Ai.Management/Extensions/AssetExtensions.cs b/Kontent.Ai.Management/Extensions/AssetExtensions.cs index c2c372ca..03fa5010 100644 --- a/Kontent.Ai.Management/Extensions/AssetExtensions.cs +++ b/Kontent.Ai.Management/Extensions/AssetExtensions.cs @@ -71,6 +71,36 @@ public static AssetFolderHierarchy GetFolderHierarchyByExternalId(this IEnumerab } return null; } + + /// + /// Gets folder hierarchy for a given folder codename + /// + /// The property retrieved from the method. + /// Folder codename + /// The instance that represents the folder found for a given folder codename. Null if not found. + public static AssetFolderHierarchy GetFolderHierarchyByCodename(this IEnumerable folders, string codename) + { + if (folders == null) + { + return null; + } + + // Recursively search for the folder hierarchy that an asset is in. Returns null if file is not in a folder. + foreach (var folder in folders) + { + if (folder.Codename == codename) + { + return folder; + } + + var nestedFolder = folder.Folders?.GetFolderHierarchyByCodename(codename); + if (nestedFolder != null) //This is required so you don't stop processing if the root contains many folders (let the above foreach loop continue) + { + return nestedFolder; + } + } + return null; + } /// /// Gets the full folder path string @@ -147,6 +177,34 @@ public static AssetFolderLinkingHierarchy GetParentLinkedFolderHierarchyByExtern } return null; } + + /// + /// Gets the folder hierarchy for a given folder identifier. + /// To use this method first convert your property retrieved from to a IEnumerable<AssetFolderLinkingHierarchy> by using the method. + /// + /// The instance. + /// Folder codename + /// Returns the instance found via a given folder identifier. + public static AssetFolderLinkingHierarchy GetParentLinkedFolderHierarchyByCodename(this IEnumerable folders, string codename) + { + if (folders != null) + { + foreach (var folder in folders) + { + if (folder.Codename == codename) + { + return folder; + } + + var nestedFolder = folder.Folders?.GetParentLinkedFolderHierarchyByCodename(codename); + if (nestedFolder != null) // This is required so you don't stop processing if the root contains many folders (let the above for-each loop continue) + { + return nestedFolder; + } + } + } + return null; + } /// /// Retrieves a list of folders with the property filled in. @@ -166,6 +224,7 @@ public static IEnumerable GetParentLinkedFolderHier ExternalId = itm.ExternalId, Folders = itm.Folders != null ? new List() : null, Id = itm.Id, + Codename = itm.Codename, Name = itm.Name }; if (itm.Folders != null) diff --git a/Kontent.Ai.Management/Models/AssetFolders/AssetFolderHierarchy.cs b/Kontent.Ai.Management/Models/AssetFolders/AssetFolderHierarchy.cs index 71b3e4d4..16832043 100644 --- a/Kontent.Ai.Management/Models/AssetFolders/AssetFolderHierarchy.cs +++ b/Kontent.Ai.Management/Models/AssetFolders/AssetFolderHierarchy.cs @@ -19,6 +19,12 @@ public sealed class AssetFolderHierarchy /// [JsonProperty("external_id", DefaultValueHandling = DefaultValueHandling.Ignore)] public string ExternalId { get; set; } + + /// + /// Gets or sets the codename of the folder. + /// + [JsonProperty("codename", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Codename { get; set; } /// /// Gets or sets the name of the folder diff --git a/Kontent.Ai.Management/Models/AssetFolders/AssetFolderLinkingHierarchy.cs b/Kontent.Ai.Management/Models/AssetFolders/AssetFolderLinkingHierarchy.cs index 25f109da..d9e16de4 100644 --- a/Kontent.Ai.Management/Models/AssetFolders/AssetFolderLinkingHierarchy.cs +++ b/Kontent.Ai.Management/Models/AssetFolders/AssetFolderLinkingHierarchy.cs @@ -21,6 +21,12 @@ public sealed class AssetFolderLinkingHierarchy /// [JsonProperty("external_id", DefaultValueHandling = DefaultValueHandling.Ignore)] public string ExternalId { get; set; } + + /// + /// Gets or sets the folder's codename. + /// + [JsonProperty("codename", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string Codename { get; set; } /// /// Name of the folder From c79343bf154ccfb6519b9d028b026e28979795eb Mon Sep 17 00:00:00 2001 From: DiegoF <11487214+DiegoFaFe@users.noreply.github.com> Date: Sun, 16 Jun 2024 19:55:21 +0200 Subject: [PATCH 7/7] Add strongly typed response for listing variants by type --- .../LanguageVariantTests.cs | 62 +++++++++++++++---- Kontent.Ai.Management/IManagementClient.cs | 11 ++++ .../ManagementClient.LanguageVariant.cs | 19 ++++++ 3 files changed, 80 insertions(+), 12 deletions(-) diff --git a/Kontent.Ai.Management.Tests/ManagementClientTests/LanguageVariantTests.cs b/Kontent.Ai.Management.Tests/ManagementClientTests/LanguageVariantTests.cs index 40adff74..9eaa30dd 100644 --- a/Kontent.Ai.Management.Tests/ManagementClientTests/LanguageVariantTests.cs +++ b/Kontent.Ai.Management.Tests/ManagementClientTests/LanguageVariantTests.cs @@ -83,6 +83,43 @@ await client.Invoking(x => x.ListLanguageVariantsByItemAsync(null)) .Should().ThrowExactlyAsync(); } + [Fact] + public async Task ListLanguageVariantsByTypeAsync_StronglyTyped_ListsVariants() + { + var client = _scenario + .WithResponses("LanguageVariantsPage1.json", "LanguageVariantsPage2.json", "LanguageVariantsPage3.json") + .CreateManagementClient(); + + var expected = new[] + { + (itemId: "00000000-0000-0000-0000-000000000000", languageId: "00000000-0000-0000-0000-000000000000"), + (itemId: "00000000-0000-0000-0000-000000000000", languageId: "10000000-0000-0000-0000-000000000000"), + (itemId: "10000000-0000-0000-0000-000000000000", languageId: "00000000-0000-0000-0000-000000000000"), + (itemId: "10000000-0000-0000-0000-000000000000", languageId: "10000000-0000-0000-0000-000000000000"), + (itemId: "20000000-0000-0000-0000-000000000000", languageId: "00000000-0000-0000-0000-000000000000"), + (itemId: "20000000-0000-0000-0000-000000000000", languageId: "10000000-0000-0000-0000-000000000000") + }.Select(x => GetExpectedComplexTestModel(x.languageId, x.itemId)); + + var identifier = Reference.ById(Guid.Parse("17ff8a28-ebe6-5c9d-95ea-18fe1ff86d2d")); + var response = await client.ListLanguageVariantsByTypeAsync(identifier).GetAllAsync(); + + _scenario + .CreateExpectations() + .HttpMethod(HttpMethod.Get) + .Response(response, expected) + .Url($"{Endpoint}/projects/{ENVIRONMENT_ID}/types/{identifier.Id}/variants") + .Validate(); + } + + [Fact] + public async Task ListLanguageVariantsByTypeAsync_StronglyTyped_IdentifierIsNull_Throws() + { + var client = _scenario.CreateManagementClient(); + + await client.Invoking(x => x.ListLanguageVariantsByTypeAsync(null)) + .Should().ThrowExactlyAsync(); + } + [Fact] public async void ListLanguageVariantsByTypeAsync_DynamicallyTyped_ListsVariants() { @@ -120,7 +157,6 @@ await client.Invoking(x => x.ListLanguageVariantsByTypeAsync(null)) .Should().ThrowExactlyAsync(); } - [Fact] public async Task ListLanguageVariantsOfContentTypeWithComponentsAsync_DynamicallyTyped_ListsVariants() { @@ -496,18 +532,20 @@ private static LanguageVariantModel GetExpectedLanguageVariantModel( }; private static List> GetExpectedComplexTestModels(params string[] languageIds) - => languageIds.Select(GetExpectedComplexTestModel).ToList(); + => languageIds.Select(x => GetExpectedComplexTestModel(x)).ToList(); - private static LanguageVariantModel GetExpectedComplexTestModel(string languageId = "78dbefe8-831b-457e-9352-f4c4eacd5024") => new() - { - Item = Reference.ById(Guid.Parse("4b628214-e4fe-4fe0-b1ff-955df33e1515")), - Language = Reference.ById(Guid.Parse(languageId)), - LastModified = DateTimeOffset.Parse("2021-11-06T13:57:26.7069564Z").UtcDateTime, - Workflow = new WorkflowStepIdentifier(Reference.ById(Guid.Parse("00000000-0000-0000-0000-000000000000")), Reference.ById(Guid.Parse("eee6db3b-545a-4785-8e86-e3772c8756f9"))), - Schedule = GetExpectedScheduleResponseModel(), - DueDate = GetExpectedDueDateModel(), - Elements = ElementsData.GetExpectedStronglyTypedElementsModel(), - }; + private static LanguageVariantModel GetExpectedComplexTestModel( + string languageId = "78dbefe8-831b-457e-9352-f4c4eacd5024", + string itemId = "4b628214-e4fe-4fe0-b1ff-955df33e1515") => new() + { + Item = Reference.ById(Guid.Parse(itemId)), + Language = Reference.ById(Guid.Parse(languageId)), + LastModified = DateTimeOffset.Parse("2021-11-06T13:57:26.7069564Z").UtcDateTime, + Workflow = new WorkflowStepIdentifier(Reference.ById(Guid.Parse("00000000-0000-0000-0000-000000000000")), Reference.ById(Guid.Parse("eee6db3b-545a-4785-8e86-e3772c8756f9"))), + Schedule = GetExpectedScheduleResponseModel(), + DueDate = GetExpectedDueDateModel(), + Elements = ElementsData.GetExpectedStronglyTypedElementsModel(), + }; private static ScheduleResponseModel GetExpectedScheduleResponseModel() => new() { diff --git a/Kontent.Ai.Management/IManagementClient.cs b/Kontent.Ai.Management/IManagementClient.cs index 6025fb95..e45fe36a 100644 --- a/Kontent.Ai.Management/IManagementClient.cs +++ b/Kontent.Ai.Management/IManagementClient.cs @@ -473,6 +473,17 @@ public interface IManagementClient /// The instance that represents the listing of language variants. Task> ListLanguageVariantsByTypeAsync(Reference identifier); + /// + /// Returns strongly typed listing of language variants with strongly typed elements for the specified content type. + /// The Content management API returns a dynamically paginated listing response limited to up to 100 objects. + /// To check if the next page is available use . + /// For getting next page use . + /// + /// Type of the content item elements + /// The identifier of the content type. + /// The instance that represents the listing of language variants. + Task>> ListLanguageVariantsByTypeAsync(Reference identifier) where T : new(); + /// /// Returns strongly typed listing of language variants containing components by type. /// The Content management API returns a dynamically paginated listing response limited to up to 100 objects. diff --git a/Kontent.Ai.Management/ManagementClient.LanguageVariant.cs b/Kontent.Ai.Management/ManagementClient.LanguageVariant.cs index bb1006c2..fb468839 100644 --- a/Kontent.Ai.Management/ManagementClient.LanguageVariant.cs +++ b/Kontent.Ai.Management/ManagementClient.LanguageVariant.cs @@ -44,6 +44,25 @@ public async Task> ListLanguageVaria response.Variants); } + /// + public async Task>> ListLanguageVariantsByTypeAsync(Reference identifier) where T : new() + { + if (identifier == null) + { + throw new ArgumentNullException(nameof(identifier)); + } + + var endpointUrl = _urlBuilder.BuildListVariantsByTypeUrl(identifier); + var response = await _actionInvoker.InvokeReadOnlyMethodAsync(endpointUrl, HttpMethod.Get); + + return new ListingResponseMappedModel>( + (token, url) => GetNextListingPageAsync(token, url), + response.Pagination?.Token, + endpointUrl, + response.Variants, + _modelProvider.GetLanguageVariantModel); + } + /// public async Task> ListLanguageVariantsOfContentTypeWithComponentsAsync(Reference identifier) {