dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.3k stars 9.96k forks source link

Expose additional info about properties and JSON types in OpenApiSchemaTransformerContext #56584

Closed captainsafia closed 2 months ago

captainsafia commented 3 months ago

Background and Motivation

OpenAPI schema transformers currently only run on schemas that are generated at the top-level for parameters and responses. For example, in the following case:

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options => 
{
    options.UseSchemaTransformer((schema, context, ct) =>
    {
        if (context.Type == typeof(int))
        {
            schema.Format = "some-custom-format";
        }
    }
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/todos/{id}", (int id) => Results.Ok<Todo>(new Todo()));

public record Todo(int Id, string Name, bool IsCompleted);

The transformer will only apply the modification on the schema generated for the id route parameter and not the id property within Todo. To resolve this issue, schemas need to be applied recursively into properties and child schemas (like those in anyOf and allOf). To support this, the implementation will apply transformers on child schemas. As part of this work, we need to expose additional APIs on OpenApiSchemaTransformerContext in order to be able to analyze

Proposed API

// Assembly: Microsoft.AspNetCore.OpenApi;

namespace Microsoft.AspNetCore.OpenApi;

public sealed class OpenApiSchemaTransformerContext
{
+   public JsonTypeInfo TypeInfo { get; init; }
+   public JsonPropertyInfo? PropertyInfo { get; init; }
}

Usage Examples

var builder = WebApplication.CreateBuilder();

builder.Services.AddOpenApi(options => 
{
    options.UseSchemaTransformer((schema, context, ct) =>
    {
        if (context.Type == typeof(int))
        {
            if (context.PropertyInfo is { AttributeProvider: { } attributeProvider } jsonPropertyInfo)
            {
                if (attributeProvider.GetCustomAttributes(inherit: false).OfType<MyCustomAttribute>().LastOrDefault() is {} attr)
                {
                    schema.Description = attr.Value;
                }
            }
            schema.Format = "some-custom-format";
        }
    }
});

var app = builder.Build();

app.MapOpenApi();

app.MapGet("/todos/{id}", (int id) => Results.Ok<Todo>(new Todo()));

public record Todo(int Id, string Name, bool IsCompleted);

Alternative Designs

Risks

dotnet-policy-service[bot] commented 3 months ago

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

mikekistler commented 3 months ago

Are you expecting users to code the recursion into subschemas themselves or will that be done by the framework? I'm hoping it is the latter, and that the new context fields are needed to implement that.

captainsafia commented 3 months ago

It's the later. The implementation referenced in the proposal is referring to the framework.

To support this, the implementation will apply transformers on child schemas. As part of this work, we need to expose additional APIs on OpenApiSchemaTransformerContext in order to be able to analyze

halter73 commented 2 months ago

API Review Notes:

API Approved!

// Assembly: Microsoft.AspNetCore.OpenApi;

namespace Microsoft.AspNetCore.OpenApi;

public sealed class OpenApiSchemaTransformerContext
{
-   public required Type Type { get; init; }
+   public required JsonTypeInfo JsonTypeInfo { get; init; }
+   public JsonPropertyInfo? JsonPropertyInfo { get; init; }
}