Skip to content

Commit 12a0766

Browse files
committed
Add handling of XML Comments on properties with a schema reference with Metadata properties
1 parent 81df677 commit 12a0766

15 files changed

+194
-64
lines changed

src/OpenApi/gen/XmlCommentGenerator.Emitter.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -459,18 +459,29 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
459459
}
460460
}
461461
462-
var isInlinedSchema = schema.Metadata is null
463-
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
464-
|| string.IsNullOrEmpty(schemaId as string);
465-
if (isInlinedSchema && context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
462+
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
466463
{
467464
// Apply comments from the property
468465
if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment))
469466
{
470-
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
471-
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
467+
var isInlinedSchema = schema.Metadata is null
468+
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
469+
|| string.IsNullOrEmpty(schemaId as string);
470+
if(isInlinedSchema)
472471
{
473-
schema.Example = jsonString.Parse();
472+
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
473+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
474+
{
475+
schema.Example = jsonString.Parse();
476+
}
477+
}
478+
else
479+
{
480+
schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
481+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
482+
{
483+
schema.Metadata["x-ref-example"] = jsonString.Parse();
484+
}
474485
}
475486
}
476487
}

src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Text.Json.Nodes;
5+
46
namespace Microsoft.AspNetCore.OpenApi;
57

68
internal static class OpenApiDocumentExtensions
@@ -21,6 +23,19 @@ public static IOpenApiSchema AddOpenApiSchemaByReference(this OpenApiDocument do
2123
document.Workspace ??= new();
2224
var location = document.BaseUri + "/components/schemas/" + schemaId;
2325
document.Workspace.RegisterComponentForDocument(document, schema, location);
24-
return new OpenApiSchemaReference(schemaId, document);
26+
27+
object? description = null;
28+
object? example = null;
29+
if (schema is OpenApiSchema actualSchema)
30+
{
31+
actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefDescriptionAnnotation, out description);
32+
actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefExampleAnnotation, out example);
33+
}
34+
35+
return new OpenApiSchemaReference(schemaId, document)
36+
{
37+
Description = description as string,
38+
Examples = example is JsonNode exampleJson ? [exampleJson] : null,
39+
};
2540
}
2641
}

src/OpenApi/src/Services/OpenApiConstants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ 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 RefDescriptionAnnotation = "x-ref-description";
17+
internal const string RefExampleAnnotation = "x-ref-example";
1618
internal const string DefaultOpenApiResponseKey = "default";
1719
// Since there's a finite set of HTTP methods that can be included in a given
1820
// OpenApiPaths, we can pre-allocate an array of these methods and use a direct

src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,9 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document =>
516516
Assert.Equal("This class validates the behavior for mapping\ngeneric types to open generics for use in\ntypeof expressions.", genericParent.Description, ignoreLineEndingDifferences: true);
517517
Assert.Equal("This property is a nullable value type.", genericParent.Properties["id"].Description);
518518
Assert.Equal("This property is a nullable reference type.", genericParent.Properties["name"].Description);
519+
Assert.Equal("This property is a generic type containing a tuple.", genericParent.Properties["taskOfTupleProp"].Description);
520+
Assert.Equal("This property is a tuple with a generic type inside.", genericParent.Properties["tupleWithGenericProp"].Description);
521+
Assert.Equal("This property is a tuple with a nested generic type inside.", genericParent.Properties["tupleWithNestedGenericProp"].Description);
519522

520523
path = document.Paths["/params-and-param-refs"].Operations[HttpMethod.Post];
521524
var paramsAndParamRefs = path.RequestBody.Content["application/json"].Schema;

src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -441,18 +441,29 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
441441
}
442442
}
443443

444-
var isInlinedSchema = schema.Metadata is null
445-
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
446-
|| string.IsNullOrEmpty(schemaId as string);
447-
if (isInlinedSchema && context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
444+
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
448445
{
449446
// Apply comments from the property
450447
if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment))
451448
{
452-
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
453-
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
449+
var isInlinedSchema = schema.Metadata is null
450+
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
451+
|| string.IsNullOrEmpty(schemaId as string);
452+
if(isInlinedSchema)
454453
{
455-
schema.Example = jsonString.Parse();
454+
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
455+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
456+
{
457+
schema.Example = jsonString.Parse();
458+
}
459+
}
460+
else
461+
{
462+
schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
463+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
464+
{
465+
schema.Metadata["x-ref-example"] = jsonString.Parse();
466+
}
456467
}
457468
}
458469
}

src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -470,18 +470,29 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
470470
}
471471
}
472472

473-
var isInlinedSchema = schema.Metadata is null
474-
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
475-
|| string.IsNullOrEmpty(schemaId as string);
476-
if (isInlinedSchema && context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
473+
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
477474
{
478475
// Apply comments from the property
479476
if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment))
480477
{
481-
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
482-
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
478+
var isInlinedSchema = schema.Metadata is null
479+
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
480+
|| string.IsNullOrEmpty(schemaId as string);
481+
if(isInlinedSchema)
483482
{
484-
schema.Example = jsonString.Parse();
483+
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
484+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
485+
{
486+
schema.Example = jsonString.Parse();
487+
}
488+
}
489+
else
490+
{
491+
schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
492+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
493+
{
494+
schema.Metadata["x-ref-example"] = jsonString.Parse();
495+
}
485496
}
486497
}
487498
}

src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -562,18 +562,29 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
562562
}
563563
}
564564

565-
var isInlinedSchema = schema.Metadata is null
566-
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
567-
|| string.IsNullOrEmpty(schemaId as string);
568-
if (isInlinedSchema && context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
565+
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
569566
{
570567
// Apply comments from the property
571568
if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment))
572569
{
573-
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
574-
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
570+
var isInlinedSchema = schema.Metadata is null
571+
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
572+
|| string.IsNullOrEmpty(schemaId as string);
573+
if(isInlinedSchema)
575574
{
576-
schema.Example = jsonString.Parse();
575+
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
576+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
577+
{
578+
schema.Example = jsonString.Parse();
579+
}
580+
}
581+
else
582+
{
583+
schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
584+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
585+
{
586+
schema.Metadata["x-ref-example"] = jsonString.Parse();
587+
}
577588
}
578589
}
579590
}

src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas_openapi.json.verified.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,12 +391,15 @@
391391
"description": "This property is a nullable reference type."
392392
},
393393
"taskOfTupleProp": {
394+
"description": "This property is a generic type containing a tuple.",
394395
"$ref": "#/components/schemas/TaskOfValueTupleOfintAndstring"
395396
},
396397
"tupleWithGenericProp": {
398+
"description": "This property is a tuple with a generic type inside.",
397399
"$ref": "#/components/schemas/ValueTupleOfintAndDictionaryOfintAndstring"
398400
},
399401
"tupleWithNestedGenericProp": {
402+
"description": "This property is a tuple with a nested generic type inside.",
400403
"$ref": "#/components/schemas/ValueTupleOfintAndDictionaryOfintAndDictionaryOfstringAndint"
401404
}
402405
},

src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -445,18 +445,29 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
445445
}
446446
}
447447

448-
var isInlinedSchema = schema.Metadata is null
449-
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
450-
|| string.IsNullOrEmpty(schemaId as string);
451-
if (isInlinedSchema && context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
448+
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
452449
{
453450
// Apply comments from the property
454451
if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment))
455452
{
456-
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
457-
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
453+
var isInlinedSchema = schema.Metadata is null
454+
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
455+
|| string.IsNullOrEmpty(schemaId as string);
456+
if(isInlinedSchema)
458457
{
459-
schema.Example = jsonString.Parse();
458+
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
459+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
460+
{
461+
schema.Example = jsonString.Parse();
462+
}
463+
}
464+
else
465+
{
466+
schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
467+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
468+
{
469+
schema.Metadata["x-ref-example"] = jsonString.Parse();
470+
}
460471
}
461472
}
462473
}

src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -463,18 +463,29 @@ public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext
463463
}
464464
}
465465

466-
var isInlinedSchema = schema.Metadata is null
467-
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
468-
|| string.IsNullOrEmpty(schemaId as string);
469-
if (isInlinedSchema && context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
466+
if (context.JsonPropertyInfo is { AttributeProvider: PropertyInfo propertyInfo })
470467
{
471468
// Apply comments from the property
472469
if (XmlCommentCache.Cache.TryGetValue(DocumentationCommentIdHelper.NormalizeDocId(propertyInfo.CreateDocumentationId()), out var propertyComment))
473470
{
474-
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
475-
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
471+
var isInlinedSchema = schema.Metadata is null
472+
|| !schema.Metadata.TryGetValue("x-schema-id", out var schemaId)
473+
|| string.IsNullOrEmpty(schemaId as string);
474+
if(isInlinedSchema)
476475
{
477-
schema.Example = jsonString.Parse();
476+
schema.Description = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
477+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
478+
{
479+
schema.Example = jsonString.Parse();
480+
}
481+
}
482+
else
483+
{
484+
schema.Metadata["x-ref-description"] = propertyComment.Value ?? propertyComment.Returns ?? propertyComment.Summary;
485+
if (propertyComment.Examples?.FirstOrDefault() is { } jsonString)
486+
{
487+
schema.Metadata["x-ref-example"] = jsonString.Parse();
488+
}
478489
}
479490
}
480491
}

0 commit comments

Comments
 (0)