Skip to content

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

Closed
@captainsafia

Description

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

  • Provide TypeInfo and PropertyInfo instead of JsonTypeInfo and JsonPropertyInfo?
  • Should properties only expose public accessors instead of initializers as well?

Risks

  • This API shape requires that users apply a null-check to disambiguate if they are in the schema for a property or not.
  • This API shape does not provide a consistent way to determine if a sub-schema that is being generated is in the anyOf of a parent type.

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcarea-mvcIncludes: MVC, Actions and Controllers, Localization, CORS, most templatesfeature-openapi

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions