diff --git a/src/Umbraco.Core/Configuration/Models/DeliveryApiSettings.cs b/src/Umbraco.Core/Configuration/Models/DeliveryApiSettings.cs
index 797a055912a1..80353edcdd2b 100644
--- a/src/Umbraco.Core/Configuration/Models/DeliveryApiSettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/DeliveryApiSettings.cs
@@ -42,8 +42,25 @@ public class DeliveryApiSettings
///
/// The content type aliases that are not to be exposed.
///
+ ///
+ /// If is configured (non-empty), this setting is ignored.
+ ///
public ISet DisallowedContentTypeAliases { get; set; } = new HashSet();
+ ///
+ /// Gets or sets the aliases of the content types that are exclusively allowed to be exposed through the Delivery API.
+ /// When configured, only content of these types will be returned from Delivery API endpoints and added to the query index.
+ ///
+ ///
+ /// The content type aliases that are allowed to be exposed.
+ ///
+ ///
+ /// When this setting is configured (non-empty), it takes precedence over .
+ /// If a content type alias appears in both lists, the allow list wins and the content type will be exposed.
+ /// If this setting is empty, all content types are allowed except those in .
+ ///
+ public ISet AllowedContentTypeAliases { get; set; } = new HashSet();
+
///
/// Gets or sets a value indicating whether the Delivery API should output rich text values as JSON instead of HTML.
///
diff --git a/src/Umbraco.Core/Configuration/Models/Validation/DeliveryApiSettingsValidator.cs b/src/Umbraco.Core/Configuration/Models/Validation/DeliveryApiSettingsValidator.cs
new file mode 100644
index 000000000000..05074690c006
--- /dev/null
+++ b/src/Umbraco.Core/Configuration/Models/Validation/DeliveryApiSettingsValidator.cs
@@ -0,0 +1,47 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Umbraco.Extensions;
+
+namespace Umbraco.Cms.Core.Configuration.Models.Validation;
+
+///
+/// Validator for configuration represented as .
+///
+public class DeliveryApiSettingsValidator : IValidateOptions
+{
+ private readonly ILogger _logger;
+
+ public DeliveryApiSettingsValidator(ILogger logger)
+ => _logger = logger;
+
+ ///
+ public ValidateOptionsResult Validate(string? name, DeliveryApiSettings options)
+ {
+ ValidateContentTypeAliasOverlap(options);
+
+ return ValidateOptionsResult.Success;
+ }
+
+ private void ValidateContentTypeAliasOverlap(DeliveryApiSettings options)
+ {
+ if (options.AllowedContentTypeAliases.Count == 0 || options.DisallowedContentTypeAliases.Count == 0)
+ {
+ return;
+ }
+
+ var overlappingAliases = options.AllowedContentTypeAliases
+ .Where(alias => options.DisallowedContentTypeAliases.InvariantContains(alias))
+ .ToArray();
+
+ if (overlappingAliases.Length > 0)
+ {
+ _logger.LogWarning(
+ "Delivery API configuration contains content type aliases that appear in both AllowedContentTypeAliases and DisallowedContentTypeAliases: {Aliases}. " +
+ "The allow list takes precedence, so these content types will be allowed. Consider removing them from the disallow list to avoid confusion.",
+ string.Join(", ", overlappingAliases));
+ }
+ }
+}
diff --git a/src/Umbraco.Core/DeliveryApi/ApiPublishedContentCache.cs b/src/Umbraco.Core/DeliveryApi/ApiPublishedContentCache.cs
index dd91b4ced111..0e48262e733e 100644
--- a/src/Umbraco.Core/DeliveryApi/ApiPublishedContentCache.cs
+++ b/src/Umbraco.Core/DeliveryApi/ApiPublishedContentCache.cs
@@ -129,5 +129,5 @@ public IEnumerable GetByIds(IEnumerable contentIds)
: null;
private bool IsAllowedContentType(IPublishedContent content)
- => _deliveryApiSettings.IsAllowedContentType(content);
+ => _deliveryApiSettings.IsAllowedContentType(content.ContentType.Alias);
}
diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
index e56a4cef2780..db436b867064 100644
--- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
+++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Configuration.cs
@@ -41,6 +41,7 @@ public static IUmbracoBuilder AddConfiguration(this IUmbracoBuilder builder)
{
// Register configuration validators.
builder.Services.AddSingleton, ContentSettingsValidator>();
+ builder.Services.AddSingleton, DeliveryApiSettingsValidator>();
builder.Services.AddSingleton, GlobalSettingsValidator>();
builder.Services.AddSingleton, HealthChecksSettingsValidator>();
builder.Services.AddSingleton, LoggingSettingsValidator>();
diff --git a/src/Umbraco.Core/Extensions/DeliveryApiSettingsExtensions.cs b/src/Umbraco.Core/Extensions/DeliveryApiSettingsExtensions.cs
index f0e35f0d6e9e..2f49086cea57 100644
--- a/src/Umbraco.Core/Extensions/DeliveryApiSettingsExtensions.cs
+++ b/src/Umbraco.Core/Extensions/DeliveryApiSettingsExtensions.cs
@@ -1,19 +1,48 @@
-using Umbraco.Cms.Core.Configuration.Models;
+using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models.PublishedContent;
namespace Umbraco.Extensions;
+///
+/// Provides extension methods for determining whether content types or content items are allowed to be exposed through
+/// the Delivery API based on the configured allow and disallow lists.
+///
public static class DeliveryApiSettingsExtensions
{
+ [Obsolete("Please use the overload of IsAllowedContentType taking a content type alias. Scheduled for removal in Umbraco 19.")]
public static bool IsAllowedContentType(this DeliveryApiSettings settings, IPublishedContent content)
=> settings.IsAllowedContentType(content.ContentType.Alias);
+ [Obsolete("Please use IsAllowedContentType and negate the result instead. Scheduled for removal in Umbraco 19.")]
public static bool IsDisallowedContentType(this DeliveryApiSettings settings, IPublishedContent content)
=> settings.IsDisallowedContentType(content.ContentType.Alias);
+ ///
+ /// Determines whether a content type alias is allowed to be exposed through the Delivery API.
+ ///
+ /// The Delivery API settings.
+ /// The content type alias to check.
+ ///
+ /// true if the content type is allowed; otherwise, false.
+ ///
+ ///
+ /// If the allow list is configured (non-empty), only content types in the allow list are permitted.
+ /// The allow list takes precedence - if a content type is in both allow and disallow lists, it is allowed.
+ /// If the allow list is empty, all content types are allowed except those in the disallow list.
+ ///
public static bool IsAllowedContentType(this DeliveryApiSettings settings, string contentTypeAlias)
- => settings.IsDisallowedContentType(contentTypeAlias) is false;
+ {
+ // If allow list is configured, it takes precedence.
+ if (settings.AllowedContentTypeAliases.Count > 0)
+ {
+ return settings.AllowedContentTypeAliases.InvariantContains(contentTypeAlias);
+ }
+ // Otherwise the content type is allowed if it's not in the disallow list.
+ return settings.DisallowedContentTypeAliases.InvariantContains(contentTypeAlias) is false;
+ }
+
+ [Obsolete("Please use IsAllowedContentType and negate the result instead. Scheduled for removal in Umbraco 19.")]
public static bool IsDisallowedContentType(this DeliveryApiSettings settings, string contentTypeAlias)
- => settings.DisallowedContentTypeAliases.InvariantContains(contentTypeAlias);
+ => settings.IsAllowedContentType(contentTypeAlias) is false;
}
diff --git a/src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexValueSetBuilder.cs
index 7c76eaddd3b1..0da2e86ee7f1 100644
--- a/src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexValueSetBuilder.cs
+++ b/src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexValueSetBuilder.cs
@@ -204,7 +204,7 @@ private bool CanIndex(IContent content)
}
// is the content type allowed in the index?
- if (_deliveryApiSettings.IsDisallowedContentType(content.ContentType.Alias))
+ if (_deliveryApiSettings.IsAllowedContentType(content.ContentType.Alias) is false)
{
return false;
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Configuration/Models/Validation/DeliveryApiSettingsValidatorTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Configuration/Models/Validation/DeliveryApiSettingsValidatorTests.cs
new file mode 100644
index 000000000000..5cd3ab6ef532
--- /dev/null
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Configuration/Models/Validation/DeliveryApiSettingsValidatorTests.cs
@@ -0,0 +1,101 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Moq;
+using NUnit.Framework;
+using Umbraco.Cms.Core.Configuration.Models;
+using Umbraco.Cms.Core.Configuration.Models.Validation;
+
+namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Configuration.Models.Validation;
+
+[TestFixture]
+public class DeliveryApiSettingsValidatorTests
+{
+ private Mock> _loggerMock;
+
+ [SetUp]
+ public void SetUp() => _loggerMock = new Mock>();
+
+ [Test]
+ public void Returns_Success_For_Configuration_With_Only_AllowList()
+ {
+ var validator = new DeliveryApiSettingsValidator(_loggerMock.Object);
+ var options = new DeliveryApiSettings
+ {
+ AllowedContentTypeAliases = new HashSet { "content1", "content2" },
+ };
+
+ ValidateOptionsResult result = validator.Validate("settings", options);
+
+ Assert.IsTrue(result.Succeeded);
+ VerifyNoWarningLogged();
+ }
+
+ [Test]
+ public void Returns_Success_For_Configuration_With_Only_DisallowList()
+ {
+ var validator = new DeliveryApiSettingsValidator(_loggerMock.Object);
+ var options = new DeliveryApiSettings
+ {
+ DisallowedContentTypeAliases = new HashSet { "content1", "content2" },
+ };
+
+ ValidateOptionsResult result = validator.Validate("settings", options);
+
+ Assert.IsTrue(result.Succeeded);
+ VerifyNoWarningLogged();
+ }
+
+ [Test]
+ public void Returns_Success_For_Configuration_With_No_Overlapping_Lists()
+ {
+ var validator = new DeliveryApiSettingsValidator(_loggerMock.Object);
+ var options = new DeliveryApiSettings
+ {
+ AllowedContentTypeAliases = new HashSet { "content1", "content2" },
+ DisallowedContentTypeAliases = new HashSet { "content3", "content4" },
+ };
+
+ ValidateOptionsResult result = validator.Validate("settings", options);
+
+ Assert.IsTrue(result.Succeeded);
+ VerifyNoWarningLogged();
+ }
+
+ [Test]
+ public void Returns_Success_But_Logs_Warning_For_Overlapping_Lists()
+ {
+ var validator = new DeliveryApiSettingsValidator(_loggerMock.Object);
+ var options = new DeliveryApiSettings
+ {
+ AllowedContentTypeAliases = new HashSet { "content1", "content2" },
+ DisallowedContentTypeAliases = new HashSet { "content1", "content3" },
+ };
+
+ ValidateOptionsResult result = validator.Validate("settings", options);
+
+ Assert.IsTrue(result.Succeeded);
+
+ VerifyWarningLogged();
+ }
+
+ private void VerifyWarningLogged()
+ {
+ var warningCount = GetWarningLogCount();
+ Assert.AreEqual(1, warningCount);
+ }
+
+ private void VerifyNoWarningLogged()
+ {
+ var warningCount = GetWarningLogCount();
+ Assert.AreEqual(0, warningCount);
+ }
+
+ private int GetWarningLogCount() =>
+ _loggerMock.Invocations
+ .Count(invocation =>
+ invocation.Method.Name == nameof(ILogger.Log) &&
+ invocation.Arguments.OfType().Any(level => level == LogLevel.Warning));
+}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PublishedContentCacheTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PublishedContentCacheTests.cs
index 405ff1416211..f4f176755d1b 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PublishedContentCacheTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/PublishedContentCacheTests.cs
@@ -24,7 +24,7 @@ public class PublishedContentCacheTests : DeliveryApiTests
private IDocumentUrlService _documentUrlService;
[SetUp]
- public void Setup()
+ public override void Setup()
{
var contentTypeOneMock = new Mock();
contentTypeOneMock.SetupGet(m => m.Alias).Returns("theContentType");
@@ -81,7 +81,7 @@ public void Setup()
[Test]
public void PublishedContentCache_CanGetById()
{
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings());
var content = publishedContentCache.GetById(_contentOneId);
Assert.IsNotNull(content);
Assert.AreEqual(_contentOneId, content.Key);
@@ -92,7 +92,7 @@ public void PublishedContentCache_CanGetById()
[Test]
public void PublishedContentCache_CanGetByRoute()
{
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings());
var content = publishedContentCache.GetByRoute("/content-two");
Assert.IsNotNull(content);
Assert.AreEqual(_contentTwoId, content.Key);
@@ -103,7 +103,7 @@ public void PublishedContentCache_CanGetByRoute()
[Test]
public void PublishedContentCache_CanGetByRoute_WithStartNodeIdPrefix()
{
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings());
var content = publishedContentCache.GetByRoute("1234/content-three");
Assert.IsNotNull(content);
Assert.AreEqual(_contentThreeId, content.Key);
@@ -114,7 +114,7 @@ public void PublishedContentCache_CanGetByRoute_WithStartNodeIdPrefix()
[Test]
public void PublishedContentCache_CanGetByIds()
{
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings());
var content = publishedContentCache.GetByIds(new[] { _contentOneId, _contentTwoId }).ToArray();
Assert.AreEqual(2, content.Length);
Assert.AreEqual(_contentOneId, content.First().Key);
@@ -123,10 +123,10 @@ public void PublishedContentCache_CanGetByIds()
[TestCase(true)]
[TestCase(false)]
- public void PublishedContentCache_GetById_SupportsDenyList(bool denied)
+ public void PublishedContentCache_GetById_SupportsDisallowList(bool denied)
{
var denyList = denied ? new[] { "theOtherContentType" } : null;
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(denyList), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList));
var content = publishedContentCache.GetById(_contentTwoId);
if (denied)
@@ -141,10 +141,10 @@ public void PublishedContentCache_GetById_SupportsDenyList(bool denied)
[TestCase(true)]
[TestCase(false)]
- public void PublishedContentCache_GetByRoute_SupportsDenyList(bool denied)
+ public void PublishedContentCache_GetByRoute_SupportsDisallowList(bool denied)
{
var denyList = denied ? new[] { "theContentType" } : null;
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(denyList), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList));
var content = publishedContentCache.GetByRoute("/content-one");
if (denied)
@@ -159,10 +159,10 @@ public void PublishedContentCache_GetByRoute_SupportsDenyList(bool denied)
[TestCase("theContentType")]
[TestCase("theOtherContentType")]
- public void PublishedContentCache_GetByIds_SupportsDenyList(string deniedContentType)
+ public void PublishedContentCache_GetByIds_SupportsDisallowList(string deniedContentType)
{
var denyList = new[] { deniedContentType };
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(denyList), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList));
var content = publishedContentCache.GetByIds(new[] { _contentOneId, _contentTwoId }).ToArray();
Assert.AreEqual(1, content.Length);
@@ -177,10 +177,10 @@ public void PublishedContentCache_GetByIds_SupportsDenyList(string deniedContent
}
[Test]
- public void PublishedContentCache_GetById_CanRetrieveContentTypesOutsideTheDenyList()
+ public void PublishedContentCache_GetById_CanRetrieveContentTypesOutsideTheDisallowList()
{
var denyList = new[] { "theContentType" };
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(denyList), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList));
var content = publishedContentCache.GetById(_contentTwoId);
Assert.IsNotNull(content);
Assert.AreEqual(_contentTwoId, content.Key);
@@ -189,10 +189,10 @@ public void PublishedContentCache_GetById_CanRetrieveContentTypesOutsideTheDenyL
}
[Test]
- public void PublishedContentCache_GetByRoute_CanRetrieveContentTypesOutsideTheDenyList()
+ public void PublishedContentCache_GetByRoute_CanRetrieveContentTypesOutsideTheDisallowList()
{
var denyList = new[] { "theOtherContentType" };
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(denyList), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList));
var content = publishedContentCache.GetByRoute("/content-one");
Assert.IsNotNull(content);
Assert.AreEqual(_contentOneId, content.Key);
@@ -204,7 +204,7 @@ public void PublishedContentCache_GetByRoute_CanRetrieveContentTypesOutsideTheDe
[TestCase("da-DK")]
public void PublishedContentCache_GetByRoute_CanRetrieveForCulture(string culture)
{
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor(culture));
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(), culture);
var content = publishedContentCache.GetByRoute("/content-four");
Assert.IsNotNull(content);
Assert.AreEqual(_contentFourId, content.Key);
@@ -216,7 +216,7 @@ public void PublishedContentCache_GetByRoute_CanRetrieveForCulture(string cultur
[TestCase(null)]
public void PublishedContentCache_GetByRoute_CannotRetrieveForMissingOrUnknownCulture(string? culture)
{
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor(culture));
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(), culture);
var content = publishedContentCache.GetByRoute("/content-four");
Assert.IsNull(content);
}
@@ -225,20 +225,131 @@ public void PublishedContentCache_GetByRoute_CannotRetrieveForMissingOrUnknownCu
public void PublishedContentCache_GetByIds_CanDenyAllRequestedContent()
{
var denyList = new[] { "theContentType", "theOtherContentType" };
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(denyList), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList));
var content = publishedContentCache.GetByIds(new[] { _contentOneId, _contentTwoId }).ToArray();
Assert.IsEmpty(content);
}
[Test]
- public void PublishedContentCache_DenyListIsCaseInsensitive()
+ public void PublishedContentCache_DisallowListIsCaseInsensitive()
{
var denyList = new[] { "THEcontentTYPE" };
- var publishedContentCache = new ApiPublishedContentCache(CreateRequestPreviewService(), CreateDeliveryApiSettings(denyList), CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor());
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList));
var content = publishedContentCache.GetByRoute("/content-one");
Assert.IsNull(content);
}
+ [TestCase(true)]
+ [TestCase(false)]
+ public void PublishedContentCache_GetById_SupportsAllowList(bool allowed)
+ {
+ var allowList = allowed ? new[] { "theOtherContentType" } : new[] { "someOtherType" };
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(allowedContentTypeAliases: allowList));
+ var content = publishedContentCache.GetById(_contentTwoId);
+
+ if (allowed)
+ {
+ Assert.IsNotNull(content);
+ }
+ else
+ {
+ Assert.IsNull(content);
+ }
+ }
+
+ [TestCase(true)]
+ [TestCase(false)]
+ public void PublishedContentCache_GetByRoute_SupportsAllowList(bool allowed)
+ {
+ var allowList = allowed ? new[] { "theContentType" } : new[] { "someOtherType" };
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(allowedContentTypeAliases: allowList));
+ var content = publishedContentCache.GetByRoute("/content-one");
+
+ if (allowed)
+ {
+ Assert.IsNotNull(content);
+ }
+ else
+ {
+ Assert.IsNull(content);
+ }
+ }
+
+ [TestCase("theContentType")]
+ [TestCase("theOtherContentType")]
+ public void PublishedContentCache_GetByIds_SupportsAllowList(string allowedContentType)
+ {
+ var allowList = new[] { allowedContentType };
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(allowedContentTypeAliases: allowList));
+ var content = publishedContentCache.GetByIds(new[] { _contentOneId, _contentTwoId }).ToArray();
+
+ Assert.AreEqual(1, content.Length);
+ if (allowedContentType == "theContentType")
+ {
+ Assert.AreEqual(_contentOneId, content.First().Key);
+ }
+ else
+ {
+ Assert.AreEqual(_contentTwoId, content.First().Key);
+ }
+ }
+
+ [Test]
+ public void PublishedContentCache_GetByIds_AllowListCanAllowMultipleContentTypes()
+ {
+ var allowList = new[] { "theContentType", "theOtherContentType" };
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(allowedContentTypeAliases: allowList));
+ var content = publishedContentCache.GetByIds(new[] { _contentOneId, _contentTwoId }).ToArray();
+ Assert.AreEqual(2, content.Length);
+ }
+
+ [Test]
+ public void PublishedContentCache_AllowListIsCaseInsensitive()
+ {
+ var allowList = new[] { "THEcontentTYPE" };
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(allowedContentTypeAliases: allowList));
+ var content = publishedContentCache.GetByRoute("/content-one");
+ Assert.IsNotNull(content);
+ }
+
+ [Test]
+ public void PublishedContentCache_AllowListTakesPrecedenceOverDisallowList()
+ {
+ var denyList = new[] { "theContentType" };
+ var allowList = new[] { "theContentType" };
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList, allowList));
+ var content = publishedContentCache.GetByRoute("/content-one");
+ Assert.IsNotNull(content);
+ }
+
+ [Test]
+ public void PublishedContentCache_AllowListIgnoresDisallowListCompletely()
+ {
+ var denyList = new[] { "theOtherContentType" };
+ var allowList = new[] { "theContentType" };
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList, allowList));
+
+ var contentOne = publishedContentCache.GetByRoute("/content-one");
+ Assert.IsNotNull(contentOne);
+
+ var contentTwo = publishedContentCache.GetById(_contentTwoId);
+ Assert.IsNull(contentTwo);
+ }
+
+ [Test]
+ public void PublishedContentCache_EmptyAllowListFallsBackToDisallowList()
+ {
+ var denyList = new[] { "theContentType" };
+ string[] allowList = Array.Empty();
+ var publishedContentCache = CreateApiPublishedContentCache(CreateDeliveryApiSettings(denyList, allowList));
+
+ var contentOne = publishedContentCache.GetByRoute("/content-one");
+ Assert.IsNull(contentOne);
+
+ var contentTwo = publishedContentCache.GetById(_contentTwoId);
+ Assert.IsNotNull(contentTwo);
+ }
+
private IVariationContextAccessor CreateVariationContextAccessor(string? culture = null)
{
var mock = new Mock();
@@ -253,16 +364,20 @@ private IRequestPreviewService CreateRequestPreviewService(bool isPreview = fals
return previewServiceMock.Object;
}
- private IOptionsMonitor CreateDeliveryApiSettings(string[]? disallowedContentTypeAliases = null)
+ private IOptionsMonitor CreateDeliveryApiSettings(string[]? disallowedContentTypeAliases = null, string[]? allowedContentTypeAliases = null)
{
var deliveryApiSettings = new DeliveryApiSettings
{
- DisallowedContentTypeAliases = new HashSet(disallowedContentTypeAliases ?? Array.Empty())
+ DisallowedContentTypeAliases = new HashSet(disallowedContentTypeAliases ?? Array.Empty()),
+ AllowedContentTypeAliases = new HashSet(allowedContentTypeAliases ?? Array.Empty()),
};
var deliveryApiOptionsMonitorMock = new Mock>();
deliveryApiOptionsMonitorMock.SetupGet(s => s.CurrentValue).Returns(deliveryApiSettings);
return deliveryApiOptionsMonitorMock.Object;
}
+ private ApiPublishedContentCache CreateApiPublishedContentCache(IOptionsMonitor deliveryApiSettings, string? culture = null)
+ => new(CreateRequestPreviewService(), deliveryApiSettings, CreateApiDocumentUrlService(), _contentCache, CreateVariationContextAccessor(culture));
+
private IApiDocumentUrlService CreateApiDocumentUrlService() => new ApiDocumentUrlService(_documentUrlService);
}