-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support for child documents + general API improvements
* KontentConfig makes it a bit easier to work with Statiq Config and Kontent * Child documents can now be axtracted through KontentConfig.GetChildren, issue #21 * Added AddKontentDocumentsToMetadata module to make it even easier to work with releated content * Moved mapping from Kontent models to IDocument into KontentDocumentHelpers
- Loading branch information
Showing
12 changed files
with
340 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
141 changes: 141 additions & 0 deletions
141
Kontent.Statiq.Tests/When_working_with_related_documents.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
using FakeItEasy; | ||
using FluentAssertions; | ||
using Kentico.Kontent.Delivery.Abstractions; | ||
using Kontent.Statiq.Tests.Models; | ||
using Kontent.Statiq.Tests.Tools; | ||
using Statiq.Common; | ||
using Statiq.Core; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace Kontent.Statiq.Tests | ||
{ | ||
public class When_working_with_related_documents | ||
{ | ||
[Fact] | ||
public async Task It_should_map_child_page_collections() | ||
{ | ||
// Arrange | ||
var home = new Home(); | ||
var sub1 = CreateArticle("Sub 1"); | ||
var sub2 = CreateArticle("Sub 2"); | ||
home.Articles = new[] { sub1, sub2 }; | ||
|
||
var deliveryClient = A.Fake<IDeliveryClient>().WithFakeContent(home); | ||
|
||
var sut = new Kontent<Home>(deliveryClient); | ||
|
||
// Act | ||
var engine = SetupExecution(sut, | ||
new[]{ | ||
new AddDocumentsToMetadata(Keys.Children, KontentConfig.GetChildren<Home>( page => page.Articles ) ), | ||
}, | ||
// Assert | ||
async docs => docs.FirstOrDefault().GetChildren().Should().HaveCount(2) | ||
); | ||
await engine.ExecuteAsync(); | ||
} | ||
|
||
[Fact] | ||
public async Task It_should_allow_multiple_child_page_collections() | ||
{ | ||
// Arrange | ||
var home = new Home(); | ||
var sub1 = CreateArticle("Sub 1"); | ||
var sub2 = CreateArticle("Sub 2"); | ||
home.Articles = new[] { sub1, sub2 }; | ||
home.Cafes = new[] | ||
{ | ||
CreateCafe("Ok Café") | ||
}; | ||
|
||
var deliveryClient = A.Fake<IDeliveryClient>().WithFakeContent(home); | ||
|
||
var sut = new Kontent<Home>(deliveryClient); | ||
|
||
// Act | ||
var engine = SetupExecution(sut, | ||
new[]{ | ||
new AddKontentDocumentsToMetadata<Home>(page => page.Articles), | ||
new AddKontentDocumentsToMetadata<Home>("cafes", page => page.Cafes), | ||
}, | ||
|
||
// Assert | ||
async docs => | ||
{ | ||
var articles = docs.FirstOrDefault().GetChildren(); | ||
articles.Should().HaveCount(2); | ||
articles.Should().Contain(a => string.Equals(a[KontentKeys.System.Name], "Sub 1")); | ||
articles.Should().Contain(a => string.Equals(a[KontentKeys.System.Name], "Sub 2")); | ||
var cafes = docs.FirstOrDefault().GetChildren("cafes"); | ||
cafes.Should().HaveCount(1); | ||
cafes.First()[KontentKeys.System.Name].Should().Be("Ok Café"); | ||
}); | ||
await engine.ExecuteAsync(); | ||
} | ||
|
||
[Fact] | ||
public async Task It_should_not_throw_on_null_or_empty_collections() | ||
{ | ||
// Arrange | ||
var home = new Home | ||
{ | ||
Articles = null!, | ||
Cafes = new Cafe[0] | ||
}; | ||
|
||
var deliveryClient = A.Fake<IDeliveryClient>().WithFakeContent(home); | ||
|
||
var sut = new Kontent<Home>(deliveryClient); | ||
|
||
// Act | ||
var engine = SetupExecution(sut, | ||
new[]{ | ||
new AddKontentDocumentsToMetadata<Home>(page => page.Articles), | ||
new AddKontentDocumentsToMetadata<Home>("cafes", page => page.Cafes), | ||
}, | ||
|
||
// Assert | ||
async docs => | ||
{ | ||
var articles = docs.FirstOrDefault().GetChildren(); | ||
articles.Should().HaveCount(0); | ||
var cafes = docs.FirstOrDefault().GetChildren("cafes"); | ||
cafes.Should().HaveCount(0); | ||
}); | ||
await engine.ExecuteAsync(); | ||
} | ||
|
||
private static Article CreateArticle(string content) | ||
{ | ||
var body = new TestRichTextContent {content}; | ||
|
||
return new Article { BodyCopy = body, System = new TestContentItemSystemAttributes{ Name = content } }; | ||
} | ||
|
||
private static Cafe CreateCafe(string name) | ||
{ | ||
return new Cafe { System = new TestContentItemSystemAttributes{ Name = name }}; | ||
} | ||
|
||
private static Engine SetupExecution<TContent>(Kontent<TContent> kontentModule, IModule[] processModules, Func<IReadOnlyList<IDocument>, Task> test) where TContent : class | ||
{ | ||
var engine = new Engine(); | ||
var pipeline = new Pipeline() | ||
{ | ||
InputModules = { kontentModule }, | ||
OutputModules = { new TestModule(test) } | ||
}; | ||
pipeline.ProcessModules.AddRange(processModules); | ||
|
||
engine.Pipelines.Add("test", pipeline); | ||
return engine; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
using Statiq.Common; | ||
using Statiq.Core; | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Kontent.Statiq | ||
{ | ||
/// <summary> | ||
/// Short-hand module for adding Kontent documents as child documents. | ||
/// <para>Use the <see cref="AddDocumentsToMetadata"/> with <see cref="KontentConfig"/> helpers for more control.</para> | ||
/// </summary> | ||
/// <typeparam name="TParent">The content type containing the </typeparam> | ||
public sealed class AddKontentDocumentsToMetadata<TParent> : AddDocumentsToMetadata | ||
{ | ||
/// <summary> | ||
/// Add Kontent documents as the default children collection. | ||
/// </summary> | ||
/// <param name="func">A function that returns the related documents from a page.</param> | ||
public AddKontentDocumentsToMetadata(Func<TParent, IEnumerable<object>> func) | ||
: base(Keys.Children, CreateConfig(func)) | ||
{ | ||
|
||
} | ||
|
||
/// <summary> | ||
/// Add Kontent documents as metadata. | ||
/// </summary> | ||
/// <param name="key">The metadata key to set</param> | ||
/// <param name="func">A function that returns the related documents from a page.</param> | ||
public AddKontentDocumentsToMetadata(string key, Func<TParent, IEnumerable<object>> func ) | ||
: base(key, CreateConfig(func)) | ||
{ | ||
|
||
} | ||
|
||
private static Config<IEnumerable<IDocument>> CreateConfig(Func<TParent, IEnumerable<object>> func) | ||
{ | ||
if (func == null) throw new ArgumentNullException(nameof(func)); | ||
|
||
return KontentConfig.GetChildren(func); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
using Statiq.Common; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace Kontent.Statiq | ||
{ | ||
/// <summary> | ||
/// Kontent specific Config expressions | ||
/// </summary> | ||
public static class KontentConfig | ||
{ | ||
/// <summary> | ||
/// Map related content into a collection of Statiq documents. | ||
/// </summary> | ||
/// <typeparam name="TContentType">The content type.</typeparam> | ||
/// <param name="getChildren">A function that returns a set of Kontent items.</param> | ||
/// <returns>A config object.</returns> | ||
public static Config<IEnumerable<IDocument>> GetChildren<TContentType>(Func<TContentType, IEnumerable<object>> getChildren) | ||
{ | ||
if (getChildren == null) throw new ArgumentNullException(nameof(getChildren)); | ||
|
||
return Config.FromDocument<IEnumerable<IDocument>>( async (doc, ctx) => | ||
{ | ||
var list = new List<IDocument>(); | ||
var parent = doc.AsKontent<TContentType>(); | ||
if (parent != null) | ||
{ | ||
var children = getChildren(parent)?.ToArray() ?? Array.Empty<object>(); | ||
foreach (var item in children) | ||
{ | ||
list.Add(await KontentDocumentHelpers.CreateDocument(ctx, item, null)); | ||
} | ||
} | ||
return list; | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Map a value from a Kontent item. | ||
/// </summary> | ||
/// <typeparam name="TContentType">The Kontent model type.</typeparam> | ||
/// <typeparam name="TValue">The return value.</typeparam> | ||
/// <param name="getValue">A function that retrieves the value from the content.</param> | ||
/// <returns>A config object.</returns> | ||
public static Config<TValue> FromKontent<TContentType, TValue>(Func<TContentType, TValue> getValue) | ||
{ | ||
if (getValue == null) throw new ArgumentNullException(nameof(getValue)); | ||
|
||
return Config.FromDocument((doc, ctx) => getValue(doc.AsKontent<TContentType>())); | ||
} | ||
|
||
/// <summary> | ||
/// Map a document from a Kontent item. | ||
/// </summary> | ||
/// <typeparam name="TContentType">The Kontent model type.</typeparam> | ||
/// <returns>A config object.</returns> | ||
public static Config<TContentType> As<TContentType>() | ||
{ | ||
return Config.FromDocument((doc, ctx) => doc.AsKontent<TContentType>()); | ||
} | ||
} | ||
} |
Oops, something went wrong.