Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SupportNonNullableReferenceTypes not working for nested record types #2758

Closed
michael-wolfenden opened this issue Jan 10, 2024 · 3 comments
Closed
Labels
bug help-wanted A change up for grabs for contributions from the community

Comments

@michael-wolfenden
Copy link

michael-wolfenden commented Jan 10, 2024

Using Swashbuckle.AspNetCore 6.5.0

Given the following:

async Task Main()
{
    var builder = WebApplication.CreateBuilder();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(options =>
    {
        options.SupportNonNullableReferenceTypes();
    });
    
    var app = builder.Build();
    app.UseSwagger();
    app.UseSwaggerUI();

    app.MapPost("/requestWithNestedChild", (Requests.RequestWithNestedChild request) => "ok");
    app.MapPost("/requestWithNonNestedChild", (Requests.RequestWithNonNestedChild request) => "ok");

    app.Run("http://*:0");
}

public class Requests
{
    public record RequestWithNestedChild(RequestWithNestedChild.NestedChild Child)
    {
        public record NestedChild(string NonNullable, string? Nullable);
    }

    public record RequestWithNonNestedChild(Child Child);
    public record Child(string NonNullable, string? Nullable);
}

The schema for Child correctly infers nullability

{
    "Child": {
        "type": "object",
        "properties": {
            "nonNullable": {
                "type": "string"
            },
            "nullable": {
                "type": "string",
                "nullable": true
            }
        },
        "additionalProperties": false
    }
}

However the schema for NestedChild incorrectly marks nonNullable as nullable

{
    "NestedChild": {
        "type": "object",
        "properties": {
            "nonNullable": {
                "type": "string",
                "nullable": true
            },
            "nullable": {
                "type": "string",
                "nullable": true
            }
        },
        "additionalProperties": false
    }
}

If I move these records outside of the Request class then nullability is inferred correctly for both

async Task Main()
{
    var builder = WebApplication.CreateBuilder();
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(options =>
    {
        options.SupportNonNullableReferenceTypes();
    });
    
    var app = builder.Build();
    app.UseSwagger();
    app.UseSwaggerUI();

    app.MapPost("/requestWithNestedChild", (Requests.RequestWithNestedChild request) => "ok");
    app.MapPost("/requestWithNonNestedChild", (Requests.RequestWithNonNestedChild request) => "ok");

    app.Run("http://*:0");
}

public record RequestWithNestedChild(RequestWithNestedChild.NestedChild Child)
{
    public record NestedChild(string NonNullable, string? Nullable);
}

public record RequestWithNonNestedChild(Child Child);
public record Child(string NonNullable, string? Nullable);

Schema

{
    "Child": {
        "type": "object",
        "properties": {
            "nonNullable": {
                "type": "string"
            },
            "nullable": {
                "type": "string",
                "nullable": true
            }
        },
        "additionalProperties": false
    },
    "NestedChild": {
        "type": "object",
        "properties": {
            "nonNullable": {
                "type": "string"
            },
            "nullable": {
                "type": "string",
                "nullable": true
            }
        },
        "additionalProperties": false
    }
}
@michael-wolfenden michael-wolfenden changed the title SupportNonNullableReferenceTypes not working for nested types SupportNonNullableReferenceTypes not working for nested record types Jan 10, 2024
@craigsmitham
Copy link

I just encountered this issue as well.

@jscarle
Copy link

jscarle commented Mar 27, 2024

I can confirm this. Took me a while to find this issue. This is the really hacky patch I came up as a solution while this gets addressed:

public sealed class NullableSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (schema.Properties.Count == 0)
            return;

        var nullabilityInfoContext = new NullabilityInfoContext();
        var contextProperties = context.Type.GetProperties();

        foreach (var (name, property) in schema.Properties)
        {
            var contextProperty = contextProperties.FirstOrDefault(x => string.Equals(name, x.Name, StringComparison.OrdinalIgnoreCase));
            if (contextProperty is null)
                continue;

            var nullabilityInfo = nullabilityInfoContext.Create(contextProperty);

            // If nullability is unknown or ambiguous, we continue.
            if (nullabilityInfo is { ReadState: NullabilityState.Unknown, WriteState: NullabilityState.Unknown } || nullabilityInfo.ReadState != nullabilityInfo.WriteState)
                continue;

            var detectedNullability = property.Nullable;
            var reflectedNullability = nullabilityInfo.ReadState == NullabilityState.Nullable;

            if (detectedNullability != reflectedNullability)
                property.Nullable = reflectedNullability;
        }
    }
}

@martincostello martincostello added help-wanted A change up for grabs for contributions from the community bug labels Apr 14, 2024
@martincostello
Copy link
Collaborator

To make issue tracking a bit less overwhelming for the new maintainers (see #2778), I've created a new tracking issue to roll-up various nullability issues here: #2793.

We'll refer back to this issue from there and include it as part of resolving that issue, but I'm going to close this one to help prune the backlog.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug help-wanted A change up for grabs for contributions from the community
Projects
None yet
Development

No branches or pull requests

4 participants