Skip to content

Commit

Permalink
Add used-in support
Browse files Browse the repository at this point in the history
  • Loading branch information
winklertomas committed Feb 28, 2025
1 parent 479011a commit 7635ea7
Show file tree
Hide file tree
Showing 21 changed files with 942 additions and 15 deletions.
27 changes: 26 additions & 1 deletion Kontent.Ai.Delivery.Abstractions/DeliveryClientExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Kontent.Ai.Delivery.Abstractions
{
Expand Down Expand Up @@ -87,5 +88,29 @@ public static Task<IDeliverySyncInitResponse> PostSyncInitAsync(this IDeliveryCl
{
return client.PostSyncInitAsync(parameters);
}

/// <summary>
/// Returns a feed that is used to traverse through strongly typed parent content items matching the optional filtering parameters.
/// </summary>
/// <param name="client">An instance of the <see cref="IDeliveryClient"/></param>
/// <param name="codename">The codename of a content item.</param>
/// <param name="parameters">An array that contains zero or more query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IDeliveryItemsFeed{IUsedInItem}"/> instance that can be used to enumerate through content item parents for the specified item codename. If no query parameters are specified, default language parents are enumerated.</returns>
public static IDeliveryItemsFeed<IUsedInItem> GetItemUsedIn(this IDeliveryClient client, string codename, params IQueryParameter[] parameters)
{
return client.GetItemUsedIn(codename, parameters);
}

/// <summary>
/// Returns a feed that is used to traverse through strongly typed parent content items matching the optional filtering parameters.
/// </summary>
/// <param name="client">An instance of the <see cref="IDeliveryClient"/></param>
/// <param name="codename">The codename of an asset.</param>
/// <param name="parameters">An array that contains zero or more query parameters, for example, for filtering, or ordering.</param>
/// <returns>The <see cref="IDeliveryItemsFeed{IUsedInItem}"/> instance that can be used to enumerate through asset parents for the specified asset codename. If no query parameters are specified, default language parents are enumerated.</returns>
public static IDeliveryItemsFeed<IUsedInItem> GetAssetUsedIn(this IDeliveryClient client, string codename, params IQueryParameter[] parameters)
{
return client.GetAssetUsedIn(codename, parameters);
}
}
}
16 changes: 16 additions & 0 deletions Kontent.Ai.Delivery.Abstractions/IDeliveryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,21 @@ public interface IDeliveryClient
/// </summary>
/// <returns>The <see cref="IDeliverySyncResponse"/> instance that represents the sync response that contains collection of delta updates and continuation token needed for further sync execution.</returns>
Task<IDeliverySyncResponse> GetSyncAsync(string continuationToken);

/// <summary>
/// Returns a feed that is used to traverse through strongly typed parent content items matching the optional filtering parameters.
/// </summary>
/// <param name="codename">The codename of a content item.</param>
/// <param name="parameters">A collection of query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IDeliveryItemsFeed{IUsedInItem}"/> instance that can be used to enumerate through content item parents for the specified item codename. If no query parameters are specified, default language parents are enumerated.</returns>
public IDeliveryItemsFeed<IUsedInItem> GetItemUsedIn(string codename, IEnumerable<IQueryParameter> parameters = null);

/// <summary>
/// Returns a feed that is used to traverse through strongly typed parent content items matching the optional filtering parameters.
/// </summary>
/// <param name="codename">The codename of an asset.</param>
/// <param name="parameters">A collection of query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IDeliveryItemsFeed{IUsedInItem}"/> instance that can be used to enumerate through asset parents for the specified asset codename. If no query parameters are specified, default language parents are enumerated.</returns>
public IDeliveryItemsFeed<IUsedInItem> GetAssetUsedIn(string codename, IEnumerable<IQueryParameter> parameters = null);
}
}
14 changes: 14 additions & 0 deletions Kontent.Ai.Delivery.Abstractions/UsedIn/IUsedInItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace Kontent.Ai.Delivery.Abstractions;

/// <summary>
/// Represents a parent content item.
/// </summary>
public interface IUsedInItem
{
/// <summary>
/// Represents system attributes of a parent content item.
/// </summary>
public IUsedInItemSystemAttributes System { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Kontent.Ai.Delivery.Abstractions
{
/// <summary>
/// Represents system attributes of a parent content item.
/// </summary>
public interface IUsedInItemSystemAttributes : ISystemAttributes
{
/// <summary>
/// Gets the language of the content item.
/// </summary>
string Language { get; }

/// <summary>
/// Gets the codename of the content type, for example "article".
/// </summary>
string Type { get; }

/// <summary>
/// Gets the codename of the content collection to which the content item belongs.
/// </summary>
public string Collection { get; }

/// <summary>
/// Gets the codename of the workflow which the content item is assigned to.
/// </summary>
public string Workflow { get; }

/// <summary>
/// Gets the codename of the workflow step which the content item is assigned to.
/// </summary>
public string WorkflowStep { get; }
}
}
22 changes: 22 additions & 0 deletions Kontent.Ai.Delivery.Caching/DeliveryClientCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,5 +175,27 @@ public Task<IDeliverySyncResponse> GetSyncAsync(string continuationToken)
{
return _deliveryClient.GetSyncAsync(continuationToken);
}

/// <summary>
/// Returns a feed that is used to traverse through strongly typed parent content items matching the optional filtering parameters.
/// </summary>
/// <param name="codename">The codename of a content item.</param>
/// <param name="parameters">A collection of query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IDeliveryItemsFeed{IUsedInItem}"/> instance that can be used to enumerate through content item parents for the specified item codename. If no query parameters are specified, default language parents are enumerated.</returns>
public IDeliveryItemsFeed<IUsedInItem> GetItemUsedIn(string codename, IEnumerable<IQueryParameter> parameters = null)
{
return _deliveryClient.GetItemUsedIn(codename, parameters);
}

/// <summary>
/// Returns a feed that is used to traverse through strongly typed parent content items matching the optional filtering parameters.
/// </summary>
/// <param name="codename">The codename of an asset.</param>
/// <param name="parameters">A collection of query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IDeliveryItemsFeed{IUsedInItem}"/> instance that can be used to enumerate through asset parents for the specified asset codename. If no query parameters are specified, default language parents are enumerated.</returns>
public IDeliveryItemsFeed<IUsedInItem> GetAssetUsedIn(string codename, IEnumerable<IQueryParameter> parameters = null)
{
return _deliveryClient.GetAssetUsedIn(codename, parameters);
}
}
}
34 changes: 34 additions & 0 deletions Kontent.Ai.Delivery.Rx.Tests/DeliveryObservableProxyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace Kontent.Ai.Delivery.Rx.Tests
public class DeliveryObservableProxyTests
{
private const string BEVERAGES_IDENTIFIER = "coffee_beverages_explained";
private const string ASSET_CODENAME = "asset_codename";
readonly string _guid;
readonly string _baseUrl;
readonly MockHttpMessageHandler _mockHttp;
Expand Down Expand Up @@ -180,6 +181,27 @@ public void LanguagesRetrieved()
Assert.NotEmpty(languages);
Assert.All(languages, language => Assert.NotNull(language.System));
}

[Fact]
public void ItemUsedInRetrieved()
{
var observable = new DeliveryObservableProxy(GetDeliveryClient(MockItemUsedIn)).GetItemUsedInObservable(Article.Codename);
var parents = observable.ToEnumerable().ToList();

Assert.NotEmpty(parents);
Assert.All(parents, item => Assert.NotNull(item.System));
}

[Fact]
public void AssetUsedInRetrieved()
{
var observable = new DeliveryObservableProxy(GetDeliveryClient(MockAssetUsedIn)).GetAssetUsedInObservable(ASSET_CODENAME);
var parents = observable.ToEnumerable().ToList();

Assert.NotEmpty(parents);
Assert.All(parents, item => Assert.NotNull(item.System));
}

public static IOptionsMonitor<DeliveryOptions> CreateMonitor(DeliveryOptions options)
{
var mock = A.Fake<IOptionsMonitor<DeliveryOptions>>();
Expand Down Expand Up @@ -286,6 +308,18 @@ private void MockLanguages()
.Respond("application/json", File.ReadAllText(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}languages.json")));
}

private void MockAssetUsedIn()
{
_mockHttp.When($"{_baseUrl}/assets/{ASSET_CODENAME}/used-in")
.Respond("application/json", File.ReadAllText(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}used_in.json")));
}

private void MockItemUsedIn()
{
_mockHttp.When($"{_baseUrl}/items/{Article.Codename}/used-in")
.Respond("application/json", File.ReadAllText(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}used_in.json")));
}

private static void AssertArticlePropertiesNotNull(Article item)
{
Assert.NotNull(item.System);
Expand Down
30 changes: 30 additions & 0 deletions Kontent.Ai.Delivery.Rx.Tests/Fixtures/used_in.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"items": [
{
"system": {
"id": "35778b62-e97f-42cb-833f-8a34c14e27ce",
"name": "rich text child",
"codename": "rich_text_child",
"language": "language1",
"type": "ct",
"collection": "default",
"workflow": "default",
"workflow_step": "published",
"last_modified": "2024-08-01T14:17:50.4141347Z"
}
},
{
"system": {
"id": "3ad63df6-a439-4a64-8193-14e999bfaaf2",
"name": "rich text child 2",
"codename": "rich_text_child_2",
"language": "language1",
"type": "ct",
"collection": "default",
"workflow": "default",
"workflow_step": "published",
"last_modified": "2024-08-01T14:17:50.4141347Z"
}
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,10 @@
<ProjectReference Include="..\Kontent.Ai.Delivery.Rx\Kontent.Ai.Delivery.Rx.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="Fixtures\used_in.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
67 changes: 56 additions & 11 deletions Kontent.Ai.Delivery.Rx/DeliveryObservableProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,53 @@ public IObservable<T> GetItemsFeedObservable<T>(params IQueryParameter[] paramet
public IObservable<T> GetItemsFeedObservable<T>(IEnumerable<IQueryParameter> parameters) where T : class
{
var feed = DeliveryClient?.GetItemsFeed<T>(parameters);
return feed == null ? null : EnumerateFeed()?.ToObservable();
return feed == null ? null : EnumerateFeed(feed)?.ToObservable();
}

IEnumerable<T> EnumerateFeed()
{
while (feed.HasMoreResults)
{
foreach (var contentItem in feed.FetchNextBatchAsync().Result.Items)
{
yield return contentItem;
}
}
}
/// <summary>
/// Returns an observable of strongly typed parent content items for specified content item that match the optional filtering parameters. Items are enumerated in batches.
/// </summary>
/// <param name="codename">The codename of a content item.</param>
/// <param name="parameters">A collection of query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IObservable{T}"/> that represents the parent content items for the specified content item. If no query parameters are specified, parents in default language are returned.</returns>
public IObservable<IUsedInItem> GetItemUsedInObservable(string codename, params IQueryParameter[] parameters)
{
return GetItemUsedInObservable(codename, (IEnumerable<IQueryParameter>)parameters);
}

/// <summary>
/// Returns an observable of strongly typed parent content items for specified content item that match the optional filtering parameters. Items are enumerated in batches.
/// </summary>
/// <param name="codename">The codename of a content item.</param>
/// <param name="parameters">A collection of query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IObservable{IUsedInItem}"/> that represents the parent content items for the specified content item. If no query parameters are specified, parents in default language are returned.</returns>
public IObservable<IUsedInItem> GetItemUsedInObservable(string codename, IEnumerable<IQueryParameter> parameters)
{
var feed = DeliveryClient?.GetItemUsedIn(codename, parameters);
return feed == null ? null : EnumerateFeed(feed)?.ToObservable();
}

/// <summary>
/// Returns an observable of strongly typed parent content items for specified asset that match the optional filtering parameters. Items are enumerated in batches.
/// </summary>
/// <param name="codename">The codename of an asset.</param>
/// <param name="parameters">A collection of query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IObservable{T}"/> that represents the parent content items for the specified content item. If no query parameters are specified, parents in default language are returned.</returns>
public IObservable<IUsedInItem> GetAssetUsedInObservable(string codename, params IQueryParameter[] parameters)
{
return GetAssetUsedInObservable(codename, (IEnumerable<IQueryParameter>)parameters);
}

/// <summary>
/// Returns an observable of strongly typed parent content items for specified asset that match the optional filtering parameters. Items are enumerated in batches.
/// </summary>
/// <param name="codename">The codename of an asset.</param>
/// <param name="parameters">A collection of query parameters, for example, for filtering or ordering.</param>
/// <returns>The <see cref="IObservable{IUsedInItem}"/> that represents the parent content items for the specified content item. If no query parameters are specified, parents in default language are returned.</returns>
public IObservable<IUsedInItem> GetAssetUsedInObservable(string codename, IEnumerable<IQueryParameter> parameters)
{
var feed = DeliveryClient?.GetAssetUsedIn(codename, parameters);
return feed == null ? null : EnumerateFeed(feed)?.ToObservable();
}

/// <summary>
Expand Down Expand Up @@ -211,6 +246,16 @@ public IObservable<ILanguage> GetLanguagesObservable(params IQueryParameter[] pa

#endregion
#region "Private methods"
private static IEnumerable<T> EnumerateFeed<T>(IDeliveryItemsFeed<T> feed) where T : class
{
while (feed.HasMoreResults)
{
foreach (var contentItem in feed.FetchNextBatchAsync().Result.Items)
{
yield return contentItem;
}
}
}

private static IObservable<T> GetObservableOfOne<T>(Func<T> responseFactory)
{
Expand Down
Loading

0 comments on commit 7635ea7

Please sign in to comment.