Skip to content

Commit c495836

Browse files
authored
Support OpenAPI polymorphic output with JsonDerivedType (#16144)
1 parent fc9b47a commit c495836

File tree

5 files changed

+36
-4
lines changed

5 files changed

+36
-4
lines changed

src/Umbraco.Cms.Api.Management/OpenApi/IOpenApiDiscriminator.cs renamed to src/Umbraco.Cms.Api.Common/OpenApi/IOpenApiDiscriminator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace Umbraco.Cms.Api.Management.OpenApi;
1+
namespace Umbraco.Cms.Api.Common.OpenApi;
22

33
/// <summary>
44
/// Marker interface that ensure the type have a "$type" discriminator in the open api schema.

src/Umbraco.Cms.Api.Common/Serialization/IUmbracoJsonTypeInfoResolver.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ namespace Umbraco.Cms.Api.Common.Serialization;
55
public interface IUmbracoJsonTypeInfoResolver : IJsonTypeInfoResolver
66
{
77
IEnumerable<Type> FindSubTypes(Type type);
8+
9+
string? GetTypeDiscriminatorValue(Type type);
810
}

src/Umbraco.Cms.Api.Common/Serialization/UmbracoJsonTypeInfoResolver.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
using System.Collections.Concurrent;
22
using System.Text.Json;
3+
using System.Text.Json.Serialization;
34
using System.Text.Json.Serialization.Metadata;
5+
using Umbraco.Cms.Api.Common.OpenApi;
46
using Umbraco.Cms.Core.Composing;
7+
using Umbraco.Extensions;
58

69
namespace Umbraco.Cms.Api.Common.Serialization;
710

@@ -15,6 +18,14 @@ public UmbracoJsonTypeInfoResolver(ITypeFinder typeFinder)
1518

1619
public IEnumerable<Type> FindSubTypes(Type type)
1720
{
21+
JsonDerivedTypeAttribute[] explicitJsonDerivedTypes = type
22+
.GetCustomAttributes<JsonDerivedTypeAttribute>(false)
23+
.ToArray();
24+
if (explicitJsonDerivedTypes.Any())
25+
{
26+
return explicitJsonDerivedTypes.Select(a => a.DerivedType);
27+
}
28+
1829
if (type.IsInterface is false)
1930
{
2031
// IMPORTANT: do NOT return an empty enumerable here. it will cause nullability to fail on reference
@@ -33,6 +44,24 @@ public IEnumerable<Type> FindSubTypes(Type type)
3344
return result;
3445
}
3546

47+
public string? GetTypeDiscriminatorValue(Type type)
48+
{
49+
JsonDerivedTypeAttribute? jsonDerivedTypeAttribute = type
50+
.GetBaseTypes(false)
51+
.WhereNotNull()
52+
.SelectMany(baseType => baseType.GetCustomAttributes<JsonDerivedTypeAttribute>(false))
53+
.FirstOrDefault(attr => attr.DerivedType == type);
54+
55+
if (jsonDerivedTypeAttribute is not null)
56+
{
57+
// IMPORTANT: do NOT perform fallback to type.Name here - it will work for the schema generation,
58+
// but not for the actual serialization, and then it's only going to cause confusion.
59+
return jsonDerivedTypeAttribute.TypeDiscriminator?.ToString();
60+
}
61+
62+
return typeof(IOpenApiDiscriminator).IsAssignableFrom(type) ? type.Name : null;
63+
}
64+
3665
public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
3766
{
3867
JsonTypeInfo result = base.GetTypeInfo(type, options);

src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Microsoft.Extensions.Options;
33
using Microsoft.OpenApi.Models;
44
using Swashbuckle.AspNetCore.SwaggerGen;
5+
using Umbraco.Cms.Api.Common.OpenApi;
56
using Umbraco.Cms.Api.Common.Serialization;
67
using Umbraco.Cms.Api.Management.DependencyInjection;
78
using Umbraco.Cms.Api.Management.OpenApi;
@@ -34,8 +35,8 @@ public void Configure(SwaggerGenOptions swaggerGenOptions)
3435
swaggerGenOptions.UseOneOfForPolymorphism();
3536

3637
// Ensure all types that implements the IOpenApiDiscriminator have a $type property in the OpenApi schema with the default value (The class name) that is expected by the server
37-
swaggerGenOptions.SelectDiscriminatorNameUsing(type => typeof(IOpenApiDiscriminator).IsAssignableFrom(type) ? "$type" : null);
38-
swaggerGenOptions.SelectDiscriminatorValueUsing(type => typeof(IOpenApiDiscriminator).IsAssignableFrom(type) ? type.Name : null);
38+
swaggerGenOptions.SelectDiscriminatorNameUsing(type => _umbracoJsonTypeInfoResolver.GetTypeDiscriminatorValue(type) is not null ? "$type" : null);
39+
swaggerGenOptions.SelectDiscriminatorValueUsing(_umbracoJsonTypeInfoResolver.GetTypeDiscriminatorValue);
3940

4041

4142
swaggerGenOptions.AddSecurityDefinition(

src/Umbraco.Cms.Api.Management/ViewModels/UserGroup/Permissions/IPermissionPresentationModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Umbraco.Cms.Api.Management.OpenApi;
1+
using Umbraco.Cms.Api.Common.OpenApi;
22

33
namespace Umbraco.Cms.Api.Management.ViewModels.UserGroup.Permissions;
44

0 commit comments

Comments
 (0)