From 6f081785e69950b4deec98cb349b9de31486ff00 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:20:19 +0100 Subject: [PATCH] V15: Fix webhook RTE serialization (#17656) * Make base type resolver class * Add new webhook serializer * fix comment * Update src/Umbraco.Infrastructure/Serialization/ContentJsonTypeResolverBase.cs Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> * Update src/Umbraco.Core/Services/WebhookRequestService.cs --------- Co-authored-by: Elitsa Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> --- .../Json/DeliveryApiJsonTypeResolver.cs | 55 +------------------ .../DependencyInjection/UmbracoBuilder.cs | 8 ++- .../Serialization/IWebhookJsonSerializer.cs | 4 ++ .../Services/WebhookRequestService.cs | 16 ++++-- .../UmbracoBuilder.CoreServices.cs | 1 + .../ContentJsonTypeResolverBase.cs | 55 +++++++++++++++++++ .../SystemTextWebhookJsonSerializer.cs | 31 +++++++++++ .../Serialization/WebhookJsonTypeResolver.cs | 4 ++ 8 files changed, 115 insertions(+), 59 deletions(-) create mode 100644 src/Umbraco.Core/Serialization/IWebhookJsonSerializer.cs create mode 100644 src/Umbraco.Infrastructure/Serialization/ContentJsonTypeResolverBase.cs create mode 100644 src/Umbraco.Infrastructure/Serialization/SystemTextWebhookJsonSerializer.cs create mode 100644 src/Umbraco.Infrastructure/Serialization/WebhookJsonTypeResolver.cs diff --git a/src/Umbraco.Cms.Api.Delivery/Json/DeliveryApiJsonTypeResolver.cs b/src/Umbraco.Cms.Api.Delivery/Json/DeliveryApiJsonTypeResolver.cs index b22e7c9341a3..55799bfdb8ff 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 5e06ea504208..f5157271c57b 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -26,8 +26,6 @@ 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; @@ -37,6 +35,7 @@ using Umbraco.Cms.Core.DynamicRoot; using Umbraco.Cms.Core.Preview; 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.Navigation; @@ -403,7 +402,10 @@ private void AddCoreServices() 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 000000000000..ba8d1ce68612 --- /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 249ed21421ab..dae4d4afac29 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 async Task CreateAsync(Guid webhookKey, string eventAlias { 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 e467c0a8a3db..50ae0d7deb7d 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -124,6 +124,7 @@ public static IUmbracoBuilder AddCoreInitialServices(this IUmbracoBuilder builde 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 000000000000..74fc7e14827d --- /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 000000000000..b75c92073c19 --- /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 000000000000..e47c06ab9d97 --- /dev/null +++ b/src/Umbraco.Infrastructure/Serialization/WebhookJsonTypeResolver.cs @@ -0,0 +1,4 @@ +namespace Umbraco.Cms.Infrastructure.Serialization; + +public class WebhookJsonTypeResolver : ContentJsonTypeResolverBase +{ }