Skip to content

Commit d27777c

Browse files
committed
Move Schema Annotations handling to a schema transformer
1 parent cd1d256 commit d27777c

File tree

5 files changed

+72
-24
lines changed

5 files changed

+72
-24
lines changed

src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,19 @@ public static IOpenApiSchema AddOpenApiSchemaByReference(this OpenApiDocument do
2626

2727
object? description = null;
2828
object? example = null;
29+
object? defaultValue = null;
2930
if (schema is OpenApiSchema actualSchema)
3031
{
3132
actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefDescriptionAnnotation, out description);
3233
actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefExampleAnnotation, out example);
34+
actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefDefaultAnnotation, out defaultValue);
3335
}
3436

3537
return new OpenApiSchemaReference(schemaId, document)
3638
{
3739
Description = description as string,
3840
Examples = example is JsonNode exampleJson ? [exampleJson] : null,
41+
Default = defaultValue is JsonNode defaultValueJson ? defaultValueJson : null,
3942
};
4043
}
4144
}

src/OpenApi/src/Services/OpenApiConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal static class OpenApiConstants
1313
internal const string DescriptionId = "x-aspnetcore-id";
1414
internal const string SchemaId = "x-schema-id";
1515
internal const string RefId = "x-ref-id";
16+
internal const string RefDefaultAnnotation = "x-ref-default";
1617
internal const string RefDescriptionAnnotation = "x-ref-description";
1718
internal const string RefExampleAnnotation = "x-ref-example";
1819
internal const string RefKeyword = "$ref";

src/OpenApi/src/Services/OpenApiOptions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Diagnostics.CodeAnalysis;
55
using System.Text.Json.Serialization.Metadata;
66
using Microsoft.AspNetCore.Mvc.ApiExplorer;
7+
using Microsoft.AspNetCore.OpenApi.Services.Schemas.Transformers;
78

89
namespace Microsoft.AspNetCore.OpenApi;
910

@@ -14,7 +15,7 @@ public sealed class OpenApiOptions
1415
{
1516
internal readonly List<IOpenApiDocumentTransformer> DocumentTransformers = [];
1617
internal readonly List<IOpenApiOperationTransformer> OperationTransformers = [];
17-
internal readonly List<IOpenApiSchemaTransformer> SchemaTransformers = [];
18+
internal readonly List<IOpenApiSchemaTransformer> SchemaTransformers = [new DescriptionDataAnnotationsSchemaTransformer()];
1819

1920
/// <summary>
2021
/// A default implementation for creating a schema reference ID for a given <see cref="JsonTypeInfo"/>.

src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,36 +101,13 @@ internal sealed class OpenApiSchemaService(
101101
{
102102
schema.ApplyNullabilityContextInfo(jsonPropertyInfo);
103103
}
104-
if (context.TypeInfo.Type.GetCustomAttributes(inherit: false).OfType<DescriptionAttribute>().LastOrDefault() is { } typeDescriptionAttribute)
105-
{
106-
schema[OpenApiSchemaKeywords.DescriptionKeyword] = typeDescriptionAttribute.Description;
107-
}
108104
if (context.PropertyInfo is { AttributeProvider: { } attributeProvider })
109105
{
110106
var propertyAttributes = attributeProvider.GetCustomAttributes(inherit: false);
111107
if (propertyAttributes.OfType<ValidationAttribute>() is { } validationAttributes)
112108
{
113109
schema.ApplyValidationAttributes(validationAttributes);
114110
}
115-
if (propertyAttributes.OfType<DefaultValueAttribute>().LastOrDefault() is { } defaultValueAttribute)
116-
{
117-
schema.ApplyDefaultValue(defaultValueAttribute.Value, context.TypeInfo);
118-
}
119-
var isInlinedSchema = schema[OpenApiConstants.SchemaId] is null;
120-
if (isInlinedSchema)
121-
{
122-
if (propertyAttributes.OfType<DescriptionAttribute>().LastOrDefault() is { } descriptionAttribute)
123-
{
124-
schema[OpenApiSchemaKeywords.DescriptionKeyword] = descriptionAttribute.Description;
125-
}
126-
}
127-
else
128-
{
129-
if (propertyAttributes.OfType<DescriptionAttribute>().LastOrDefault() is { } descriptionAttribute)
130-
{
131-
schema[OpenApiConstants.RefDescriptionAnnotation] = descriptionAttribute.Description;
132-
}
133-
}
134111
}
135112
schema.PruneNullTypeForComponentizedTypes();
136113
return schema;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
5+
using System.ComponentModel;
6+
using System.Linq;
7+
using System.Text.Json;
8+
using System.Text.Json.Nodes;
9+
using System.Text.Json.Serialization.Metadata;
10+
11+
namespace Microsoft.AspNetCore.OpenApi.Services.Schemas.Transformers;
12+
13+
internal class DescriptionDataAnnotationsSchemaTransformer : IOpenApiSchemaTransformer
14+
{
15+
public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken)
16+
{
17+
schema.Metadata ??= new Dictionary<string, object>();
18+
var isInlinedSchema = !schema.Metadata.ContainsKey(OpenApiConstants.SchemaId) || string.IsNullOrEmpty(schema.Metadata[OpenApiConstants.SchemaId] as string);
19+
if (context.JsonTypeInfo.Type.GetCustomAttributes(inherit: false).OfType<DescriptionAttribute>().LastOrDefault() is { } typeDescriptionAttribute)
20+
{
21+
schema.Description = typeDescriptionAttribute.Description;
22+
}
23+
24+
if (context.JsonPropertyInfo?.AttributeProvider?.GetCustomAttributes(inherit: false).OfType<DescriptionAttribute>().LastOrDefault() is { } propertyDescriptionAttribute)
25+
{
26+
if (isInlinedSchema)
27+
{
28+
schema.Description = propertyDescriptionAttribute.Description;
29+
}
30+
else
31+
{
32+
schema.Metadata![OpenApiConstants.RefDescriptionAnnotation] = propertyDescriptionAttribute.Description;
33+
}
34+
}
35+
36+
if (context.JsonTypeInfo.Type.GetCustomAttributes(inherit: false).OfType<DefaultValueAttribute>().LastOrDefault() is { } typeDefaultValueAttribute)
37+
{
38+
schema.Default = GetDefaultValueAsJsonNode(typeDefaultValueAttribute, context.JsonTypeInfo);
39+
}
40+
41+
if (context.JsonPropertyInfo?.AttributeProvider?.GetCustomAttributes(inherit: false).OfType<DefaultValueAttribute>().LastOrDefault() is { } propertyDefaultValueAttribute)
42+
{
43+
var defaultValueJson = GetDefaultValueAsJsonNode(propertyDefaultValueAttribute, context.JsonTypeInfo);
44+
if (isInlinedSchema)
45+
{
46+
schema.Default = defaultValueJson;
47+
}
48+
else
49+
{
50+
schema.Metadata![OpenApiConstants.RefDefaultAnnotation] = defaultValueJson!;
51+
}
52+
}
53+
54+
return Task.CompletedTask;
55+
}
56+
57+
private static JsonNode? GetDefaultValueAsJsonNode(DefaultValueAttribute defaultValueAttribute, JsonTypeInfo jsonTypeInfo)
58+
{
59+
if(defaultValueAttribute.Value is null)
60+
{
61+
return null;
62+
}
63+
64+
return JsonSerializer.SerializeToNode(defaultValueAttribute.Value, jsonTypeInfo);
65+
}
66+
}

0 commit comments

Comments
 (0)