diff --git a/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilter.json b/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilter.json new file mode 100644 index 00000000..3761a988 --- /dev/null +++ b/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilter.json @@ -0,0 +1,67 @@ +{ + "data": [ + { + "item": { + "id": "4b628214-e4fe-4fe0-b1ff-955df33e1515", + "name": "Sample Article", + "codename": "sample_article", + "type": { + "id": "ba5cd79d-6d4b-5bed-a681-6aa3a366c8f7", + "codename": "article" + }, + "collection": { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "default" + }, + "spaces": [ + { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "default" + } + ], + "sitemap_locations": [], + "last_modified": "2024-01-15T10:30:00.000Z" + } + }, + { + "item": { + "id": "6a8b4d04-7d3e-4d3c-8b9a-4c7e8f9a1b2c", + "name": "Another Article", + "codename": "another_article", + "type": { + "id": "ba5cd79d-6d4b-5bed-a681-6aa3a366c8f7", + "codename": "article" + }, + "collection": { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "default" + }, + "spaces": [ + { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "default" + } + ], + "sitemap_locations": [], + "external_id": "ext_another_article", + "last_modified": "2024-01-16T14:45:00.000Z" + }, + "variant": { + "item": { + "id": "6a8b4d04-7d3e-4d3c-8b9a-4c7e8f9a1b2c", + "codename": "another_article" + }, + "language": { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "en-US" + }, + "elements": [], + "last_modified": "2024-01-16T14:45:00.000Z" + } + } + ], + "pagination": { + "continuation_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", + "next_page": "https://manage.kontent.ai/v2/projects/environmentId/early-access/variants/filter?continuation_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilterWithElements.json b/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilterWithElements.json new file mode 100644 index 00000000..33e018cc --- /dev/null +++ b/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilterWithElements.json @@ -0,0 +1,69 @@ +{ + "data": [ + { + "item": { + "id": "6a8b4d04-7d3e-4d3c-8b9a-4c7e8f9a1b2c", + "name": "Article with Content", + "codename": "article_with_content", + "type": { + "id": "ba5cd79d-6d4b-5bed-a681-6aa3a366c8f7", + "codename": "article" + }, + "collection": { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "default" + }, + "spaces": [ + { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "default" + } + ], + "sitemap_locations": [], + "external_id": "ext_article_with_content", + "last_modified": "2024-01-16T14:45:00.000Z" + }, + "variant": { + "item": { + "id": "6a8b4d04-7d3e-4d3c-8b9a-4c7e8f9a1b2c", + "codename": "article_with_content" + }, + "language": { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "en-US" + }, + "elements": [ + { + "element": { + "id": "bf7c2d26-4043-4c84-8e3e-5c318b3c19f4", + "codename": "title" + }, + "value": "Sample Article Title" + }, + { + "element": { + "id": "47bf7194-1c84-4c84-8e3e-5c318b3c29g5", + "codename": "summary" + }, + "value": "This is a sample article summary" + } + ], + "last_modified": "2024-01-16T14:45:00.000Z", + "workflow": { + "workflow_identifier": { + "id": "88ac5e6e-1c84-4c84-8e3e-5c318b3c44f6", + "codename": "default_workflow" + }, + "step_identifier": { + "id": "99bc6f7f-2d95-5d95-9f4f-6d429c4d5517", + "codename": "published" + } + } + } + } + ], + "pagination": { + "continuation_token": null, + "next_page": null + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilterWithoutElements.json b/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilterWithoutElements.json new file mode 100644 index 00000000..ea8ff995 --- /dev/null +++ b/Kontent.Ai.Management.Tests/Data/VariantFilter/VariantFilterWithoutElements.json @@ -0,0 +1,42 @@ +{ + "data": [ + { + "item": { + "id": "4b628214-e4fe-4fe0-b1ff-955df33e1515", + "name": "Sample Article", + "codename": "sample_article", + "type": { + "id": "ba5cd79d-6d4b-5bed-a681-6aa3a366c8f7", + "codename": "article" + }, + "collection": { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "default" + }, + "spaces": [ + { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "default" + } + ], + "sitemap_locations": [], + "last_modified": "2024-01-15T10:30:00.000Z" + }, + "variant": { + "item": { + "id": "4b628214-e4fe-4fe0-b1ff-955df33e1515", + "codename": "sample_article" + }, + "language": { + "id": "00000000-0000-0000-0000-000000000000", + "codename": "en-US" + }, + "last_modified": "2024-01-15T10:30:00.000Z" + } + } + ], + "pagination": { + "continuation_token": null, + "next_page": null + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/ManagementClientTests/VariantFilterTests.cs b/Kontent.Ai.Management.Tests/ManagementClientTests/VariantFilterTests.cs new file mode 100644 index 00000000..af5cb225 --- /dev/null +++ b/Kontent.Ai.Management.Tests/ManagementClientTests/VariantFilterTests.cs @@ -0,0 +1,276 @@ +using FluentAssertions; +using Kontent.Ai.Management.Models.Shared; +using Kontent.Ai.Management.Models.VariantFilter; +using Kontent.Ai.Management.Tests.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Kontent.Ai.Management.Tests.ManagementClientTests; + +public class VariantFilterTests : IClassFixture +{ + private readonly FileSystemFixture _fileSystemFixture; + + public VariantFilterTests(FileSystemFixture fileSystemFixture) + { + _fileSystemFixture = fileSystemFixture; + _fileSystemFixture.SetSubFolder("VariantFilter"); + } + + [Fact] + public async Task FilterVariantsAsync_WithValidRequest_ReturnsVariantFilterListingResponseServerModel() + { + var client = _fileSystemFixture.CreateMockClientWithResponse("VariantFilter.json"); + + var request = new VariantFilterRequestModel + { + Filters = new VariantFilterFiltersModel + { + SearchPhrase = "test", + Language = Reference.ByCodename("en-US"), + ContentTypes = new List + { + Reference.ByCodename("article") + }, + CompletionStatuses = new List { VariantFilterCompletionStatus.Completed } + }, + Order = new VariantFilterOrderModel + { + By = "name", + Direction = VariantFilterOrderDirection.Ascending + } + }; + + var response = await client.EarlyAccess.FilterVariantsAsync(request); + + response.Should().NotBeNull(); + response.Should().BeAssignableTo>(); + + var firstItem = response.ToList().First(); + firstItem.Item.Should().NotBeNull(); + firstItem.Item.Id.Should().NotBeEmpty(); + firstItem.Item.Name.Should().NotBeNullOrEmpty(); + } + + [Fact] + public async Task FilterVariantsAsync_WithNullRequest_ThrowsArgumentNullException() + { + var client = _fileSystemFixture.CreateMockClientWithResponse("VariantFilter.json"); + + await Assert.ThrowsAsync(async () => await client.EarlyAccess.FilterVariantsAsync(null)); + } + + [Fact] + public async Task FilterVariantsAsync_WithMinimalRequest_ReturnsVariantFilterListingResponseServerModel() + { + var client = _fileSystemFixture.CreateMockClientWithResponse("VariantFilter.json"); + + var request = new VariantFilterRequestModel + { + Filters = new VariantFilterFiltersModel + { + Language = Reference.ByCodename("en-US") + } + }; + + var response = await client.EarlyAccess.FilterVariantsAsync(request); + + response.Should().NotBeNull(); + response.Should().BeAssignableTo>(); + + var firstItem = response.ToList().First(); + firstItem.Item.Should().NotBeNull(); + firstItem.Item.Id.Should().NotBeEmpty(); + firstItem.Item.Name.Should().NotBeNullOrEmpty(); + } + + [Fact] + public async Task FilterVariantsAsync_WithComplexFilters_ReturnsVariantFilterListingResponseServerModel() + { + var client = _fileSystemFixture.CreateMockClientWithResponse("VariantFilter.json"); + + var request = new VariantFilterRequestModel + { + Filters = new VariantFilterFiltersModel + { + Language = Reference.ByCodename("en-US"), + ContentTypes = new List + { + Reference.ByCodename("article"), + Reference.ByCodename("blog_post") + }, + Contributors = new List + { + UserIdentifier.ByEmail("user@example.com") + }, + CompletionStatuses = new List { VariantFilterCompletionStatus.Completed, VariantFilterCompletionStatus.Unfinished }, + WorkflowSteps = new List + { + new VariantFilterWorkflowStepsModel + { + WorkflowReference = Reference.ByCodename("default"), + WorkflowStepReferences = new List + { + Reference.ByCodename("draft") + } + } + }, + TaxonomyGroups = new List + { + new VariantFilterTaxonomyGroupModel + { + TaxonomyReference = Reference.ByCodename("categories"), + TermReferences = new List + { + Reference.ByCodename("tech") + }, + IncludeUncategorized = false + } + } + }, + Order = new VariantFilterOrderModel + { + By = "last_modified", + Direction = VariantFilterOrderDirection.Descending + } + }; + + var response = await client.EarlyAccess.FilterVariantsAsync(request); + + response.Should().NotBeNull(); + response.Should().BeAssignableTo>(); + + var items = response.ToList(); + items.Should().HaveCount(2); + + var firstItem = items.First(); + firstItem.Item.Should().NotBeNull(); + firstItem.Item.Id.Should().NotBeEmpty(); + firstItem.Item.Name.Should().NotBeNullOrEmpty(); + + // Check the second item that has variant data + var secondItem = items.Skip(1).First(); + secondItem.Item.Should().NotBeNull(); + secondItem.Item.Id.Should().Be(new Guid("6a8b4d04-7d3e-4d3c-8b9a-4c7e8f9a1b2c")); + secondItem.Item.Name.Should().Be("Another Article"); + secondItem.Item.Codename.Should().Be("another_article"); + secondItem.Item.ExternalId.Should().Be("ext_another_article"); + + // Assert variant properties + secondItem.Variant.Should().NotBeNull(); + secondItem.Variant.Language.Should().NotBeNull(); + secondItem.Variant.Language.Codename.Should().Be("en-US"); + secondItem.Variant.Item.Should().NotBeNull(); + secondItem.Variant.Item.Codename.Should().Be("another_article"); + secondItem.Variant.LastModified.Should().NotBeNull(); + } + + [Fact] + public async Task FilterVariantsAsync_WithIncludeContentFalse_ReturnsVariantWithoutElements() + { + var client = _fileSystemFixture.CreateMockClientWithResponse("VariantFilterWithoutElements.json"); + + var request = new VariantFilterRequestModel + { + Filters = new VariantFilterFiltersModel + { + Language = Reference.ByCodename("en-US") + }, + IncludeContent = false + }; + + var response = await client.EarlyAccess.FilterVariantsAsync(request); + + response.Should().NotBeNull(); + response.Should().BeAssignableTo>(); + + var firstItem = response.ToList().First(); + firstItem.Item.Should().NotBeNull(); + firstItem.Item.Id.Should().Be(new Guid("4b628214-e4fe-4fe0-b1ff-955df33e1515")); + firstItem.Item.Name.Should().Be("Sample Article"); + + // Variant should exist but without elements + firstItem.Variant.Should().NotBeNull(); + firstItem.Variant.Language.Codename.Should().Be("en-US"); + firstItem.Variant.Elements.Should().BeNull(); + } + + [Fact] + public async Task FilterVariantsAsync_WithIncludeContentTrue_ReturnsVariantWithElements() + { + var client = _fileSystemFixture.CreateMockClientWithResponse("VariantFilterWithElements.json"); + + var request = new VariantFilterRequestModel + { + Filters = new VariantFilterFiltersModel + { + Language = Reference.ByCodename("en-US"), + ContentTypes = new List + { + Reference.ByCodename("article") + } + }, + IncludeContent = true + }; + + var response = await client.EarlyAccess.FilterVariantsAsync(request); + + response.Should().NotBeNull(); + response.Should().BeAssignableTo>(); + + var firstItem = response.ToList().First(); + firstItem.Item.Should().NotBeNull(); + firstItem.Item.Id.Should().Be(new Guid("6a8b4d04-7d3e-4d3c-8b9a-4c7e8f9a1b2c")); + firstItem.Item.Name.Should().Be("Article with Content"); + firstItem.Item.ExternalId.Should().Be("ext_article_with_content"); + + // Variant should exist with elements when include_content=true + firstItem.Variant.Should().NotBeNull(); + firstItem.Variant.Language.Codename.Should().Be("en-US"); + firstItem.Variant.Elements.Should().NotBeNull(); + firstItem.Variant.Elements.Should().HaveCount(2); + firstItem.Variant.Workflow.Should().NotBeNull(); + firstItem.Variant.Workflow.Step.Should().NotBeNull(); + firstItem.Variant.Workflow.Step.Codename.Should().Be("published"); + } + + [Fact] + public async Task FilterVariantsAsync_WithEnums_UsesCorrectValues() + { + var client = _fileSystemFixture.CreateMockClientWithResponse("VariantFilter.json"); + + var request = new VariantFilterRequestModel + { + Filters = new VariantFilterFiltersModel + { + Language = Reference.ByCodename("en-US"), + CompletionStatuses = new List + { + VariantFilterCompletionStatus.Completed, + VariantFilterCompletionStatus.Unfinished, + VariantFilterCompletionStatus.NotTranslated, + VariantFilterCompletionStatus.AllDone + } + }, + Order = new VariantFilterOrderModel + { + By = "name", + Direction = VariantFilterOrderDirection.Ascending + } + }; + + var response = await client.EarlyAccess.FilterVariantsAsync(request); + + // Verify that enums are properly serialized and the request succeeds + response.Should().NotBeNull(); + response.Should().BeAssignableTo>(); + + var firstItem = response.ToList().First(); + firstItem.Item.Should().NotBeNull(); + firstItem.Item.Id.Should().NotBeEmpty(); + firstItem.Item.Name.Should().NotBeNullOrEmpty(); + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management.Tests/Modules/UrlBuilder/VariantFilterTests.cs b/Kontent.Ai.Management.Tests/Modules/UrlBuilder/VariantFilterTests.cs new file mode 100644 index 00000000..da170433 --- /dev/null +++ b/Kontent.Ai.Management.Tests/Modules/UrlBuilder/VariantFilterTests.cs @@ -0,0 +1,15 @@ +using FluentAssertions; +using Xunit; + +namespace Kontent.Ai.Management.Tests.Modules.UrlBuilder; + +public partial class EndpointUrlBuilderTests +{ + [Fact] + public void BuildVariantFilterUrl_ReturnsCorrectUrl() + { + var result = _builder.BuildVariantFilterUrl(); + + result.Should().Be($"{ENDPOINT}/projects/{ENVIRONMENT_ID}/early-access/variants/filter"); + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/IManagementClient.cs b/Kontent.Ai.Management/IManagementClient.cs index d733bb7c..7bb48660 100644 --- a/Kontent.Ai.Management/IManagementClient.cs +++ b/Kontent.Ai.Management/IManagementClient.cs @@ -43,6 +43,12 @@ namespace Kontent.Ai.Management; /// public interface IManagementClient { + /// + /// Gets the early access client for experimental features. + /// These features may change or be removed in future versions. + /// + IManagementClientEarlyAccess EarlyAccess { get; } + /// /// Returns asset. /// @@ -872,4 +878,5 @@ public interface IManagementClient /// /// The instance that represents the custom app. Task ModifyCustomAppAsync(Reference identifier, IEnumerable changes); + } diff --git a/Kontent.Ai.Management/IManagementClientEarlyAccess.cs b/Kontent.Ai.Management/IManagementClientEarlyAccess.cs new file mode 100644 index 00000000..fdfc62c4 --- /dev/null +++ b/Kontent.Ai.Management/IManagementClientEarlyAccess.cs @@ -0,0 +1,20 @@ +using Kontent.Ai.Management.Models.Shared; +using Kontent.Ai.Management.Models.VariantFilter; +using System.Threading.Tasks; + +namespace Kontent.Ai.Management; + +/// +/// Represents a set of early access Content Management API requests. +/// These features are experimental and may change or be removed in future versions. +/// +public interface IManagementClientEarlyAccess +{ + /// + /// Returns listing of filtered variants. + /// This is an early access feature that may change or be removed in future versions. + /// + /// The variant filter request containing filters and ordering options. + /// The instance representing the filtered variants. + Task> FilterVariantsAsync(VariantFilterRequestModel variantFilterRequest); +} \ No newline at end of file diff --git a/Kontent.Ai.Management/ManagementClient.cs b/Kontent.Ai.Management/ManagementClient.cs index 747f2991..418fa042 100644 --- a/Kontent.Ai.Management/ManagementClient.cs +++ b/Kontent.Ai.Management/ManagementClient.cs @@ -22,6 +22,7 @@ public sealed partial class ManagementClient : IManagementClient private readonly ActionInvoker _actionInvoker; private readonly EndpointUrlBuilder _urlBuilder; private readonly IModelProvider _modelProvider; + private readonly Lazy _earlyAccess; /// /// Initializes a new instance of the class for managing content of the specified environment. @@ -62,6 +63,7 @@ public ManagementClient(ManagementOptions ManagementOptions, System.Net.Http.Htt new ManagementHttpClient(new Modules.HttpClient.HttpClient(httpClient), new DefaultResiliencePolicyProvider(ManagementOptions.MaxRetryAttempts), ManagementOptions.EnableResilienceLogic), new MessageCreator(ManagementOptions.ApiKey)); _modelProvider = ManagementOptions.ModelProvider ?? new ModelProvider(); + _earlyAccess = new Lazy(() => new ManagementClientEarlyAccess(_actionInvoker, _urlBuilder)); } internal ManagementClient(EndpointUrlBuilder urlBuilder, ActionInvoker actionInvoker, IModelProvider modelProvider = null) @@ -69,8 +71,15 @@ internal ManagementClient(EndpointUrlBuilder urlBuilder, ActionInvoker actionInv _urlBuilder = urlBuilder ?? throw new ArgumentNullException(nameof(urlBuilder)); _actionInvoker = actionInvoker ?? throw new ArgumentNullException(nameof(actionInvoker)); _modelProvider = modelProvider ?? new ModelProvider(); + _earlyAccess = new Lazy(() => new ManagementClientEarlyAccess(_actionInvoker, _urlBuilder)); } + /// + /// Gets the early access client for experimental features. + /// These features may change or be removed in future versions. + /// + public IManagementClientEarlyAccess EarlyAccess => _earlyAccess.Value; + private async Task> GetNextListingPageAsync(string continuationToken, string url) where TListingResponse : IListingResponse { diff --git a/Kontent.Ai.Management/ManagementClientEarlyAccess.cs b/Kontent.Ai.Management/ManagementClientEarlyAccess.cs new file mode 100644 index 00000000..f88806ef --- /dev/null +++ b/Kontent.Ai.Management/ManagementClientEarlyAccess.cs @@ -0,0 +1,53 @@ +using Kontent.Ai.Management.Models.Shared; +using Kontent.Ai.Management.Models.VariantFilter; +using Kontent.Ai.Management.Modules.ActionInvoker; +using Kontent.Ai.Management.Modules.UrlBuilder; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Kontent.Ai.Management; + +/// +/// Provides access to early access Content Management API features. +/// These features are experimental and may change or be removed in future versions. +/// +public sealed class ManagementClientEarlyAccess : IManagementClientEarlyAccess +{ + private readonly ActionInvoker _actionInvoker; + private readonly EndpointUrlBuilder _urlBuilder; + + internal ManagementClientEarlyAccess(ActionInvoker actionInvoker, EndpointUrlBuilder urlBuilder) + { + _actionInvoker = actionInvoker ?? throw new ArgumentNullException(nameof(actionInvoker)); + _urlBuilder = urlBuilder ?? throw new ArgumentNullException(nameof(urlBuilder)); + } + + /// + public async Task> FilterVariantsAsync(VariantFilterRequestModel variantFilterRequest) + { + ArgumentNullException.ThrowIfNull(variantFilterRequest); + + var endpointUrl = _urlBuilder.BuildVariantFilterUrl(); + var response = await _actionInvoker.InvokeMethodAsync(endpointUrl, HttpMethod.Post, variantFilterRequest); + + return new ListingResponseModel( + GetNextListingPageAsync, + response.Pagination?.Token, + endpointUrl, + response.Data); + } + + private async Task> GetNextListingPageAsync(string continuationToken, string url) + where TListingResponse : IListingResponse + { + var headers = new Dictionary + { + { "x-continuation", continuationToken } + }; + var response = await _actionInvoker.InvokeReadOnlyMethodAsync(url, HttpMethod.Get, headers); + + return response; + } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterCompletionStatus.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterCompletionStatus.cs new file mode 100644 index 00000000..42f59f33 --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterCompletionStatus.cs @@ -0,0 +1,33 @@ +using System.Runtime.Serialization; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents the completion status of a content item variant. +/// +public enum VariantFilterCompletionStatus +{ + /// + /// The variant is unfinished. + /// + [EnumMember(Value = "unfinished")] + Unfinished, + + /// + /// The variant is completed. + /// + [EnumMember(Value = "completed")] + Completed, + + /// + /// The variant is not translated. + /// + [EnumMember(Value = "not_translated")] + NotTranslated, + + /// + /// The variant is all done (completed and translated). + /// + [EnumMember(Value = "all_done")] + AllDone +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterFiltersModel.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterFiltersModel.cs new file mode 100644 index 00000000..8550ef0a --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterFiltersModel.cs @@ -0,0 +1,59 @@ +using Kontent.Ai.Management.Models.Shared; +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents the variant filter filters model. +/// +public class VariantFilterFiltersModel +{ + /// + /// Gets or sets the search phrase. + /// + [JsonProperty("search_phrase")] + public string SearchPhrase { get; set; } + + /// + /// Gets or sets the language. + /// + [JsonProperty("language")] + public Reference Language { get; set; } + + /// + /// Gets or sets the content types. + /// + [JsonProperty("content_types")] + public IEnumerable ContentTypes { get; set; } + + /// + /// Gets or sets the contributors. + /// + [JsonProperty("contributors")] + public IEnumerable Contributors { get; set; } + + /// + /// Gets or sets whether to filter items with no contributors. + /// + [JsonProperty("has_no_contributors")] + public bool? HasNoContributors { get; set; } + + /// + /// Gets or sets the completion statuses. + /// + [JsonProperty("completion_statuses")] + public IEnumerable CompletionStatuses { get; set; } + + /// + /// Gets or sets the workflow steps. + /// + [JsonProperty("workflow_steps")] + public IEnumerable WorkflowSteps { get; set; } + + /// + /// Gets or sets the taxonomy groups. + /// + [JsonProperty("taxonomy_groups")] + public IEnumerable TaxonomyGroups { get; set; } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterItemModel.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterItemModel.cs new file mode 100644 index 00000000..49396763 --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterItemModel.cs @@ -0,0 +1,23 @@ +using Kontent.Ai.Management.Models.Items; +using Kontent.Ai.Management.Models.LanguageVariants; +using Newtonsoft.Json; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents a variant filter item model. +/// +public class VariantFilterItemModel +{ + /// + /// Gets or sets the content item. + /// + [JsonProperty("item")] + public ContentItemModel Item { get; set; } + + /// + /// Gets or sets the language variant (only includes `elements` when include_content is set to true). + /// + [JsonProperty("variant", DefaultValueHandling = DefaultValueHandling.Ignore)] + public LanguageVariantModel Variant { get; set; } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterListingResponseServerModel.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterListingResponseServerModel.cs new file mode 100644 index 00000000..0d790835 --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterListingResponseServerModel.cs @@ -0,0 +1,29 @@ +using Kontent.Ai.Management.Models.Shared; +using Newtonsoft.Json; +using System.Collections; +using System.Collections.Generic; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents the variant filter response model. +/// +[JsonObject] +internal class VariantFilterListingResponseServerModel : IListingResponse +{ + /// + /// Gets or sets the variant filter data. + /// + [JsonProperty("data")] + public IEnumerable Data { get; set; } + + /// + /// Gets or sets the pagination response. + /// + [JsonProperty("pagination")] + public PaginationResponseModel Pagination { get; set; } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public IEnumerator GetEnumerator() => Data.GetEnumerator(); +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterOrderDirection.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterOrderDirection.cs new file mode 100644 index 00000000..e1c0759c --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterOrderDirection.cs @@ -0,0 +1,21 @@ +using System.Runtime.Serialization; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents the order direction for variant filter results. +/// +public enum VariantFilterOrderDirection +{ + /// + /// Ascending order. + /// + [EnumMember(Value = "asc")] + Ascending, + + /// + /// Descending order. + /// + [EnumMember(Value = "desc")] + Descending +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterOrderModel.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterOrderModel.cs new file mode 100644 index 00000000..57b57e48 --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterOrderModel.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents the variant filter order model. +/// +public class VariantFilterOrderModel +{ + /// + /// Gets or sets the order by column. + /// + [JsonProperty("by")] + public string By { get; set; } + + /// + /// Gets or sets the order direction. + /// + [JsonProperty("direction")] + public VariantFilterOrderDirection Direction { get; set; } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterRequestModel.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterRequestModel.cs new file mode 100644 index 00000000..b21dbf69 --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterRequestModel.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents the variant filter request model. +/// +public class VariantFilterRequestModel +{ + /// + /// Gets or sets the filters. + /// + [JsonProperty("filters")] + public VariantFilterFiltersModel Filters { get; set; } + + /// + /// Gets or sets the order. + /// + [JsonProperty("order")] + public VariantFilterOrderModel Order { get; set; } + + /// + /// Gets or sets whether to include content in the response. If the content is not included, the `elements` property in the language variants response will be omitted. + /// + [JsonProperty("include_content")] + public bool? IncludeContent { get; set; } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterTaxonomyGroupModel.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterTaxonomyGroupModel.cs new file mode 100644 index 00000000..3a5f7781 --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterTaxonomyGroupModel.cs @@ -0,0 +1,29 @@ +using Kontent.Ai.Management.Models.Shared; +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents the variant filter taxonomy group model. +/// +public class VariantFilterTaxonomyGroupModel +{ + /// + /// Gets or sets the taxonomy reference. + /// + [JsonProperty("taxonomy_identifier")] + public Reference TaxonomyReference { get; set; } + + /// + /// Gets or sets the term references. + /// + [JsonProperty("term_identifiers")] + public IEnumerable TermReferences { get; set; } + + /// + /// Gets or sets whether to include uncategorized items. + /// + [JsonProperty("include_uncategorized")] + public bool IncludeUncategorized { get; set; } +} \ No newline at end of file diff --git a/Kontent.Ai.Management/Models/VariantFilter/VariantFilterWorkflowStepsModel.cs b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterWorkflowStepsModel.cs new file mode 100644 index 00000000..bb4e4e3f --- /dev/null +++ b/Kontent.Ai.Management/Models/VariantFilter/VariantFilterWorkflowStepsModel.cs @@ -0,0 +1,23 @@ +using Kontent.Ai.Management.Models.Shared; +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Kontent.Ai.Management.Models.VariantFilter; + +/// +/// Represents the variant filter workflow steps model. +/// +public class VariantFilterWorkflowStepsModel +{ + /// + /// Gets or sets the workflow reference. + /// + [JsonProperty("workflow_identifier")] + public Reference WorkflowReference { get; set; } + + /// + /// Gets or sets the workflow step references. + /// + [JsonProperty("step_identifiers")] + public IEnumerable WorkflowStepReferences { 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 12a4cbae..0352b661 100644 --- a/Kontent.Ai.Management/Modules/UrlBuilder/EndpointUrlBuilder.cs +++ b/Kontent.Ai.Management/Modules/UrlBuilder/EndpointUrlBuilder.cs @@ -32,6 +32,7 @@ internal sealed class EndpointUrlBuilder private readonly EnvironmentRolesTemplate _environmentRolesTemplate; private readonly UserTemplate _userTemplate; private readonly WebSpotlightTemplate _webSpotlightTemplate; + private readonly VariantFilterTemplate _variantFilterTemplate; private readonly CustomAppTemplate _customAppTemplate; private readonly ManagementOptions _options; @@ -55,6 +56,7 @@ public EndpointUrlBuilder(ManagementOptions options) _environmentRolesTemplate = new EnvironmentRolesTemplate(); _userTemplate = new UserTemplate(); _webSpotlightTemplate = new WebSpotlightTemplate(); + _variantFilterTemplate = new VariantFilterTemplate(); _customAppTemplate = new CustomAppTemplate(); _options = options; @@ -237,6 +239,8 @@ public string BuildSubscriptionUserDeactivateDisableUrl(UserIdentifier identifie public string BuildCustomAppUrl() => GetEnvironmentUrl(_customAppTemplate.Url); + public string BuildVariantFilterUrl() => GetEnvironmentUrl(_variantFilterTemplate.Url); + public string BuildCustomAppUrl(Reference identifier) => GetEnvironmentUrl(string.Concat(_customAppTemplate.GetIdentifierUrlSegment(identifier))); private string GetEnvironmentUrl(string path, params string[] parameters) => GetUrl(BuildEnvironmentUrl(), path, parameters); diff --git a/Kontent.Ai.Management/Modules/UrlBuilder/Templates/VariantFilterTemplate.cs b/Kontent.Ai.Management/Modules/UrlBuilder/Templates/VariantFilterTemplate.cs new file mode 100644 index 00000000..c845c759 --- /dev/null +++ b/Kontent.Ai.Management/Modules/UrlBuilder/Templates/VariantFilterTemplate.cs @@ -0,0 +1,11 @@ +using System; + +namespace Kontent.Ai.Management.Modules.UrlBuilder.Templates; + +internal class VariantFilterTemplate : UrlTemplate +{ + public override string Url => "/early-access/variants/filter"; + public override string UrlId => throw new InvalidOperationException("Variant Filter does not have Id Url."); + public override string UrlCodename => throw new InvalidOperationException("Variant Filter does not have Codename Url."); + public override string UrlExternalId => throw new InvalidOperationException("Variant Filter does not have External Id Url."); +} \ No newline at end of file