Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
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<FileSystemFixture>
{
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>
{
Reference.ByCodename("article")
},
CompletionStatuses = new List<string> { "completed" }
},
Order = new VariantFilterOrderModel
{
By = "name",
Direction = "asc"
}
};

var response = await client.EarlyAccess.FilterVariantsAsync(request);

response.Should().NotBeNull();
response.Should().BeAssignableTo<IListingResponseModel<VariantFilterItemModel>>();

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<ArgumentNullException>(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<IListingResponseModel<VariantFilterItemModel>>();

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>
{
Reference.ByCodename("article"),
Reference.ByCodename("blog_post")
},
Contributors = new List<UserIdentifier>
{
UserIdentifier.ByEmail("[email protected]")
},
CompletionStatuses = new List<string> { "completed", "unfinished" },
WorkflowSteps = new List<VariantFilterWorkflowStepsModel>
{
new VariantFilterWorkflowStepsModel
{
WorkflowReference = Reference.ByCodename("default"),
WorkflowStepReferences = new List<Reference>
{
Reference.ByCodename("draft")
}
}
},
TaxonomyGroups = new List<VariantFilterTaxonomyGroupModel>
{
new VariantFilterTaxonomyGroupModel
{
TaxonomyReference = Reference.ByCodename("categories"),
TermReferences = new List<Reference>
{
Reference.ByCodename("tech")
},
IncludeUncategorized = false
}
}
},
Order = new VariantFilterOrderModel
{
By = "last_modified",
Direction = "desc"
}
};

var response = await client.EarlyAccess.FilterVariantsAsync(request);

response.Should().NotBeNull();
response.Should().BeAssignableTo<IListingResponseModel<VariantFilterItemModel>>();

var firstItem = response.ToList().First();
firstItem.Item.Should().NotBeNull();
firstItem.Item.Id.Should().NotBeEmpty();
firstItem.Item.Name.Should().NotBeNullOrEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -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");
}
}
7 changes: 7 additions & 0 deletions Kontent.Ai.Management/IManagementClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ namespace Kontent.Ai.Management;
/// </summary>
public interface IManagementClient
{
/// <summary>
/// Gets the early access client for experimental features.
/// These features may change or be removed in future versions.
/// </summary>
IManagementClientEarlyAccess EarlyAccess { get; }

/// <summary>
/// Returns asset.
/// </summary>
Expand Down Expand Up @@ -872,4 +878,5 @@ public interface IManagementClient
/// </summary>
/// <returns>The <see cref="CustomAppModel"/> instance that represents the custom app.</returns>
Task<CustomAppModel> ModifyCustomAppAsync(Reference identifier, IEnumerable<CustomAppOperationBaseModel> changes);

}
20 changes: 20 additions & 0 deletions Kontent.Ai.Management/IManagementClientEarlyAccess.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Kontent.Ai.Management.Models.Shared;
using Kontent.Ai.Management.Models.VariantFilter;
using System.Threading.Tasks;

namespace Kontent.Ai.Management;

/// <summary>
/// Represents a set of early access Content Management API requests.
/// These features are experimental and may change or be removed in future versions.
/// </summary>
public interface IManagementClientEarlyAccess
{
/// <summary>
/// Returns listing of filtered variants.
/// This is an early access feature that may change or be removed in future versions.
/// </summary>
/// <param name="variantFilterRequest">The variant filter request containing filters and ordering options.</param>
/// <returns>The <see cref="IListingResponseModel{VariantFilterItemModel}"/> instance representing the filtered variants.</returns>
Task<IListingResponseModel<VariantFilterItemModel>> FilterVariantsAsync(VariantFilterRequestModel variantFilterRequest);
}
9 changes: 9 additions & 0 deletions Kontent.Ai.Management/ManagementClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ManagementClientEarlyAccess> _earlyAccess;

/// <summary>
/// Initializes a new instance of the <see cref="ManagementClient"/> class for managing content of the specified environment.
Expand Down Expand Up @@ -62,15 +63,23 @@ 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<ManagementClientEarlyAccess>(() => new ManagementClientEarlyAccess(_actionInvoker, _urlBuilder));
}

internal ManagementClient(EndpointUrlBuilder urlBuilder, ActionInvoker actionInvoker, IModelProvider modelProvider = null)
{
_urlBuilder = urlBuilder ?? throw new ArgumentNullException(nameof(urlBuilder));
_actionInvoker = actionInvoker ?? throw new ArgumentNullException(nameof(actionInvoker));
_modelProvider = modelProvider ?? new ModelProvider();
_earlyAccess = new Lazy<ManagementClientEarlyAccess>(() => new ManagementClientEarlyAccess(_actionInvoker, _urlBuilder));
}

/// <summary>
/// Gets the early access client for experimental features.
/// These features may change or be removed in future versions.
/// </summary>
public IManagementClientEarlyAccess EarlyAccess => _earlyAccess.Value;

private async Task<IListingResponse<TModel>> GetNextListingPageAsync<TListingResponse, TModel>(string continuationToken, string url)
where TListingResponse : IListingResponse<TModel>
{
Expand Down
53 changes: 53 additions & 0 deletions Kontent.Ai.Management/ManagementClientEarlyAccess.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Provides access to early access Content Management API features.
/// These features are experimental and may change or be removed in future versions.
/// </summary>
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));
}

/// <inheritdoc />
public async Task<IListingResponseModel<VariantFilterItemModel>> FilterVariantsAsync(VariantFilterRequestModel variantFilterRequest)
{
ArgumentNullException.ThrowIfNull(variantFilterRequest);

var endpointUrl = _urlBuilder.BuildVariantFilterUrl();
var response = await _actionInvoker.InvokeMethodAsync<VariantFilterRequestModel, VariantFilterListingResponseServerModel>(endpointUrl, HttpMethod.Post, variantFilterRequest);

return new ListingResponseModel<VariantFilterItemModel>(
GetNextListingPageAsync<VariantFilterListingResponseServerModel, VariantFilterItemModel>,
response.Pagination?.Token,
endpointUrl,
response.Data);
}

private async Task<IListingResponse<TModel>> GetNextListingPageAsync<TListingResponse, TModel>(string continuationToken, string url)
where TListingResponse : IListingResponse<TModel>
{
var headers = new Dictionary<string, string>
{
{ "x-continuation", continuationToken }
};
var response = await _actionInvoker.InvokeReadOnlyMethodAsync<TListingResponse>(url, HttpMethod.Get, headers);

return response;
}
}
Loading
Loading