Files
Umbraco-CMS/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/OutputExpansionStrategyTests.cs
Bjarke Berg c06e89af64 Content Delivery API (#14051)
* Add the core parts of the headless PoC

* Add Content API project (WIP - loads of TODOs and dupes that need to be fixed!)

* Rename the content API project and namespaces

* Fixed bad merge

* Rename everything "Headless" to "ContentApi" or "Api"

* Refactor Content + Media: Key => Id, Name not nullable

* Make Content API property return value types independent of datatype configuration

* Clean up refactorings

* First stab at an expansion strategy using content picker as example implementation

* Use named JSON options for content API serialization

* Proper inclusion and registration of the content API

* Introduce API media builder

* Make MNTP return API content/media depending on configuration (instead of links) and support output expansion

* Content API: Get by controllers (#13740)

* Adding ContentApiControllerBase

* Adding get by id and url controllers

* Change route of get all test controller

* Rename to ContentApiController

* Refactoring

* Removing test controller

* Content API: Add start-node header value to deal with url collisions (#13746)

* Use start-node header value to deal with url collisions

* Cleanup

* Rename "url" param to "path"

* Adding a start node service to get the start-node header value

* Trim '/' from both beginning and end

* Content API: Support Accept-Language header (#13831)

* Move the content API JSON type resolver to an appropriate namespace

* Add localization based on Accept-Language header

* Content API: Output expansion  (#13848)

* Implement request based output expansion strategy + expansion output cache at property level

* Slighty leaner implementation for default output expansion strategy

* Clarify the code a bit

* Fix bad merge

* Encapsulate content API dependencies in the DI

* Support multi-site and multi-culture routing + a little rename/refactor (#13882)

* Support multi-site and multi-culture routing + a little rename/refactor

* Make the by route controller handle root nodes

* Rename Url to Path in API content output

* Add a few comments for magic route creation

* Rename services from "Default" to "Noop"

* Ensure that Umbraco can boot without adding "AddContentApi()" to ConfigureServices

* Moved incorrectly placed media builder

* Fix API routes (#13915)

* Fix multi URL picker value converter trying to access disposed objects in edge cases

* Delivery API: Content routing and structure (#13984)

* Introduce content route instead of content path, rename and rework start item (previously start node) handling

* Strip out start node path in generated route path

* Make the start-item header take precedence over the request domain

* Conditionally enabling the delivery API + add protection and preview support + refactor all services to be singletons + ensure no-op implementations for all required services (#13992)

* Include umbraco properties (width, height, ...) in the Media Properties collection (#14023)

* Move umbraco properties (width, height, ...) to the Properties collection of the API Media model

* Don't output the umbracoFile property of media items

* Add content type deny list (#14025)

* Create a deny list of content types and utilize it for output generation

* Add unit tests

* Dedicated property cache level for Content API (#14027)

* Support redirect tracking (#14033)

* Create a deny list of content types and utilize it for output generation

* Add unit tests

* Handle redirect tracking in the content API

* Include start item routing info for redirects

* Add cultures and their routes to the API output (#14038)

* Create a deny list of content types and utilize it for output generation

* Add unit tests

* Handle redirect tracking in the content API

* Include start item routing info for redirects

* Add culture routes to root output (for HREFLANG support)

* Rename redirect service method to better match its purpose

* Review changes

* Delivery API: Query controller (#14041)

* Initial commit

* Custom ContentAPIFieldDefinitionCollection

* Make index IUmbracoContentIndex

* Add querying for children by parent id (key)

* Add missing interface

* Adding querying endpoint

* Test code

* Compose unpublishedValueSet, so that you get the correct data in the ContentAPI index

* Renaming

* Fix ancestorKeys index values

* Adding IApiQueryExtensionService to be able to query the ContentAPI index in a generic way

* Fix IApiQueryService and clean up QueryContentApiController using it

* Support querying for path

* Fix content API indexing

* Fix default sorting

* Implement concrete QueryOption implementations

* Introduce new ExecuteQuery that uses the Core OptionHandlers

* Implement ExecuteQuery

* Change ExecuteQuery signature and implementation

* Implement demo sorting and fetching

* Add query option handlers and collection builder for them

* Cleanup

* Revert "Conditionally enabling the delivery API + add protection and preview support + refactor all services to be singletons + ensure no-op implementations for all required services (#13992)"

This reverts commit 78e1f748e55383baecd123d06457111e18f13365.

* Revert "Delivery API: Content routing and structure (#13984)"

This reverts commit a0292ae5350362dd6c1c5bc9763deda928c78a75.

* Revert "Fix multi URL picker value converter trying to access disposed objects in edge cases"

This reverts commit 6b7c37a5bf7871bee93a2b2640bbc6ef591f14db.

* Revert "Conditionally enabling the delivery API + add protection and preview support + refactor all services to be singletons + ensure no-op implementations for all required services (#13992)"

This reverts commit 78e1f748e55383baecd123d06457111e18f13365.

* Revert "Delivery API: Content routing and structure (#13984)"

This reverts commit a0292ae5350362dd6c1c5bc9763deda928c78a75.

* Revert "Fix multi URL picker value converter trying to access disposed objects in edge cases"

This reverts commit 6b7c37a5bf7871bee93a2b2640bbc6ef591f14db.

* Fix multi URL picker value converter trying to access disposed objects in edge cases

* Delivery API: Content routing and structure (#13984)

* Introduce content route instead of content path, rename and rework start item (previously start node) handling

* Strip out start node path in generated route path

* Make the start-item header take precedence over the request domain

* Conditionally enabling the delivery API + add protection and preview support + refactor all services to be singletons + ensure no-op implementations for all required services (#13992)

* Test commit

* Refactored interfaces for the query handlers and for the selectors (that will handle the value of the fetch query option)

* Implemented a base class for the query options

* Refactored the names of the selectors and made use of the base class

* Refactored the ApiQueryService

* Refactored the QueryContentApiController.cs

* Conditionally enabling the delivery API + add protection and preview support + refactor all services to be singletons + ensure no-op implementations for all required services (#13992)

* Fixing merge gone wrong

* Fix multi URL picker value converter trying to access disposed objects in edge cases

* Delivery API: Content routing and structure (#13984)

* Introduce content route instead of content path, rename and rework start item (previously start node) handling

* Strip out start node path in generated route path

* Make the start-item header take precedence over the request domain

* Conditionally enabling the delivery API + add protection and preview support + refactor all services to be singletons + ensure no-op implementations for all required services (#13992)

* Make fetching work with the new setup

* Moving files to dedicated folders

* Removing ? for array

* Rename selector query method

* Implement FilterHandler and some filters

* Implement SortHandler and sort some sorts

* Refactoring

* Adding more fields to index due to querying

* Appending filtering and sorting queries

* Implementing a new ISelectorHandler without Examine types

* Re-implementing the collection to have a dedicated one for the selectors

* Implementing a new IFilterHandler without Examine types & refactoring the filters implementing it

* Adding a new collection dedicated to filters

* Renaming the old collection

* Implementing a new ISortHandler without Examine types & refactoring the sorts implementing it

* Adding a new collection for the sorts & adding all collections to UmbracoBuilder.Collections

* Refactoring the service to use the new collections and types

* Refactoring the fields in ContentApiFieldDefinitionCollection

* Remove nullability in Handlers

* Don't return null for selector

* Add TODO for having the filters support negation

* Changing the SortType to FieldType with our custom types on the SortOption

* Fix AncestorsSelector

* Fix ApiQueryService

* Documentation

* Fix Swagger docs

* Refactor the QueryContentApiController

* Adding handling for the IApiContentResponse in the JsonTypeResolver

* Refactor the service to use a safe fallback value in Examine queries

* Adding Noop for the IApiQueryService

* Cleanup

* Remove comment

* Fix name field for indexing

* Don't inherit QueryOptionBase in filters

* Fix casing for API index constant + swap FIXME with TODO

* Add TODO for handling missing fetch with start-item header

* Rename query handler parameters to not leak source (i.e. query string)

---------

Co-authored-by: kjac <kja@umbraco.dk>
Co-authored-by: Elitsa <>
Co-authored-by: Zeegaan <nge@umbraco.dk>

* Delivery API: Adding pagination to query endpoint (#14083)

* Adding pagination to query endpoint

* Optimize the paging using Examine directly

* Fix comment

* Remove skip/take code duplication

---------

Co-authored-by: kjac <kja@umbraco.dk>

* Add missing CompatibilitySuppressions.xml

* Make Delivery API packable

* Make Api.Common packable

* Renamed extension method and namespace so it is discoverable

* Untangle ApiVersion configuration into api.common, so delivery api do not require the management api to boot.

* configure options in management api

* RTE output as JSON for Content API (#14067)

* Conditionally serve RTE output as JSON instead of HTML

* Fixed merge

* Rename to Delivery API (#14119)

* Rename ContentApi to DeliveryApi

* Rename delivery API index implementation

* Update comments from "Content API" to "Delivery API"

* Rename project from Content to Delivery

* Add dedicated controller base for content delivery API

* Rename delivery API content index to include "content" specifically

* Fix compat suppressions

---------

Co-authored-by: kjac <kja@umbraco.dk>
Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com>
Co-authored-by: Zeegaan <nge@umbraco.dk>
2023-04-19 11:21:31 +02:00

445 lines
22 KiB
C#

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Api.Delivery.Rendering;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.Models.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.PropertyEditors.DeliveryApi;
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
using Umbraco.Cms.Core.PublishedCache;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.DeliveryApi;
[TestFixture]
public class OutputExpansionStrategyTests : PropertyValueConverterTests
{
private IPublishedContentType _contentType;
private IPublishedContentType _elementType;
[SetUp]
public void SetUp()
{
var contentType = new Mock<IPublishedContentType>();
contentType.SetupGet(c => c.Alias).Returns("thePageType");
contentType.SetupGet(c => c.ItemType).Returns(PublishedItemType.Content);
_contentType = contentType.Object;
var elementType = new Mock<IPublishedContentType>();
elementType.SetupGet(c => c.Alias).Returns("theElementType");
elementType.SetupGet(c => c.ItemType).Returns(PublishedItemType.Element);
_elementType = elementType.Object;
}
[Test]
public void OutputExpansionStrategy_ExpandsNothingByDefault()
{
var accessor = CreateOutputExpansionStrategyAccessor();
var apiContentBuilder = new ApiContentBuilder(new ApiContentNameProvider(), ApiContentRouteBuilder(), accessor);
var content = new Mock<IPublishedContent>();
var prop1 = new PublishedElementPropertyBase(DeliveryApiPropertyType, content.Object, false, PropertyCacheLevel.None);
var prop2 = new PublishedElementPropertyBase(DefaultPropertyType, content.Object, false, PropertyCacheLevel.None);
var contentPickerContent = CreateSimplePickedContent(123, 456);
var contentPickerProperty = CreateContentPickerProperty(content.Object, contentPickerContent.Key, "contentPicker", apiContentBuilder);
SetupContentMock(content, prop1, prop2, contentPickerProperty);
var result = apiContentBuilder.Build(content.Object);
Assert.AreEqual(3, result.Properties.Count);
Assert.AreEqual("Delivery API value", result.Properties[DeliveryApiPropertyType.Alias]);
Assert.AreEqual("Default value", result.Properties[DefaultPropertyType.Alias]);
var contentPickerOutput = result.Properties["contentPicker"] as ApiContent;
Assert.IsNotNull(contentPickerOutput);
Assert.AreEqual(contentPickerContent.Key, contentPickerOutput.Id);
Assert.IsEmpty(contentPickerOutput.Properties);
}
[Test]
public void OutputExpansionStrategy_CanExpandSpecificContent()
{
var accessor = CreateOutputExpansionStrategyAccessor(false, new[] { "contentPickerTwo" });
var apiContentBuilder = new ApiContentBuilder(new ApiContentNameProvider(), ApiContentRouteBuilder(), accessor);
var content = new Mock<IPublishedContent>();
var contentPickerOneContent = CreateSimplePickedContent(12, 34);
var contentPickerOneProperty = CreateContentPickerProperty(content.Object, contentPickerOneContent.Key, "contentPickerOne", apiContentBuilder);
var contentPickerTwoContent = CreateSimplePickedContent(56, 78);
var contentPickerTwoProperty = CreateContentPickerProperty(content.Object, contentPickerTwoContent.Key, "contentPickerTwo", apiContentBuilder);
SetupContentMock(content, contentPickerOneProperty, contentPickerTwoProperty);
var result = apiContentBuilder.Build(content.Object);
Assert.AreEqual(2, result.Properties.Count);
var contentPickerOneOutput = result.Properties["contentPickerOne"] as ApiContent;
Assert.IsNotNull(contentPickerOneOutput);
Assert.AreEqual(contentPickerOneContent.Key, contentPickerOneOutput.Id);
Assert.IsEmpty(contentPickerOneOutput.Properties);
var contentPickerTwoOutput = result.Properties["contentPickerTwo"] as ApiContent;
Assert.IsNotNull(contentPickerTwoOutput);
Assert.AreEqual(contentPickerTwoContent.Key, contentPickerTwoOutput.Id);
Assert.AreEqual(2, contentPickerTwoOutput.Properties.Count);
Assert.AreEqual(56, contentPickerTwoOutput.Properties["numberOne"]);
Assert.AreEqual(78, contentPickerTwoOutput.Properties["numberTwo"]);
}
[Test]
public void OutputExpansionStrategy_CanExpandAllContent()
{
var accessor = CreateOutputExpansionStrategyAccessor(true);
var apiContentBuilder = new ApiContentBuilder(new ApiContentNameProvider(), ApiContentRouteBuilder(), accessor);
var content = new Mock<IPublishedContent>();
var contentPickerOneContent = CreateSimplePickedContent(12, 34);
var contentPickerOneProperty = CreateContentPickerProperty(content.Object, contentPickerOneContent.Key, "contentPickerOne", apiContentBuilder);
var contentPickerTwoContent = CreateSimplePickedContent(56, 78);
var contentPickerTwoProperty = CreateContentPickerProperty(content.Object, contentPickerTwoContent.Key, "contentPickerTwo", apiContentBuilder);
SetupContentMock(content, contentPickerOneProperty, contentPickerTwoProperty);
var result = apiContentBuilder.Build(content.Object);
Assert.AreEqual(2, result.Properties.Count);
var contentPickerOneOutput = result.Properties["contentPickerOne"] as ApiContent;
Assert.IsNotNull(contentPickerOneOutput);
Assert.AreEqual(contentPickerOneContent.Key, contentPickerOneOutput.Id);
Assert.AreEqual(2, contentPickerOneOutput.Properties.Count);
Assert.AreEqual(12, contentPickerOneOutput.Properties["numberOne"]);
Assert.AreEqual(34, contentPickerOneOutput.Properties["numberTwo"]);
var contentPickerTwoOutput = result.Properties["contentPickerTwo"] as ApiContent;
Assert.IsNotNull(contentPickerTwoOutput);
Assert.AreEqual(contentPickerTwoContent.Key, contentPickerTwoOutput.Id);
Assert.AreEqual(2, contentPickerTwoOutput.Properties.Count);
Assert.AreEqual(56, contentPickerTwoOutput.Properties["numberOne"]);
Assert.AreEqual(78, contentPickerTwoOutput.Properties["numberTwo"]);
}
[TestCase("contentPicker", "contentPicker")]
[TestCase("rootPicker", "nestedPicker")]
public void OutputExpansionStrategy_DoesNotExpandNestedContentPicker(string rootPropertyTypeAlias, string nestedPropertyTypeAlias)
{
var accessor = CreateOutputExpansionStrategyAccessor(false, new[] { rootPropertyTypeAlias, nestedPropertyTypeAlias });
var apiContentBuilder = new ApiContentBuilder(new ApiContentNameProvider(), ApiContentRouteBuilder(), accessor);
var content = new Mock<IPublishedContent>();
var nestedContentPickerContent = CreateSimplePickedContent(987, 654);
var contentPickerContent = CreateMultiLevelPickedContent(123, nestedContentPickerContent, nestedPropertyTypeAlias, apiContentBuilder);
var contentPickerContentProperty = CreateContentPickerProperty(content.Object, contentPickerContent.Key, rootPropertyTypeAlias, apiContentBuilder);
SetupContentMock(content, contentPickerContentProperty);
var result = apiContentBuilder.Build(content.Object);
Assert.AreEqual(1, result.Properties.Count);
var contentPickerOneOutput = result.Properties[rootPropertyTypeAlias] as ApiContent;
Assert.IsNotNull(contentPickerOneOutput);
Assert.AreEqual(contentPickerContent.Key, contentPickerOneOutput.Id);
Assert.AreEqual(2, contentPickerOneOutput.Properties.Count);
Assert.AreEqual(123, contentPickerOneOutput.Properties["number"]);
var nestedContentPickerOutput = contentPickerOneOutput.Properties[nestedPropertyTypeAlias] as ApiContent;
Assert.IsNotNull(nestedContentPickerOutput);
Assert.AreEqual(nestedContentPickerContent.Key, nestedContentPickerOutput.Id);
Assert.IsEmpty(nestedContentPickerOutput.Properties);
}
[Test]
public void OutputExpansionStrategy_DoesNotExpandElementsByDefault()
{
var accessor = CreateOutputExpansionStrategyAccessor();
var apiContentBuilder = new ApiContentBuilder(new ApiContentNameProvider(), ApiContentRouteBuilder(), accessor);
var apiElementBuilder = new ApiElementBuilder(accessor);
var contentPickerValue = CreateSimplePickedContent(111, 222);
var contentPicker2Value = CreateSimplePickedContent(666, 777);
var content = new Mock<IPublishedContent>();
SetupContentMock(
content,
CreateNumberProperty(content.Object, 444, "number"),
CreateElementProperty(content.Object, "element", 333, contentPickerValue.Key, "contentPicker", apiContentBuilder, apiElementBuilder),
CreateElementProperty(content.Object, "element2", 555, contentPicker2Value.Key, "contentPicker", apiContentBuilder, apiElementBuilder));
var result = apiContentBuilder.Build(content.Object);
Assert.AreEqual(3, result.Properties.Count);
Assert.AreEqual(444, result.Properties["number"]);
var expectedElementOutputs = new[]
{
new
{
PropertyAlias = "element",
ElementNumber = 333,
ElementContentPicker = contentPickerValue.Key
},
new
{
PropertyAlias = "element2",
ElementNumber = 555,
ElementContentPicker = contentPicker2Value.Key
}
};
foreach (var expectedElementOutput in expectedElementOutputs)
{
var elementOutput = result.Properties[expectedElementOutput.PropertyAlias] as IApiElement;
Assert.IsNotNull(elementOutput);
Assert.AreEqual(2, elementOutput.Properties.Count);
Assert.AreEqual(expectedElementOutput.ElementNumber, elementOutput.Properties["number"]);
var contentPickerOutput = elementOutput.Properties["contentPicker"] as IApiContent;
Assert.IsNotNull(contentPickerOutput);
Assert.AreEqual(expectedElementOutput.ElementContentPicker, contentPickerOutput.Id);
Assert.AreEqual(0, contentPickerOutput.Properties.Count);
}
}
[Test]
public void OutputExpansionStrategy_CanExpandSpecifiedElement()
{
var accessor = CreateOutputExpansionStrategyAccessor(false, new[] { "element" });
var apiContentBuilder = new ApiContentBuilder(new ApiContentNameProvider(), ApiContentRouteBuilder(), accessor);
var apiElementBuilder = new ApiElementBuilder(accessor);
var contentPickerValue = CreateSimplePickedContent(111, 222);
var contentPicker2Value = CreateSimplePickedContent(666, 777);
var content = new Mock<IPublishedContent>();
SetupContentMock(
content,
CreateNumberProperty(content.Object, 444, "number"),
CreateElementProperty(content.Object, "element", 333, contentPickerValue.Key, "contentPicker", apiContentBuilder, apiElementBuilder),
CreateElementProperty(content.Object, "element2", 555, contentPicker2Value.Key, "contentPicker", apiContentBuilder, apiElementBuilder));
var result = apiContentBuilder.Build(content.Object);
Assert.AreEqual(3, result.Properties.Count);
Assert.AreEqual(444, result.Properties["number"]);
var elementOutput = result.Properties["element"] as IApiElement;
Assert.IsNotNull(elementOutput);
Assert.AreEqual(2, elementOutput.Properties.Count);
Assert.AreEqual(333, elementOutput.Properties["number"]);
var contentPickerOutput = elementOutput.Properties["contentPicker"] as IApiContent;
Assert.IsNotNull(contentPickerOutput);
Assert.AreEqual(contentPickerValue.Key, contentPickerOutput.Id);
Assert.AreEqual(2, contentPickerOutput.Properties.Count);
Assert.AreEqual(111, contentPickerOutput.Properties["numberOne"]);
Assert.AreEqual(222, contentPickerOutput.Properties["numberTwo"]);
elementOutput = result.Properties["element2"] as IApiElement;
Assert.IsNotNull(elementOutput);
Assert.AreEqual(2, elementOutput.Properties.Count);
Assert.AreEqual(555, elementOutput.Properties["number"]);
contentPickerOutput = elementOutput.Properties["contentPicker"] as IApiContent;
Assert.IsNotNull(contentPickerOutput);
Assert.AreEqual(contentPicker2Value.Key, contentPickerOutput.Id);
Assert.AreEqual(0, contentPickerOutput.Properties.Count);
}
[Test]
public void OutputExpansionStrategy_CanExpandAllElements()
{
var accessor = CreateOutputExpansionStrategyAccessor(true );
var apiContentBuilder = new ApiContentBuilder(new ApiContentNameProvider(), ApiContentRouteBuilder(), accessor);
var apiElementBuilder = new ApiElementBuilder(accessor);
var contentPickerValue = CreateSimplePickedContent(111, 222);
var contentPicker2Value = CreateSimplePickedContent(666, 777);
var content = new Mock<IPublishedContent>();
SetupContentMock(
content,
CreateNumberProperty(content.Object, 444, "number"),
CreateElementProperty(content.Object, "element", 333, contentPickerValue.Key, "contentPicker", apiContentBuilder, apiElementBuilder),
CreateElementProperty(content.Object, "element2", 555, contentPicker2Value.Key, "contentPicker", apiContentBuilder, apiElementBuilder));
var result = apiContentBuilder.Build(content.Object);
Assert.AreEqual(3, result.Properties.Count);
Assert.AreEqual(444, result.Properties["number"]);
var expectedElementOutputs = new[]
{
new
{
PropertyAlias = "element",
ElementNumber = 333,
ElementContentPicker = contentPickerValue.Key,
ContentNumberOne = 111,
ContentNumberTwo = 222
},
new
{
PropertyAlias = "element2",
ElementNumber = 555,
ElementContentPicker = contentPicker2Value.Key,
ContentNumberOne = 666,
ContentNumberTwo = 777
}
};
foreach (var expectedElementOutput in expectedElementOutputs)
{
var elementOutput = result.Properties[expectedElementOutput.PropertyAlias] as IApiElement;
Assert.IsNotNull(elementOutput);
Assert.AreEqual(2, elementOutput.Properties.Count);
Assert.AreEqual(expectedElementOutput.ElementNumber, elementOutput.Properties["number"]);
var contentPickerOutput = elementOutput.Properties["contentPicker"] as IApiContent;
Assert.IsNotNull(contentPickerOutput);
Assert.AreEqual(expectedElementOutput.ElementContentPicker, contentPickerOutput.Id);
Assert.AreEqual(2, contentPickerOutput.Properties.Count);
Assert.AreEqual(expectedElementOutput.ContentNumberOne, contentPickerOutput.Properties["numberOne"]);
Assert.AreEqual(expectedElementOutput.ContentNumberTwo, contentPickerOutput.Properties["numberTwo"]);
}
}
[Test]
public void OutputExpansionStrategy_DoesNotExpandElementNestedContentPicker()
{
var accessor = CreateOutputExpansionStrategyAccessor(false, new[] { "element" });
var apiContentBuilder = new ApiContentBuilder(new ApiContentNameProvider(), ApiContentRouteBuilder(), accessor);
var apiElementBuilder = new ApiElementBuilder(accessor);
var nestedContentPickerValue = CreateSimplePickedContent(111, 222);
var contentPickerValue = CreateMultiLevelPickedContent(987, nestedContentPickerValue, "contentPicker", apiContentBuilder);
var content = new Mock<IPublishedContent>();
SetupContentMock(content, CreateElementProperty(content.Object, "element", 333, contentPickerValue.Key, "contentPicker", apiContentBuilder, apiElementBuilder));
var result = apiContentBuilder.Build(content.Object);
Assert.AreEqual(1, result.Properties.Count);
var elementOutput = result.Properties["element"] as IApiElement;
Assert.IsNotNull(elementOutput);
Assert.AreEqual(2, elementOutput.Properties.Count);
Assert.AreEqual(333, elementOutput.Properties["number"]);
var contentPickerOutput = elementOutput.Properties["contentPicker"] as IApiContent;
Assert.IsNotNull(contentPickerOutput);
Assert.AreEqual(contentPickerValue.Key, contentPickerOutput.Id);
Assert.AreEqual(2, contentPickerOutput.Properties.Count);
Assert.AreEqual(987, contentPickerOutput.Properties["number"]);
var nestedContentPickerOutput = contentPickerOutput.Properties["contentPicker"] as IApiContent;
Assert.IsNotNull(nestedContentPickerOutput);
Assert.AreEqual(nestedContentPickerValue.Key, nestedContentPickerOutput.Id);
Assert.AreEqual(0, nestedContentPickerOutput.Properties.Count);
}
private IOutputExpansionStrategyAccessor CreateOutputExpansionStrategyAccessor(bool expandAll = false, string[]? expandPropertyAliases = null)
{
var httpContextMock = new Mock<HttpContext>();
var httpRequestMock = new Mock<HttpRequest>();
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
var expand = expandAll ? "all" : expandPropertyAliases != null ? $"property:{string.Join(",", expandPropertyAliases)}" : null;
httpRequestMock
.SetupGet(r => r.Query)
.Returns(new QueryCollection(new Dictionary<string, StringValues> { { "expand", expand } }));
httpContextMock.SetupGet(c => c.Request).Returns(httpRequestMock.Object);
httpContextAccessorMock.SetupGet(a => a.HttpContext).Returns(httpContextMock.Object);
IOutputExpansionStrategy outputExpansionStrategy = new RequestContextOutputExpansionStrategy(httpContextAccessorMock.Object);
var outputExpansionStrategyAccessorMock = new Mock<IOutputExpansionStrategyAccessor>();
outputExpansionStrategyAccessorMock.Setup(s => s.TryGetValue(out outputExpansionStrategy)).Returns(true);
return outputExpansionStrategyAccessorMock.Object;
}
private void SetupContentMock(Mock<IPublishedContent> content, params IPublishedProperty[] properties)
{
var key = Guid.NewGuid();
var name = "The page";
var urlSegment = "url-segment";
ConfigurePublishedContentMock(content, key, name, urlSegment, _contentType, properties);
RegisterContentWithProviders(content.Object);
}
private IPublishedContent CreateSimplePickedContent(int numberOneValue, int numberTwoValue)
{
var content = new Mock<IPublishedContent>();
SetupContentMock(
content,
CreateNumberProperty(content.Object, numberOneValue, "numberOne"),
CreateNumberProperty(content.Object, numberTwoValue, "numberTwo"));
return content.Object;
}
private IPublishedContent CreateMultiLevelPickedContent(int numberValue, IPublishedContent nestedContentPickerValue, string nestedContentPickerPropertyTypeAlias, ApiContentBuilder apiContentBuilder)
{
var content = new Mock<IPublishedContent>();
SetupContentMock(
content,
CreateNumberProperty(content.Object, numberValue, "number"),
CreateContentPickerProperty(content.Object, nestedContentPickerValue.Key, nestedContentPickerPropertyTypeAlias, apiContentBuilder));
return content.Object;
}
private PublishedElementPropertyBase CreateContentPickerProperty(IPublishedElement parent, Guid pickedContentKey, string propertyTypeAlias, IApiContentBuilder contentBuilder)
{
ContentPickerValueConverter contentPickerValueConverter = new ContentPickerValueConverter(PublishedSnapshotAccessor, contentBuilder);
var contentPickerPropertyType = SetupPublishedPropertyType(contentPickerValueConverter, propertyTypeAlias, Constants.PropertyEditors.Aliases.ContentPicker);
return new PublishedElementPropertyBase(contentPickerPropertyType, parent, false, PropertyCacheLevel.None, new GuidUdi(Constants.UdiEntityType.Document, pickedContentKey).ToString());
}
private PublishedElementPropertyBase CreateNumberProperty(IPublishedElement parent, int propertyValue, string propertyTypeAlias)
{
var numberPropertyType = SetupPublishedPropertyType(new IntegerValueConverter(), propertyTypeAlias, Constants.PropertyEditors.Aliases.Label);
return new PublishedElementPropertyBase(numberPropertyType, parent, false, PropertyCacheLevel.None, propertyValue);
}
private PublishedElementPropertyBase CreateElementProperty(
IPublishedElement parent,
string elementPropertyAlias,
int numberPropertyValue,
Guid contentPickerPropertyValue,
string contentPickerPropertyTypeAlias,
IApiContentBuilder apiContentBuilder,
IApiElementBuilder apiElementBuilder)
{
var element = new Mock<IPublishedElement>();
element.SetupGet(c => c.ContentType).Returns(_elementType);
element.SetupGet(c => c.Properties).Returns(new[]
{
CreateNumberProperty(element.Object, numberPropertyValue, "number"),
CreateContentPickerProperty(element.Object, contentPickerPropertyValue, contentPickerPropertyTypeAlias, apiContentBuilder)
});
var elementValueConverter = new Mock<IDeliveryApiPropertyValueConverter>();
elementValueConverter
.Setup(p => p.ConvertIntermediateToDeliveryApiObject(
It.IsAny<IPublishedElement>(),
It.IsAny<IPublishedPropertyType>(),
It.IsAny<PropertyCacheLevel>(),
It.IsAny<object?>(),
It.IsAny<bool>()))
.Returns(() => apiElementBuilder.Build(element.Object));
elementValueConverter.Setup(p => p.IsConverter(It.IsAny<IPublishedPropertyType>())).Returns(true);
elementValueConverter.Setup(p => p.GetPropertyCacheLevel(It.IsAny<IPublishedPropertyType>())).Returns(PropertyCacheLevel.None);
elementValueConverter.Setup(p => p.GetDeliveryApiPropertyCacheLevel(It.IsAny<IPublishedPropertyType>())).Returns(PropertyCacheLevel.None);
var elementPropertyType = SetupPublishedPropertyType(elementValueConverter.Object, elementPropertyAlias, "My.Element.Property");
return new PublishedElementPropertyBase(elementPropertyType, parent, false, PropertyCacheLevel.None);
}
private IApiContentRouteBuilder ApiContentRouteBuilder() => new ApiContentRouteBuilder(PublishedUrlProvider, CreateGlobalSettings(), Mock.Of<IVariationContextAccessor>());
}