Skip to content

Commit

Permalink
Don't require AttributeProvider for JsonPropertyInfo-based modificati…
Browse files Browse the repository at this point in the history
…ons (#56712)

* Don't require AttributeProvider for JsonPropertyInfo-based modifications

* Add test coverage for fix
  • Loading branch information
captainsafia authored Jul 10, 2024
1 parent d498542 commit dc1a065
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 2 deletions.
5 changes: 4 additions & 1 deletion src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ internal sealed class OpenApiSchemaService(
schema.ApplyPrimitiveTypesAndFormats(context);
schema.ApplySchemaReferenceId(context);
schema.ApplyPolymorphismOptions(context);
if (context.PropertyInfo is { AttributeProvider: { } attributeProvider } jsonPropertyInfo)
if (context.PropertyInfo is { } jsonPropertyInfo)
{
// Short-circuit STJ's handling of nested properties, which uses a reference to the
// properties type schema with a schema that uses a document level reference.
Expand All @@ -102,6 +102,9 @@ internal sealed class OpenApiSchemaService(
return new JsonObject { [OpenApiSchemaKeywords.RefKeyword] = context.TypeInfo.GetSchemaReferenceId() };
}
schema.ApplyNullabilityContextInfo(jsonPropertyInfo);
}
if (context.PropertyInfo is { AttributeProvider: { } attributeProvider })
{
if (attributeProvider.GetCustomAttributes(inherit: false).OfType<ValidationAttribute>() is { } validationAttributes)
{
schema.ApplyValidationAttributes(validationAttributes);
Expand Down
3 changes: 2 additions & 1 deletion src/OpenApi/test/Services/OpenApiDocumentServiceTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ internal static OpenApiDocumentService CreateDocumentService(IEndpointRouteBuild
provider.OnProvidersExecuted(context);

var apiDescriptionGroupCollectionProvider = CreateApiDescriptionGroupCollectionProvider(context.Results);
var jsonOptions = builder.ServiceProvider.GetService<IOptions<Microsoft.AspNetCore.Http.Json.JsonOptions>>() ?? Options.Create(new Microsoft.AspNetCore.Http.Json.JsonOptions());

var schemaService = new OpenApiSchemaService("Test", Options.Create(new Microsoft.AspNetCore.Http.Json.JsonOptions()), builder.ServiceProvider, options.Object);
var schemaService = new OpenApiSchemaService("Test", jsonOptions, builder.ServiceProvider, options.Object);
((TestServiceProvider)builder.ServiceProvider).TestSchemaService = schemaService;
var documentService = new OpenApiDocumentService("Test", apiDescriptionGroupCollectionProvider, hostEnvironment, options.Object, builder.ServiceProvider, new OpenApiTestServer());
((TestServiceProvider)builder.ServiceProvider).TestDocumentService = documentService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.IO.Pipelines;
using System.Text.Json.Serialization.Metadata;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;

Expand Down Expand Up @@ -468,6 +470,51 @@ await VerifyOpenApiDocument(builder, document =>
});
}

[Fact]
public async Task SupportsNestedTypes_WithNoAttributeProvider()
{
// Arrange: this test ensures that we can correctly handle the scenario
// where the attribute provider is null and we need to patch the property mappings
// that are created by the underlying JsonSchemaExporter.
var serviceCollection = new ServiceCollection();
serviceCollection.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.TypeInfoResolver = options.SerializerOptions.TypeInfoResolver?.WithAddedModifier(jsonTypeInfo =>
{
foreach (var propertyInfo in jsonTypeInfo.Properties)
{
propertyInfo.AttributeProvider = null;
}
});
});
var builder = CreateBuilder(serviceCollection);

// Act
builder.MapPost("/api", (NestedType type) => { });

// Assert
await VerifyOpenApiDocument(builder, document =>
{
var operation = document.Paths["/api"].Operations[OperationType.Post];
var requestBody = operation.RequestBody;
var content = Assert.Single(requestBody.Content);
Assert.Equal("NestedType", content.Value.Schema.Reference.Id);
var schema = content.Value.Schema.GetEffective(document);
Assert.Collection(schema.Properties,
property =>
{
Assert.Equal("name", property.Key);
Assert.Equal("string", property.Value.Type);
},
property =>
{
Assert.Equal("nested", property.Key);
Assert.Equal("NestedType", property.Value.Reference.Id);
});
});
}

private class DescriptionTodo
{
[Description("The unique identifier for a todo item.")]
Expand Down

0 comments on commit dc1a065

Please sign in to comment.