Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument documen
if (schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var schemaId) &&
schemaId is string schemaIdString)
{
return document.AddOpenApiSchemaByReference(schemaIdString, schema);
return new OpenApiSchemaReference(schemaIdString, document);
}
var relativeSchemaId = $"#/components/schemas/{rootSchemaId}{refIdString.Replace("#", string.Empty)}";
return new OpenApiSchemaReference(relativeSchemaId, document);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,124 @@ await VerifyOpenApiDocument(builder, document =>
});
}

// Test for: https://github.com/dotnet/aspnetcore/issues/63503
[Fact]
public async Task HandlesCircularReferencesRegardlessOfPropertyOrder_SelfFirst()
{
var builder = CreateBuilder();
builder.MapPost("/", (DirectCircularModelSelfFirst dto) => { });

// Assert
await VerifyOpenApiDocument(builder, document =>
{
Assert.NotNull(document.Components?.Schemas);
var schema = document.Components.Schemas["DirectCircularModelSelfFirst"];
Assert.Equal(JsonSchemaType.Object, schema.Type);
Assert.NotNull(schema.Properties);
Assert.Collection(schema.Properties,
property =>
{
Assert.Equal("self", property.Key);
var reference = Assert.IsType<OpenApiSchemaReference>(property.Value);
Assert.Equal("#/components/schemas/DirectCircularModelSelfFirst", reference.Reference.ReferenceV3);
},
property =>
{
Assert.Equal("referenced", property.Key);
var reference = Assert.IsType<OpenApiSchemaReference>(property.Value);
});

// Verify that it does not result in an empty schema for a referenced schema
var referencedSchema = document.Components.Schemas["ReferencedModel"];
Assert.NotNull(referencedSchema.Properties);
Assert.NotEmpty(referencedSchema.Properties);
var idProperty = Assert.Single(referencedSchema.Properties);
Assert.Equal("id", idProperty.Key);
var idPropertySchema = Assert.IsType<OpenApiSchema>(idProperty.Value);
Assert.Equal(JsonSchemaType.Integer, idPropertySchema.Type);
});
}

// Test for: https://github.com/dotnet/aspnetcore/issues/63503
[Fact]
public async Task HandlesCircularReferencesRegardlessOfPropertyOrder_SelfLast()
{
var builder = CreateBuilder();
builder.MapPost("/", (DirectCircularModelSelfLast dto) => { });

await VerifyOpenApiDocument(builder, document =>
{
Assert.NotNull(document.Components?.Schemas);
var schema = document.Components.Schemas["DirectCircularModelSelfLast"];
Assert.Equal(JsonSchemaType.Object, schema.Type);
Assert.NotNull(schema.Properties);
Assert.Collection(schema.Properties,
property =>
{
Assert.Equal("referenced", property.Key);
var reference = Assert.IsType<OpenApiSchemaReference>(property.Value);
},
property =>
{
Assert.Equal("self", property.Key);
var reference = Assert.IsType<OpenApiSchemaReference>(property.Value);
Assert.Equal("#/components/schemas/DirectCircularModelSelfLast", reference.Reference.ReferenceV3);
});

// Verify that it does not result in an empty schema for a referenced schema
var referencedSchema = document.Components.Schemas["ReferencedModel"];
Assert.NotNull(referencedSchema.Properties);
Assert.NotEmpty(referencedSchema.Properties);
var idProperty = Assert.Single(referencedSchema.Properties);
Assert.Equal("id", idProperty.Key);
var idPropertySchema = Assert.IsType<OpenApiSchema>(idProperty.Value);
Assert.Equal(JsonSchemaType.Integer, idPropertySchema.Type);
});
}

// Test for: https://github.com/dotnet/aspnetcore/issues/63503
[Fact]
public async Task HandlesCircularReferencesRegardlessOfPropertyOrder_MultipleSelf()
{
var builder = CreateBuilder();
builder.MapPost("/", (DirectCircularModelMultiple dto) => { });

await VerifyOpenApiDocument(builder, document =>
{
Assert.NotNull(document.Components?.Schemas);
var schema = document.Components.Schemas["DirectCircularModelMultiple"];
Assert.Equal(JsonSchemaType.Object, schema.Type);
Assert.NotNull(schema.Properties);
Assert.Collection(schema.Properties,
property =>
{
Assert.Equal("selfFirst", property.Key);
var reference = Assert.IsType<OpenApiSchemaReference>(property.Value);
Assert.Equal("#/components/schemas/DirectCircularModelMultiple", reference.Reference.ReferenceV3);
},
property =>
{
Assert.Equal("referenced", property.Key);
var reference = Assert.IsType<OpenApiSchemaReference>(property.Value);
},
property =>
{
Assert.Equal("selfLast", property.Key);
var reference = Assert.IsType<OpenApiSchemaReference>(property.Value);
Assert.Equal("#/components/schemas/DirectCircularModelMultiple", reference.Reference.ReferenceV3);
});

// Verify that it does not result in an empty schema for a referenced schema
var referencedSchema = document.Components.Schemas["ReferencedModel"];
Assert.NotNull(referencedSchema.Properties);
Assert.NotEmpty(referencedSchema.Properties);
var idProperty = Assert.Single(referencedSchema.Properties);
Assert.Equal("id", idProperty.Key);
var idPropertySchema = Assert.IsType<OpenApiSchema>(idProperty.Value);
Assert.Equal(JsonSchemaType.Integer, idPropertySchema.Type);
});
}

// Test models for issue 61194
private class Config
{
Expand Down Expand Up @@ -1060,5 +1178,30 @@ public sealed class RefUser
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}

// Test models for issue 63503
private class DirectCircularModelSelfFirst
{
public DirectCircularModelSelfFirst Self { get; set; } = null!;
public ReferencedModel Referenced { get; set; } = null!;
}

private class DirectCircularModelSelfLast
{
public ReferencedModel Referenced { get; set; } = null!;
public DirectCircularModelSelfLast Self { get; set; } = null!;
}

private class DirectCircularModelMultiple
{
public DirectCircularModelMultiple SelfFirst { get; set; } = null!;
public ReferencedModel Referenced { get; set; } = null!;
public DirectCircularModelMultiple SelfLast { get; set; } = null!;
}

private class ReferencedModel
{
public int Id { get; set; }
}
}
#nullable restore
Loading