Un-routable content should never be accessible in the delivery API (#14242)
This commit is contained in:
@@ -43,6 +43,12 @@ public class ByIdContentApiController : ContentApiItemControllerBase
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
return await Task.FromResult(Ok(ApiContentResponseBuilder.Build(contentItem)));
|
||||
IApiContentResponse? apiContentResponse = ApiContentResponseBuilder.Build(contentItem);
|
||||
if (apiContentResponse is null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return await Task.FromResult(Ok(apiContentResponse));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.New.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Api.Delivery.Controllers;
|
||||
@@ -53,7 +54,7 @@ public class QueryContentApiController : ContentApiControllerBase
|
||||
|
||||
PagedModel<Guid> pagedResult = queryAttempt.Result;
|
||||
IEnumerable<IPublishedContent> contentItems = ApiPublishedContentCache.GetByIds(pagedResult.Items);
|
||||
IApiContentResponse[] apiContentItems = contentItems.Select(ApiContentResponseBuilder.Build).ToArray();
|
||||
IApiContentResponse[] apiContentItems = contentItems.Select(ApiContentResponseBuilder.Build).WhereNotNull().ToArray();
|
||||
|
||||
var model = new PagedViewModel<IApiContentResponse>
|
||||
{
|
||||
|
||||
@@ -19,8 +19,14 @@ public abstract class ApiContentBuilderBase<T>
|
||||
|
||||
protected abstract T Create(IPublishedContent content, Guid id, string name, string contentType, IApiContentRoute route, IDictionary<string, object?> properties);
|
||||
|
||||
public virtual T Build(IPublishedContent content)
|
||||
public virtual T? Build(IPublishedContent content)
|
||||
{
|
||||
IApiContentRoute? route = _apiContentRouteBuilder.Build(content);
|
||||
if (route is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
IDictionary<string, object?> properties =
|
||||
_outputExpansionStrategyAccessor.TryGetValue(out IOutputExpansionStrategy? outputExpansionStrategy)
|
||||
? outputExpansionStrategy.MapContentProperties(content)
|
||||
@@ -31,7 +37,7 @@ public abstract class ApiContentBuilderBase<T>
|
||||
content.Key,
|
||||
_apiContentNameProvider.GetName(content),
|
||||
content.ContentType.Alias,
|
||||
_apiContentRouteBuilder.Build(content),
|
||||
route,
|
||||
properties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Umbraco.Cms.Core.Models.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
@@ -14,12 +15,26 @@ public sealed class ApiContentResponseBuilder : ApiContentBuilderBase<IApiConten
|
||||
|
||||
protected override IApiContentResponse Create(IPublishedContent content, Guid id, string name, string contentType, IApiContentRoute route, IDictionary<string, object?> properties)
|
||||
{
|
||||
var cultures = content.Cultures.Values
|
||||
.Where(publishedCultureInfo => publishedCultureInfo.Culture.IsNullOrWhiteSpace() == false) // filter out invariant cultures
|
||||
.ToDictionary(
|
||||
publishedCultureInfo => publishedCultureInfo.Culture,
|
||||
publishedCultureInfo => _apiContentRouteBuilder.Build(content, publishedCultureInfo.Culture));
|
||||
var routesByCulture = new Dictionary<string, IApiContentRoute>();
|
||||
|
||||
return new ApiContentResponse(id, name, contentType, route, properties, cultures);
|
||||
foreach (PublishedCultureInfo publishedCultureInfo in content.Cultures.Values)
|
||||
{
|
||||
if (publishedCultureInfo.Culture.IsNullOrWhiteSpace())
|
||||
{
|
||||
// filter out invariant cultures
|
||||
continue;
|
||||
}
|
||||
|
||||
IApiContentRoute? cultureRoute = _apiContentRouteBuilder.Build(content, publishedCultureInfo.Culture);
|
||||
if (cultureRoute == null)
|
||||
{
|
||||
// content is un-routable in this culture
|
||||
continue;
|
||||
}
|
||||
|
||||
routesByCulture[publishedCultureInfo.Culture] = cultureRoute;
|
||||
}
|
||||
|
||||
return new ApiContentResponse(id, name, contentType, route, properties, routesByCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
_globalSettings = globalSettings.Value;
|
||||
}
|
||||
|
||||
public IApiContentRoute Build(IPublishedContent content, string? culture = null)
|
||||
public IApiContentRoute? Build(IPublishedContent content, string? culture = null)
|
||||
{
|
||||
if (content.ItemType != PublishedItemType.Content)
|
||||
{
|
||||
@@ -42,11 +42,17 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
// in some scenarios the published content is actually routable, but due to the built-in handling of i.e. lacking culture setup
|
||||
// the URL provider resolves the content URL as empty string or "#". since the Delivery API handles routing explicitly,
|
||||
// we can perform fallback to the content route.
|
||||
if (contentPath.IsNullOrWhiteSpace() || "#".Equals(contentPath))
|
||||
if (IsInvalidContentPath(contentPath))
|
||||
{
|
||||
contentPath = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot().Content?.GetRouteById(content.Id, culture) ?? contentPath;
|
||||
}
|
||||
|
||||
// if the content path has still not been resolved as a valid path, the content is un-routable in this culture
|
||||
if (IsInvalidContentPath(contentPath))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
contentPath = contentPath.EnsureStartsWith("/");
|
||||
if (_globalSettings.HideTopLevelNodeFromPath == false)
|
||||
{
|
||||
@@ -55,4 +61,6 @@ public sealed class ApiContentRouteBuilder : IApiContentRouteBuilder
|
||||
|
||||
return new ApiContentRoute(contentPath, new ApiContentStartItem(root.Key, rootPath));
|
||||
}
|
||||
|
||||
private static bool IsInvalidContentPath(string path) => path.IsNullOrWhiteSpace() || "#".Equals(path);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public interface IApiContentBuilder
|
||||
{
|
||||
IApiContent Build(IPublishedContent content);
|
||||
IApiContent? Build(IPublishedContent content);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public interface IApiContentResponseBuilder
|
||||
{
|
||||
IApiContentResponse Build(IPublishedContent content);
|
||||
IApiContentResponse? Build(IPublishedContent content);
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ namespace Umbraco.Cms.Core.DeliveryApi;
|
||||
|
||||
public interface IApiContentRouteBuilder
|
||||
{
|
||||
IApiContentRoute Build(IPublishedContent content, string? culture = null);
|
||||
IApiContentRoute? Build(IPublishedContent content, string? culture = null);
|
||||
}
|
||||
|
||||
@@ -117,9 +117,12 @@ internal sealed partial class ApiRichTextParser : IApiRichTextParser
|
||||
{
|
||||
case Constants.UdiEntityType.Document:
|
||||
IPublishedContent? content = publishedSnapshot.Content?.GetById(udi);
|
||||
if (content != null)
|
||||
IApiContentRoute? route = content != null
|
||||
? _apiContentRouteBuilder.Build(content)
|
||||
: null;
|
||||
if (route != null)
|
||||
{
|
||||
attributes["route"] = _apiContentRouteBuilder.Build(content);
|
||||
attributes["route"] = route;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -177,14 +177,17 @@ public class MultiUrlPickerValueConverter : PropertyValueConverterBase, IDeliver
|
||||
{
|
||||
case Constants.UdiEntityType.Document:
|
||||
IPublishedContent? content = publishedSnapshot.Content?.GetById(item.Udi.Guid);
|
||||
return content == null
|
||||
IApiContentRoute? route = content != null
|
||||
? _apiContentRouteBuilder.Build(content)
|
||||
: null;
|
||||
return content == null || route == null
|
||||
? null
|
||||
: ApiLink.Content(
|
||||
item.Name.IfNullOrWhiteSpace(_apiContentNameProvider.GetName(content)),
|
||||
item.Target,
|
||||
content.Key,
|
||||
content.ContentType.Alias,
|
||||
_apiContentRouteBuilder.Build(content));
|
||||
route);
|
||||
case Constants.UdiEntityType.Media:
|
||||
IPublishedContent? media = publishedSnapshot.Media?.GetById(item.Udi.Guid);
|
||||
return media == null
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
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.PublishedCache;
|
||||
@@ -61,10 +62,36 @@ public class ContentBuilderTests : DeliveryApiTests
|
||||
var customNameProvider = new Mock<IApiContentNameProvider>();
|
||||
customNameProvider.Setup(n => n.GetName(content.Object)).Returns($"Custom name for: {content.Object.Name}");
|
||||
|
||||
var builder = new ApiContentBuilder(customNameProvider.Object, Mock.Of<IApiContentRouteBuilder>(), CreateOutputExpansionStrategyAccessor());
|
||||
var routeBuilder = new Mock<IApiContentRouteBuilder>();
|
||||
routeBuilder
|
||||
.Setup(r => r.Build(content.Object, It.IsAny<string?>()))
|
||||
.Returns(new ApiContentRoute(content.Object.UrlSegment!, new ApiContentStartItem(Guid.NewGuid(), "/")));
|
||||
|
||||
var builder = new ApiContentBuilder(customNameProvider.Object, routeBuilder.Object, CreateOutputExpansionStrategyAccessor());
|
||||
var result = builder.Build(content.Object);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.AreEqual("Custom name for: The page", result.Name);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContentBuilder_ReturnsNullForUnRoutableContent()
|
||||
{
|
||||
var content = new Mock<IPublishedContent>();
|
||||
|
||||
var contentType = new Mock<IPublishedContentType>();
|
||||
contentType.SetupGet(c => c.Alias).Returns("thePageType");
|
||||
|
||||
ConfigurePublishedContentMock(content, Guid.NewGuid(), "The page", "the-page", contentType.Object, Array.Empty<PublishedElementPropertyBase>());
|
||||
|
||||
var routeBuilder = new Mock<IApiContentRouteBuilder>();
|
||||
routeBuilder
|
||||
.Setup(r => r.Build(content.Object, It.IsAny<string?>()))
|
||||
.Returns((ApiContentRoute)null);
|
||||
|
||||
var builder = new ApiContentBuilder(new ApiContentNameProvider(), routeBuilder.Object, CreateOutputExpansionStrategyAccessor());
|
||||
var result = builder.Build(content.Object);
|
||||
|
||||
Assert.Null(result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.DeliveryApi;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
@@ -21,6 +22,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath);
|
||||
var result = builder.Build(root);
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root", result.StartItem.Path);
|
||||
@@ -38,6 +40,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath);
|
||||
var result = builder.Build(child);
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the-child", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root", result.StartItem.Path);
|
||||
@@ -58,6 +61,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath);
|
||||
var result = builder.Build(grandchild);
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the-child/the-grandchild", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root", result.StartItem.Path);
|
||||
@@ -74,11 +78,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(false);
|
||||
var result = builder.Build(child, "en-us");
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the-child-en-us", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root-en-us", result.StartItem.Path);
|
||||
|
||||
result = builder.Build(child, "da-dk");
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the-child-da-dk", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root-da-dk", result.StartItem.Path);
|
||||
@@ -95,11 +101,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(false);
|
||||
var result = builder.Build(child, "en-us");
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the-child", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root-en-us", result.StartItem.Path);
|
||||
|
||||
result = builder.Build(child, "da-dk");
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the-child", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root-da-dk", result.StartItem.Path);
|
||||
@@ -116,11 +124,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
|
||||
var builder = CreateApiContentRouteBuilder(false);
|
||||
var result = builder.Build(child, "en-us");
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the-child-en-us", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root", result.StartItem.Path);
|
||||
|
||||
result = builder.Build(child, "da-dk");
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the-child-da-dk", result.Path);
|
||||
Assert.AreEqual(rootKey, result.StartItem.Id);
|
||||
Assert.AreEqual("the-root", result.StartItem.Path);
|
||||
@@ -144,36 +154,18 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
[TestCase("#")]
|
||||
public void FallsBackToContentPathIfUrlProviderCannotResolveUrl(string resolvedUrl)
|
||||
{
|
||||
var publishedUrlProviderMock = new Mock<IPublishedUrlProvider>();
|
||||
publishedUrlProviderMock
|
||||
.Setup(p => p.GetUrl(It.IsAny<IPublishedContent>(), It.IsAny<UrlMode>(), It.IsAny<string?>(), It.IsAny<Uri?>()))
|
||||
.Returns(resolvedUrl);
|
||||
var result = GetUnRoutableRoute(resolvedUrl, "/the/content/route");
|
||||
Assert.IsNotNull(result);
|
||||
Assert.AreEqual("/the/content/route", result.Path);
|
||||
}
|
||||
|
||||
var publishedContentCacheMock = new Mock<IPublishedContentCache>();
|
||||
publishedContentCacheMock
|
||||
.Setup(c => c.GetRouteById(It.IsAny<int>(), It.IsAny<string?>()))
|
||||
.Returns("/the/content/route");
|
||||
|
||||
var publishedSnapshotMock = new Mock<IPublishedSnapshot>();
|
||||
publishedSnapshotMock
|
||||
.SetupGet(s => s.Content)
|
||||
.Returns(publishedContentCacheMock.Object);
|
||||
var publishedSnapshot = publishedSnapshotMock.Object;
|
||||
|
||||
var publishedSnapshotAccessorMock = new Mock<IPublishedSnapshotAccessor>();
|
||||
publishedSnapshotAccessorMock
|
||||
.Setup(a => a.TryGetPublishedSnapshot(out publishedSnapshot))
|
||||
.Returns(true);
|
||||
|
||||
var content = SetupVariantPublishedContent("The Content", Guid.NewGuid());
|
||||
|
||||
var builder = new ApiContentRouteBuilder(
|
||||
publishedUrlProviderMock.Object,
|
||||
CreateGlobalSettings(),
|
||||
Mock.Of<IVariationContextAccessor>(),
|
||||
publishedSnapshotAccessorMock.Object);
|
||||
|
||||
Assert.AreEqual("/the/content/route", builder.Build(content).Path);
|
||||
[TestCase("")]
|
||||
[TestCase(" ")]
|
||||
[TestCase("#")]
|
||||
public void YieldsNullForUnRoutableContent(string contentPath)
|
||||
{
|
||||
var result = GetUnRoutableRoute(contentPath, contentPath);
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
@@ -253,4 +245,38 @@ public class ContentRouteBuilderTests : DeliveryApiTests
|
||||
CreateGlobalSettings(hideTopLevelNodeFromPath),
|
||||
Mock.Of<IVariationContextAccessor>(),
|
||||
Mock.Of<IPublishedSnapshotAccessor>());
|
||||
|
||||
private IApiContentRoute? GetUnRoutableRoute(string publishedUrl, string routeById)
|
||||
{
|
||||
var publishedUrlProviderMock = new Mock<IPublishedUrlProvider>();
|
||||
publishedUrlProviderMock
|
||||
.Setup(p => p.GetUrl(It.IsAny<IPublishedContent>(), It.IsAny<UrlMode>(), It.IsAny<string?>(), It.IsAny<Uri?>()))
|
||||
.Returns(publishedUrl);
|
||||
|
||||
var publishedContentCacheMock = new Mock<IPublishedContentCache>();
|
||||
publishedContentCacheMock
|
||||
.Setup(c => c.GetRouteById(It.IsAny<int>(), It.IsAny<string?>()))
|
||||
.Returns(routeById);
|
||||
|
||||
var publishedSnapshotMock = new Mock<IPublishedSnapshot>();
|
||||
publishedSnapshotMock
|
||||
.SetupGet(s => s.Content)
|
||||
.Returns(publishedContentCacheMock.Object);
|
||||
var publishedSnapshot = publishedSnapshotMock.Object;
|
||||
|
||||
var publishedSnapshotAccessorMock = new Mock<IPublishedSnapshotAccessor>();
|
||||
publishedSnapshotAccessorMock
|
||||
.Setup(a => a.TryGetPublishedSnapshot(out publishedSnapshot))
|
||||
.Returns(true);
|
||||
|
||||
var content = SetupVariantPublishedContent("The Content", Guid.NewGuid());
|
||||
|
||||
var builder = new ApiContentRouteBuilder(
|
||||
publishedUrlProviderMock.Object,
|
||||
CreateGlobalSettings(),
|
||||
Mock.Of<IVariationContextAccessor>(),
|
||||
publishedSnapshotAccessorMock.Object);
|
||||
|
||||
return builder.Build(content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,13 +15,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.DeliveryApi;
|
||||
[TestFixture]
|
||||
public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTests
|
||||
{
|
||||
private MultiNodeTreePickerValueConverter MultiNodeTreePickerValueConverter()
|
||||
private MultiNodeTreePickerValueConverter MultiNodeTreePickerValueConverter(IApiContentRouteBuilder? routeBuilder = null)
|
||||
{
|
||||
var expansionStrategyAccessor = CreateOutputExpansionStrategyAccessor();
|
||||
|
||||
var contentNameProvider = new ApiContentNameProvider();
|
||||
var apiUrProvider = new ApiMediaUrlProvider(PublishedUrlProvider);
|
||||
var routeBuilder = new ApiContentRouteBuilder(PublishedUrlProvider, CreateGlobalSettings(), Mock.Of<IVariationContextAccessor>(), Mock.Of<IPublishedSnapshotAccessor>());
|
||||
routeBuilder = routeBuilder ?? new ApiContentRouteBuilder(PublishedUrlProvider, CreateGlobalSettings(), Mock.Of<IVariationContextAccessor>(), Mock.Of<IPublishedSnapshotAccessor>());
|
||||
return new MultiNodeTreePickerValueConverter(
|
||||
PublishedSnapshotAccessor,
|
||||
Mock.Of<IUmbracoContextAccessor>(),
|
||||
@@ -274,4 +274,21 @@ public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTest
|
||||
Assert.NotNull(result);
|
||||
Assert.IsEmpty(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultiNodeTreePickerValueConverter_YieldsNothingForUnRoutableContent()
|
||||
{
|
||||
var publishedDataType = MultiNodePickerPublishedDataType(false, Constants.UdiEntityType.Document);
|
||||
var publishedPropertyType = new Mock<IPublishedPropertyType>();
|
||||
publishedPropertyType.SetupGet(p => p.DataType).Returns(publishedDataType);
|
||||
|
||||
// mocking the route builder will make it yield null values for all routes, so there is no need to setup anything on the mock
|
||||
var routeBuilder = new Mock<IApiContentRouteBuilder>();
|
||||
var valueConverter = MultiNodeTreePickerValueConverter(routeBuilder.Object);
|
||||
|
||||
var inter = new Udi[] { new GuidUdi(Constants.UdiEntityType.Document, PublishedContent.Key) };
|
||||
var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of<IPublishedElement>(), publishedPropertyType.Object, PropertyCacheLevel.Element, inter, false) as IEnumerable<IApiContent>;
|
||||
Assert.NotNull(result);
|
||||
Assert.IsEmpty(result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user