diff --git a/src/Umbraco.Cms.Api.Delivery/Json/DeliveryApiJsonTypeResolver.cs b/src/Umbraco.Cms.Api.Delivery/Json/DeliveryApiJsonTypeResolver.cs index b22e7c9341..55799bfdb8 100644 --- a/src/Umbraco.Cms.Api.Delivery/Json/DeliveryApiJsonTypeResolver.cs +++ b/src/Umbraco.Cms.Api.Delivery/Json/DeliveryApiJsonTypeResolver.cs @@ -1,56 +1,7 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; -using Umbraco.Cms.Core.Models.DeliveryApi; +using Umbraco.Cms.Infrastructure.Serialization; namespace Umbraco.Cms.Api.Delivery.Json; // see https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/polymorphism?pivots=dotnet-7-0 -public class DeliveryApiJsonTypeResolver : DefaultJsonTypeInfoResolver -{ - public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) - { - JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options); - - Type[] derivedTypes = GetDerivedTypes(jsonTypeInfo); - if (derivedTypes.Length > 0) - { - ConfigureJsonPolymorphismOptions(jsonTypeInfo, derivedTypes); - } - - return jsonTypeInfo; - } - - protected virtual Type[] GetDerivedTypes(JsonTypeInfo jsonTypeInfo) - { - if (jsonTypeInfo.Type == typeof(IApiContent)) - { - return new[] { typeof(ApiContent) }; - } - - if (jsonTypeInfo.Type == typeof(IApiContentResponse)) - { - return new[] { typeof(ApiContentResponse) }; - } - - if (jsonTypeInfo.Type == typeof(IRichTextElement)) - { - return new[] { typeof(RichTextRootElement), typeof(RichTextGenericElement), typeof(RichTextTextElement) }; - } - - return Array.Empty(); - } - - protected void ConfigureJsonPolymorphismOptions(JsonTypeInfo jsonTypeInfo, params Type[] derivedTypes) - { - jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions - { - UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization, - }; - - foreach (Type derivedType in derivedTypes) - { - jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(derivedType)); - } - } -} +public class DeliveryApiJsonTypeResolver : ContentJsonTypeResolverBase +{ } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index ffeafe17b7..5abbe12f9f 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -26,8 +26,6 @@ using Umbraco.Cms.Core.Notifications; using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.PublishedCache.Internal; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Runtime; using Umbraco.Cms.Core.Scoping; @@ -36,7 +34,10 @@ using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.ContentTypeEditing; using Umbraco.Cms.Core.DynamicRoot; using Umbraco.Cms.Core.Preview; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Core.PublishedCache.Internal; using Umbraco.Cms.Core.Security.Authorization; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services.FileSystem; using Umbraco.Cms.Core.Services.ImportExport; using Umbraco.Cms.Core.Services.Querying.RecycleBin; @@ -373,7 +374,10 @@ namespace Umbraco.Cms.Core.DependencyInjection Services.AddUnique(); Services.AddUnique(); Services.AddUnique(); - Services.AddUnique(); + Services.AddUnique(factory => new WebhookRequestService( + factory.GetRequiredService(), + factory.GetRequiredService(), + factory.GetRequiredService())); // Data type configuration cache Services.AddUnique(); diff --git a/src/Umbraco.Core/Serialization/IWebhookJsonSerializer.cs b/src/Umbraco.Core/Serialization/IWebhookJsonSerializer.cs new file mode 100644 index 0000000000..ba8d1ce686 --- /dev/null +++ b/src/Umbraco.Core/Serialization/IWebhookJsonSerializer.cs @@ -0,0 +1,4 @@ +namespace Umbraco.Cms.Core.Serialization; + +public interface IWebhookJsonSerializer : IJsonSerializer +{ } diff --git a/src/Umbraco.Core/Services/WebhookRequestService.cs b/src/Umbraco.Core/Services/WebhookRequestService.cs index 249ed21421..dae4d4afac 100644 --- a/src/Umbraco.Core/Services/WebhookRequestService.cs +++ b/src/Umbraco.Core/Services/WebhookRequestService.cs @@ -1,4 +1,6 @@ -using Umbraco.Cms.Core.Models; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Serialization; @@ -9,13 +11,19 @@ public class WebhookRequestService : IWebhookRequestService { private readonly ICoreScopeProvider _coreScopeProvider; private readonly IWebhookRequestRepository _webhookRequestRepository; - private readonly IJsonSerializer _jsonSerializer; + private readonly IWebhookJsonSerializer _webhookJsonSerializer; + [Obsolete("This constructor is obsolete and will be removed in future versions. Scheduled for removal in V17")] public WebhookRequestService(ICoreScopeProvider coreScopeProvider, IWebhookRequestRepository webhookRequestRepository, IJsonSerializer jsonSerializer) + : this (coreScopeProvider, webhookRequestRepository, StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public WebhookRequestService(ICoreScopeProvider coreScopeProvider, IWebhookRequestRepository webhookRequestRepository, IWebhookJsonSerializer webhookJsonSerializer) { _coreScopeProvider = coreScopeProvider; _webhookRequestRepository = webhookRequestRepository; - _jsonSerializer = jsonSerializer; + _webhookJsonSerializer = webhookJsonSerializer; } public async Task CreateAsync(Guid webhookKey, string eventAlias, object? payload) @@ -26,7 +34,7 @@ public class WebhookRequestService : IWebhookRequestService { WebhookKey = webhookKey, EventAlias = eventAlias, - RequestObject = _jsonSerializer.Serialize(payload), + RequestObject = _webhookJsonSerializer.Serialize(payload), RetryCount = 0, }; diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 6d99898cd6..f35495e7a6 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -123,6 +123,7 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddUnique(); // register database builder // *not* a singleton, don't want to keep it around diff --git a/src/Umbraco.Infrastructure/Serialization/ContentJsonTypeResolverBase.cs b/src/Umbraco.Infrastructure/Serialization/ContentJsonTypeResolverBase.cs new file mode 100644 index 0000000000..74fc7e1482 --- /dev/null +++ b/src/Umbraco.Infrastructure/Serialization/ContentJsonTypeResolverBase.cs @@ -0,0 +1,55 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; +using Umbraco.Cms.Core.Models.DeliveryApi; + +namespace Umbraco.Cms.Infrastructure.Serialization; + +public abstract class ContentJsonTypeResolverBase : DefaultJsonTypeInfoResolver +{ + public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) + { + JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options); + + Type[] derivedTypes = GetDerivedTypes(jsonTypeInfo); + if (derivedTypes.Length > 0) + { + ConfigureJsonPolymorphismOptions(jsonTypeInfo, derivedTypes); + } + + return jsonTypeInfo; + } + + public virtual Type[] GetDerivedTypes(JsonTypeInfo jsonTypeInfo) + { + if (jsonTypeInfo.Type == typeof(IApiContent)) + { + return new[] { typeof(ApiContent) }; + } + + if (jsonTypeInfo.Type == typeof(IApiContentResponse)) + { + return new[] { typeof(ApiContentResponse) }; + } + + if (jsonTypeInfo.Type == typeof(IRichTextElement)) + { + return new[] { typeof(RichTextRootElement), typeof(RichTextGenericElement), typeof(RichTextTextElement) }; + } + + return Array.Empty(); + } + + public void ConfigureJsonPolymorphismOptions(JsonTypeInfo jsonTypeInfo, params Type[] derivedTypes) + { + jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions + { + UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FailSerialization, + }; + + foreach (Type derivedType in derivedTypes) + { + jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(derivedType)); + } + } +} diff --git a/src/Umbraco.Infrastructure/Serialization/SystemTextWebhookJsonSerializer.cs b/src/Umbraco.Infrastructure/Serialization/SystemTextWebhookJsonSerializer.cs new file mode 100644 index 0000000000..b75c92073c --- /dev/null +++ b/src/Umbraco.Infrastructure/Serialization/SystemTextWebhookJsonSerializer.cs @@ -0,0 +1,31 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Umbraco.Cms.Core.Serialization; + +namespace Umbraco.Cms.Infrastructure.Serialization; + +/// +public sealed class SystemTextWebhookJsonSerializer : SystemTextJsonSerializerBase, IWebhookJsonSerializer +{ + private readonly JsonSerializerOptions _jsonSerializerOptions; + + /// + /// Initializes a new instance of the class. + /// + public SystemTextWebhookJsonSerializer() + => _jsonSerializerOptions = new JsonSerializerOptions() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + Converters = + { + new JsonStringEnumConverter(), + new JsonUdiConverter(), + new JsonUdiRangeConverter(), + new JsonObjectConverter(), // Required for block editor values + new JsonBlockValueConverter() + }, + TypeInfoResolver = new WebhookJsonTypeResolver(), + }; + + protected override JsonSerializerOptions JsonSerializerOptions => _jsonSerializerOptions; +} diff --git a/src/Umbraco.Infrastructure/Serialization/WebhookJsonTypeResolver.cs b/src/Umbraco.Infrastructure/Serialization/WebhookJsonTypeResolver.cs new file mode 100644 index 0000000000..e47c06ab9d --- /dev/null +++ b/src/Umbraco.Infrastructure/Serialization/WebhookJsonTypeResolver.cs @@ -0,0 +1,4 @@ +namespace Umbraco.Cms.Infrastructure.Serialization; + +public class WebhookJsonTypeResolver : ContentJsonTypeResolverBase +{ }