diff --git a/eng/Versions.props b/eng/Versions.props
index 1021eb1bceba..75eb1ccf6fd1 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -335,8 +335,8 @@
$(XunitVersion)
2.8.2
5.2.2
- 2.0.0-preview5
- 2.0.0-preview5
+ 2.0.0-preview7
+ 2.0.0-preview7
6.0.322601
1.10.93
diff --git a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
index f91854fa7825..73896cf85ecb 100644
--- a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
+++ b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
@@ -55,6 +55,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
+ using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Any;
{{GeneratedCodeAttribute}}
@@ -256,12 +257,15 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
if (operationParameter is not null)
{
- operationParameter.Description = parameterComment.Description;
+ var targetOperationParameter = operationParameter is OpenApiParameterReference reference
+ ? reference.Target
+ : (OpenApiParameter)operationParameter;
+ targetOperationParameter.Description = parameterComment.Description;
if (parameterComment.Example is { } jsonString)
{
- operationParameter.Example = jsonString.Parse();
+ targetOperationParameter.Example = jsonString.Parse();
}
- operationParameter.Deprecated = parameterComment.Deprecated;
+ targetOperationParameter.Deprecated = parameterComment.Deprecated;
}
else
{
diff --git a/src/OpenApi/sample/Program.cs b/src/OpenApi/sample/Program.cs
index 0db7b41a29c1..cc1899e40482 100644
--- a/src/OpenApi/sample/Program.cs
+++ b/src/OpenApi/sample/Program.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Text.Json.Serialization;
using Microsoft.OpenApi.Models;
using Sample.Transformers;
@@ -11,6 +12,13 @@
#pragma warning restore IL2026
builder.Services.AddAuthentication().AddJwtBearer();
+// Supports representing integer formats as strictly numerically values
+// inside the schema.
+builder.Services.ConfigureHttpJsonOptions(options =>
+{
+ options.SerializerOptions.NumberHandling = JsonNumberHandling.Strict;
+});
+
builder.Services.AddOpenApi("v1", options =>
{
options.AddHeader("X-Version", "1.0");
diff --git a/src/OpenApi/sample/Transformers/AddBearerSecuritySchemeTransformer.cs b/src/OpenApi/sample/Transformers/AddBearerSecuritySchemeTransformer.cs
index 02bd033b7628..05ea43c8580f 100644
--- a/src/OpenApi/sample/Transformers/AddBearerSecuritySchemeTransformer.cs
+++ b/src/OpenApi/sample/Transformers/AddBearerSecuritySchemeTransformer.cs
@@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
namespace Sample.Transformers;
@@ -14,7 +15,7 @@ public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransf
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
if (authenticationSchemes.Any(authScheme => authScheme.Name == "Bearer"))
{
- var requirements = new Dictionary
+ var requirements = new Dictionary
{
["Bearer"] = new OpenApiSecurityScheme
{
diff --git a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs
index 0c1f8a8643b4..8ebc675598af 100644
--- a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs
+++ b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs
@@ -120,8 +120,15 @@ internal static void ApplyValidationAttributes(this JsonNode schema, IEnumerable
}
else if (attribute is MinLengthAttribute minLengthAttribute)
{
- var targetKey = schema[OpenApiSchemaKeywords.TypeKeyword]?.GetValue() == "array" ? OpenApiSchemaKeywords.MinItemsKeyword : OpenApiSchemaKeywords.MinLengthKeyword;
- schema[targetKey] = minLengthAttribute.Length;
+ if (MapJsonNodeToSchemaType(schema[OpenApiSchemaKeywords.TypeKeyword]) is { } schemaTypes &&
+ schemaTypes.HasFlag(JsonSchemaType.Array))
+ {
+ schema[OpenApiSchemaKeywords.MinItemsKeyword] = minLengthAttribute.Length;
+ }
+ else
+ {
+ schema[OpenApiSchemaKeywords.MinLengthKeyword] = minLengthAttribute.Length;
+ }
}
else if (attribute is LengthAttribute lengthAttribute)
{
@@ -191,14 +198,13 @@ internal static void ApplyPrimitiveTypesAndFormats(this JsonNode schema, JsonSch
var underlyingType = Nullable.GetUnderlyingType(type);
if (_simpleTypeToOpenApiSchema.TryGetValue(underlyingType ?? type, out var openApiSchema))
{
- schema[OpenApiSchemaKeywords.NullableKeyword] = openApiSchema.Nullable || (schema[OpenApiSchemaKeywords.TypeKeyword] is JsonArray schemaType && schemaType.GetValues().Contains("null"));
- schema[OpenApiSchemaKeywords.TypeKeyword] = openApiSchema.Type.ToString();
+ if (underlyingType != null && MapJsonNodeToSchemaType(schema[OpenApiSchemaKeywords.TypeKeyword]) is { } schemaTypes &&
+ !schemaTypes.HasFlag(JsonSchemaType.Null))
+ {
+ schema[OpenApiSchemaKeywords.TypeKeyword] = (schemaTypes | JsonSchemaType.Null).ToString();
+ }
schema[OpenApiSchemaKeywords.FormatKeyword] = openApiSchema.Format;
schema[OpenApiConstants.SchemaId] = createSchemaReferenceId(context.TypeInfo);
- schema[OpenApiSchemaKeywords.NullableKeyword] = underlyingType != null;
- // Clear out patterns that the underlying JSON schema generator uses to represent
- // validations for DateTime, DateTimeOffset, and integers.
- schema[OpenApiSchemaKeywords.PatternKeyword] = null;
}
}
@@ -334,14 +340,17 @@ internal static void ApplyParameterInfo(this JsonNode schema, ApiParameterDescri
schema.ApplyRouteConstraints(constraints);
}
- if (parameterDescription.Source is { } bindingSource && SupportsNullableProperty(bindingSource))
+ if (parameterDescription.Source is { } bindingSource
+ && SupportsNullableProperty(bindingSource)
+ && MapJsonNodeToSchemaType(schema[OpenApiSchemaKeywords.TypeKeyword]) is { } schemaTypes &&
+ schemaTypes.HasFlag(JsonSchemaType.Null))
{
- schema[OpenApiSchemaKeywords.NullableKeyword] = false;
+ schema[OpenApiSchemaKeywords.TypeKeyword] = (schemaTypes & ~JsonSchemaType.Null).ToString();
}
// Parameters sourced from the header, query, route, and/or form cannot be nullable based on our binding
// rules but can be optional.
- static bool SupportsNullableProperty(BindingSource bindingSource) =>bindingSource == BindingSource.Header
+ static bool SupportsNullableProperty(BindingSource bindingSource) => bindingSource == BindingSource.Header
|| bindingSource == BindingSource.Query
|| bindingSource == BindingSource.Path
|| bindingSource == BindingSource.Form
@@ -435,9 +444,11 @@ internal static void ApplyNullabilityContextInfo(this JsonNode schema, Parameter
var nullabilityInfoContext = new NullabilityInfoContext();
var nullabilityInfo = nullabilityInfoContext.Create(parameterInfo);
- if (nullabilityInfo.WriteState == NullabilityState.Nullable)
+ if (nullabilityInfo.WriteState == NullabilityState.Nullable
+ && MapJsonNodeToSchemaType(schema[OpenApiSchemaKeywords.TypeKeyword]) is { } schemaTypes
+ && !schemaTypes.HasFlag(JsonSchemaType.Null))
{
- schema[OpenApiSchemaKeywords.NullableKeyword] = true;
+ schema[OpenApiSchemaKeywords.TypeKeyword] = (schemaTypes | JsonSchemaType.Null).ToString();
}
}
@@ -452,7 +463,54 @@ internal static void ApplyNullabilityContextInfo(this JsonNode schema, JsonPrope
// all schema (no type, no format, no constraints).
if (propertyInfo.PropertyType != typeof(object) && (propertyInfo.IsGetNullable || propertyInfo.IsSetNullable))
{
- schema[OpenApiSchemaKeywords.NullableKeyword] = true;
+ if (MapJsonNodeToSchemaType(schema[OpenApiSchemaKeywords.TypeKeyword]) is { } schemaTypes &&
+ !schemaTypes.HasFlag(JsonSchemaType.Null))
+ {
+ schema[OpenApiSchemaKeywords.TypeKeyword] = (schemaTypes | JsonSchemaType.Null).ToString();
+ }
+ }
+ }
+
+ private static JsonSchemaType? MapJsonNodeToSchemaType(JsonNode? jsonNode)
+ {
+ if (jsonNode is not JsonArray jsonArray)
+ {
+ if (Enum.TryParse(jsonNode?.GetValue(), true, out var openApiSchemaType))
+ {
+ return openApiSchemaType;
+ }
+
+ return jsonNode is JsonValue jsonValue && jsonValue.TryGetValue(out var identifier)
+ ? ToSchemaType(identifier)
+ : null;
+ }
+
+ JsonSchemaType? schemaType = null;
+
+ foreach (var node in jsonArray)
+ {
+ if (node is JsonValue jsonValue && jsonValue.TryGetValue(out var identifier))
+ {
+ var type = ToSchemaType(identifier);
+ schemaType = schemaType.HasValue ? (schemaType | type) : type;
+ }
+ }
+
+ return schemaType;
+
+ static JsonSchemaType ToSchemaType(string identifier)
+ {
+ return identifier.ToLowerInvariant() switch
+ {
+ "null" => JsonSchemaType.Null,
+ "boolean" => JsonSchemaType.Boolean,
+ "integer" => JsonSchemaType.Integer,
+ "number" => JsonSchemaType.Number,
+ "string" => JsonSchemaType.String,
+ "array" => JsonSchemaType.Array,
+ "object" => JsonSchemaType.Object,
+ _ => throw new InvalidOperationException($"Unknown schema type: {identifier}"),
+ };
}
}
}
diff --git a/src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs b/src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs
index b16171bc27cb..cb3b6b26abfc 100644
--- a/src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs
+++ b/src/OpenApi/src/Extensions/OpenApiDocumentExtensions.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Models.References;
namespace Microsoft.AspNetCore.OpenApi;
@@ -9,17 +10,17 @@ namespace Microsoft.AspNetCore.OpenApi;
internal static class OpenApiDocumentExtensions
{
///
- /// Registers a into the top-level components store on the
+ /// Registers a into the top-level components store on the
/// and returns a resolvable reference to it.
///
/// The to register the schema onto.
/// The ID that serves as the key for the schema in the schema store.
- /// The to register into the document.
- /// An with a reference to the stored schema.
- public static OpenApiSchema AddOpenApiSchemaByReference(this OpenApiDocument document, string schemaId, OpenApiSchema schema)
+ /// The to register into the document.
+ /// An with a reference to the stored schema.
+ public static IOpenApiSchema AddOpenApiSchemaByReference(this OpenApiDocument document, string schemaId, IOpenApiSchema schema)
{
document.Components ??= new();
- document.Components.Schemas ??= new Dictionary();
+ document.Components.Schemas ??= new Dictionary();
document.Components.Schemas[schemaId] = schema;
document.Workspace ??= new();
var location = document.BaseUri + "/components/schemas/" + schemaId;
diff --git a/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs b/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs
index 29e17c2f1363..cea73303ef25 100644
--- a/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs
+++ b/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs
@@ -8,6 +8,7 @@
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
using OpenApiConstants = Microsoft.AspNetCore.OpenApi.OpenApiConstants;
internal sealed partial class OpenApiJsonSchema
@@ -220,10 +221,6 @@ public static void ReadProperty(ref Utf8JsonReader reader, string propertyName,
var valueConverter = (JsonConverter)options.GetTypeInfo(typeof(OpenApiJsonSchema)).Converter;
schema.Items = valueConverter.Read(ref reader, typeof(OpenApiJsonSchema), options)?.Schema;
break;
- case OpenApiSchemaKeywords.NullableKeyword:
- reader.Read();
- schema.Nullable = reader.GetBoolean();
- break;
case OpenApiSchemaKeywords.DescriptionKeyword:
reader.Read();
schema.Description = reader.GetString();
@@ -274,7 +271,7 @@ public static void ReadProperty(ref Utf8JsonReader reader, string propertyName,
case OpenApiSchemaKeywords.PropertiesKeyword:
reader.Read();
var props = ReadDictionary(ref reader);
- schema.Properties = props?.ToDictionary(p => p.Key, p => p.Value.Schema);
+ schema.Properties = props?.ToDictionary(p => p.Key, p => p.Value.Schema as IOpenApiSchema);
break;
case OpenApiSchemaKeywords.AdditionalPropertiesKeyword:
reader.Read();
@@ -290,7 +287,7 @@ public static void ReadProperty(ref Utf8JsonReader reader, string propertyName,
reader.Read();
schema.Type = JsonSchemaType.Object;
var schemas = ReadList(ref reader);
- schema.AnyOf = schemas?.Select(s => s.Schema).ToList();
+ schema.AnyOf = schemas?.Select(s => s.Schema as IOpenApiSchema).ToList();
break;
case OpenApiSchemaKeywords.DiscriminatorKeyword:
reader.Read();
@@ -322,7 +319,8 @@ public static void ReadProperty(ref Utf8JsonReader reader, string propertyName,
break;
case OpenApiSchemaKeywords.RefKeyword:
reader.Read();
- schema.Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = reader.GetString() };
+ schema.Annotations ??= new Dictionary();
+ schema.Annotations[OpenApiConstants.RefId] = reader.GetString();
break;
default:
reader.Skip();
diff --git a/src/OpenApi/src/Schemas/OpenApiSchemaKeywords.cs b/src/OpenApi/src/Schemas/OpenApiSchemaKeywords.cs
index 37e4121462b9..255cfae73c1c 100644
--- a/src/OpenApi/src/Schemas/OpenApiSchemaKeywords.cs
+++ b/src/OpenApi/src/Schemas/OpenApiSchemaKeywords.cs
@@ -12,7 +12,6 @@ internal class OpenApiSchemaKeywords
public const string AnyOfKeyword = "anyOf";
public const string EnumKeyword = "enum";
public const string DefaultKeyword = "default";
- public const string NullableKeyword = "nullable";
public const string DescriptionKeyword = "description";
public const string DiscriminatorKeyword = "discriminatorName";
public const string DiscriminatorMappingKeyword = "discriminatorMapping";
diff --git a/src/OpenApi/src/Services/OpenApiConstants.cs b/src/OpenApi/src/Services/OpenApiConstants.cs
index e6d99096dc45..8e5d29824514 100644
--- a/src/OpenApi/src/Services/OpenApiConstants.cs
+++ b/src/OpenApi/src/Services/OpenApiConstants.cs
@@ -12,6 +12,7 @@ internal static class OpenApiConstants
internal const string DefaultOpenApiRoute = "/openapi/{documentName}.json";
internal const string DescriptionId = "x-aspnetcore-id";
internal const string SchemaId = "x-schema-id";
+ internal const string RefId = "x-ref-id";
internal const string DefaultOpenApiResponseKey = "default";
// Since there's a finite set of operation types that can be included in a given
// OpenApiPaths, we can pre-allocate an array of these types and use a direct
diff --git a/src/OpenApi/src/Services/OpenApiDocumentService.cs b/src/OpenApi/src/Services/OpenApiDocumentService.cs
index 08cc50a5f953..45fa4fa3f9f0 100644
--- a/src/OpenApi/src/Services/OpenApiDocumentService.cs
+++ b/src/OpenApi/src/Services/OpenApiDocumentService.cs
@@ -26,6 +26,7 @@
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Models.References;
namespace Microsoft.AspNetCore.OpenApi;
@@ -89,7 +90,7 @@ public async Task GetOpenApiDocumentAsync(IServiceProvider scop
document.Workspace.RegisterComponents(document);
if (document.Components?.Schemas is not null)
{
- document.Components.Schemas = new SortedDictionary(document.Components.Schemas);
+ document.Components.Schemas = new SortedDictionary(document.Components.Schemas);
}
return document;
}
@@ -399,14 +400,14 @@ private async Task GetResponseAsync(
return response;
}
- private async Task?> GetParametersAsync(
+ private async Task?> GetParametersAsync(
OpenApiDocument document,
ApiDescription description,
IServiceProvider scopedServiceProvider,
IOpenApiSchemaTransformer[] schemaTransformers,
CancellationToken cancellationToken)
{
- List? parameters = null;
+ List? parameters = null;
foreach (var parameter in description.ParameterDescriptions)
{
if (ShouldIgnoreParameter(parameter))
@@ -513,7 +514,7 @@ private async Task GetFormRequestBody(
Content = new Dictionary()
};
- var schema = new OpenApiSchema { Type = JsonSchemaType.Object, Properties = new Dictionary() };
+ IOpenApiSchema schema = new OpenApiSchema { Type = JsonSchemaType.Object, Properties = new Dictionary() };
// Group form parameters by their name because MVC explodes form parameters that are bound from the
// same model instance into separate ApiParameterDescriptions in ApiExplorer, while minimal APIs does not.
//
@@ -547,7 +548,7 @@ private async Task GetFormRequestBody(
schema.AllOf.Add(new OpenApiSchema
{
Type = JsonSchemaType.Object,
- Properties = new Dictionary
+ Properties = new Dictionary
{
[description.Name] = parameterSchema
}
@@ -583,7 +584,7 @@ private async Task GetFormRequestBody(
schema.AllOf.Add(new OpenApiSchema
{
Type = JsonSchemaType.Object,
- Properties = new Dictionary
+ Properties = new Dictionary
{
[description.Name] = parameterSchema
}
@@ -611,7 +612,7 @@ private async Task GetFormRequestBody(
{
if (hasMultipleFormParameters)
{
- var propertySchema = new OpenApiSchema { Type = JsonSchemaType.Object, Properties = new Dictionary() };
+ var propertySchema = new OpenApiSchema { Type = JsonSchemaType.Object, Properties = new Dictionary() };
foreach (var description in parameter)
{
propertySchema.Properties[description.Name] = await _componentService.GetOrCreateSchemaAsync(document, description.Type, scopedServiceProvider, schemaTransformers, description, cancellationToken: cancellationToken);
@@ -706,7 +707,7 @@ private static Type GetTargetType(ApiDescription description, ApiParameterDescri
var requiresModelMetadataFallbackForEnum = parameterType == typeof(string)
&& parameter.ModelMetadata.ModelType != parameter.Type
&& parameter.ModelMetadata.ModelType.IsEnum;
- // Enums are exempt because we want to set the OpenApiSchema.Enum field when feasible.
+ // Enums are exempt because we want to set the IOpenApiSchema.Enum field when feasible.
// parameter.Type = typeof(TEnum), typeof(TypeWithTryParse)
// parameter.ModelMetadata.Type = typeof(string)
var hasTryParse = bindingMetadata?.HasTryParse == true && parameterType is not null && !parameterType.IsEnum;
diff --git a/src/OpenApi/src/Services/OpenApiGenerator.cs b/src/OpenApi/src/Services/OpenApiGenerator.cs
index ce76cfb88694..a96ab12c5b85 100644
--- a/src/OpenApi/src/Services/OpenApiGenerator.cs
+++ b/src/OpenApi/src/Services/OpenApiGenerator.cs
@@ -20,6 +20,7 @@
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Primitives;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
using Microsoft.OpenApi.Models.References;
namespace Microsoft.AspNetCore.OpenApi;
@@ -363,10 +364,10 @@ private List GetOperationTags(MethodInfo methodInfo, Endpoi
return [new(controllerName, document)];
}
- private List GetOpenApiParameters(MethodInfo methodInfo, RoutePattern pattern, bool disableInferredBody)
+ private List GetOpenApiParameters(MethodInfo methodInfo, RoutePattern pattern, bool disableInferredBody)
{
var parameters = PropertyAsParameterInfo.Flatten(methodInfo.GetParameters(), ParameterBindingMethodCache.Instance);
- var openApiParameters = new List();
+ var openApiParameters = new List();
foreach (var parameter in parameters)
{
diff --git a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs
index 986c9252a636..0714e77fee35 100644
--- a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs
+++ b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs
@@ -17,6 +17,8 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
+using Microsoft.OpenApi.Models.References;
namespace Microsoft.AspNetCore.OpenApi;
@@ -94,14 +96,6 @@ internal sealed class OpenApiSchemaService(
schema.MapPolymorphismOptionsToDiscriminator(context, createSchemaReferenceId);
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.
- // For example, if the property is a `public NestedTyped Nested { get; set; }` property,
- // "nested": "#/properties/nested" becomes "nested": "#/components/schemas/NestedType"
- if (jsonPropertyInfo.PropertyType == jsonPropertyInfo.DeclaringType)
- {
- return new JsonObject { [OpenApiSchemaKeywords.RefKeyword] = createSchemaReferenceId(context.TypeInfo) };
- }
schema.ApplyNullabilityContextInfo(jsonPropertyInfo);
}
if (context.PropertyInfo is { AttributeProvider: { } attributeProvider })
@@ -124,7 +118,7 @@ internal sealed class OpenApiSchemaService(
}
};
- internal async Task GetOrCreateSchemaAsync(OpenApiDocument document, Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, CancellationToken cancellationToken = default)
+ internal async Task GetOrCreateSchemaAsync(OpenApiDocument document, Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, CancellationToken cancellationToken = default)
{
var key = parameterDescription?.ParameterDescriptor is IParameterInfoParameterDescriptor parameterInfoDescription
&& parameterDescription.ModelMetadata.PropertyName is null
@@ -143,8 +137,14 @@ internal async Task GetOrCreateSchemaAsync(OpenApiDocument docume
return ResolveReferenceForSchema(document, schema);
}
- internal static OpenApiSchema ResolveReferenceForSchema(OpenApiDocument document, OpenApiSchema schema, string? baseSchemaId = null)
+ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument document, IOpenApiSchema inputSchema, string? baseSchemaId = null)
{
+ var schema = inputSchema is OpenApiSchemaReference schemaReference
+ ? schemaReference.Target
+ : inputSchema is OpenApiSchema directSchema
+ ? directSchema
+ : throw new InvalidOperationException("The input schema must be an OpenApiSchema or OpenApiSchemaReference.");
+
if (schema.Annotations is not null &&
schema.Annotations.TryGetValue(OpenApiConstants.SchemaId, out var resolvedBaseSchemaId))
{
@@ -200,8 +200,8 @@ internal static OpenApiSchema ResolveReferenceForSchema(OpenApiDocument document
// the `#` ID is generated by the exporter since it has no base document to baseline against. In this
// case we we want to replace the reference ID with the schema ID that was generated by the
// `CreateSchemaReferenceId` method in the OpenApiSchemaService.
- if (schema.Reference is { Type: ReferenceType.Schema, Id: "#" } &&
- schema.Annotations is not null &&
+ if (schema.Annotations is not null &&
+ schema.Annotations.ContainsKey(OpenApiConstants.RefId) &&
schema.Annotations.TryGetValue(OpenApiConstants.SchemaId, out var schemaId) &&
schemaId is string schemaIdString)
{
@@ -212,8 +212,8 @@ schema.Annotations is not null &&
// we don't want to replace the top-level inline schema with a reference to itself. We want to replace
// inline schemas to reference schemas for all schemas referenced in the top-level schema though (such as
// `allOf`, `oneOf`, `anyOf`, `items`, `properties`, etc.) which is why `isTopLevel` is only set once.
- if (schema.Reference is null &&
- schema.Annotations is not null &&
+ if (schema.Annotations is not null &&
+ !schema.Annotations.ContainsKey(OpenApiConstants.RefId) &&
schema.Annotations.TryGetValue(OpenApiConstants.SchemaId, out var referenceId) &&
referenceId is string referenceIdString)
{
@@ -222,14 +222,14 @@ schema.Annotations is not null &&
: referenceIdString;
if (targetReferenceId is not null)
{
- schema = document.AddOpenApiSchemaByReference(targetReferenceId, schema);
+ return document.AddOpenApiSchemaByReference(targetReferenceId, schema);
}
}
return schema;
}
- internal async Task ApplySchemaTransformersAsync(OpenApiSchema schema, Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, CancellationToken cancellationToken = default)
+ internal async Task ApplySchemaTransformersAsync(IOpenApiSchema schema, Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, CancellationToken cancellationToken = default)
{
if (schemaTransformers.Length == 0)
{
@@ -252,7 +252,7 @@ internal async Task ApplySchemaTransformersAsync(OpenApiSchema schema, Type type
}
}
- private async Task InnerApplySchemaTransformersAsync(OpenApiSchema schema,
+ private async Task InnerApplySchemaTransformersAsync(IOpenApiSchema inputSchema,
JsonTypeInfo jsonTypeInfo,
JsonPropertyInfo? jsonPropertyInfo,
OpenApiSchemaTransformerContext context,
@@ -260,6 +260,11 @@ private async Task InnerApplySchemaTransformersAsync(OpenApiSchema schema,
CancellationToken cancellationToken = default)
{
context.UpdateJsonTypeInfo(jsonTypeInfo, jsonPropertyInfo);
+ var schema = inputSchema is OpenApiSchemaReference schemaReference
+ ? schemaReference.Target
+ : inputSchema is OpenApiSchema directSchema
+ ? directSchema
+ : throw new InvalidOperationException("The input schema must be an OpenApiSchema or OpenApiSchemaReference.");
await transformer.TransformAsync(schema, context, cancellationToken);
// Only apply transformers on polymorphic schemas where we can resolve the derived
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
index 3e1be752293a..3b7a14c9aae6 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AddOpenApiTests.CanInterceptAddOpenApi#OpenApiXmlCommentSupport.generated.verified.cs
@@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
+ using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Any;
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
@@ -237,12 +238,15 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
if (operationParameter is not null)
{
- operationParameter.Description = parameterComment.Description;
+ var targetOperationParameter = operationParameter is OpenApiParameterReference reference
+ ? reference.Target
+ : (OpenApiParameter)operationParameter;
+ targetOperationParameter.Description = parameterComment.Description;
if (parameterComment.Example is { } jsonString)
{
- operationParameter.Example = jsonString.Parse();
+ targetOperationParameter.Example = jsonString.Parse();
}
- operationParameter.Deprecated = parameterComment.Deprecated;
+ targetOperationParameter.Deprecated = parameterComment.Deprecated;
}
else
{
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
index 14d8bbcacb55..aa9137dd68b8 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/CompletenessTests.SupportsAllXmlTagsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
@@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
+ using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Any;
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
@@ -334,12 +335,15 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
if (operationParameter is not null)
{
- operationParameter.Description = parameterComment.Description;
+ var targetOperationParameter = operationParameter is OpenApiParameterReference reference
+ ? reference.Target
+ : (OpenApiParameter)operationParameter;
+ targetOperationParameter.Description = parameterComment.Description;
if (parameterComment.Example is { } jsonString)
{
- operationParameter.Example = jsonString.Parse();
+ targetOperationParameter.Example = jsonString.Parse();
}
- operationParameter.Deprecated = parameterComment.Deprecated;
+ targetOperationParameter.Deprecated = parameterComment.Deprecated;
}
else
{
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
index 54e370279a60..8e06da9831f3 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromControllers#OpenApiXmlCommentSupport.generated.verified.cs
@@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
+ using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Any;
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
@@ -241,12 +242,15 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
if (operationParameter is not null)
{
- operationParameter.Description = parameterComment.Description;
+ var targetOperationParameter = operationParameter is OpenApiParameterReference reference
+ ? reference.Target
+ : (OpenApiParameter)operationParameter;
+ targetOperationParameter.Description = parameterComment.Description;
if (parameterComment.Example is { } jsonString)
{
- operationParameter.Example = jsonString.Parse();
+ targetOperationParameter.Example = jsonString.Parse();
}
- operationParameter.Deprecated = parameterComment.Deprecated;
+ targetOperationParameter.Deprecated = parameterComment.Deprecated;
}
else
{
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
index 0973c3d23999..397466f8fa7e 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/OperationTests.SupportsXmlCommentsOnOperationsFromMinimalApis#OpenApiXmlCommentSupport.generated.verified.cs
@@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
+ using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Any;
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
@@ -249,12 +250,15 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
if (operationParameter is not null)
{
- operationParameter.Description = parameterComment.Description;
+ var targetOperationParameter = operationParameter is OpenApiParameterReference reference
+ ? reference.Target
+ : (OpenApiParameter)operationParameter;
+ targetOperationParameter.Description = parameterComment.Description;
if (parameterComment.Example is { } jsonString)
{
- operationParameter.Example = jsonString.Parse();
+ targetOperationParameter.Example = jsonString.Parse();
}
- operationParameter.Deprecated = parameterComment.Deprecated;
+ targetOperationParameter.Deprecated = parameterComment.Deprecated;
}
else
{
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
index 8abf7d226a17..ae4701bc970b 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/SchemaTests.SupportsXmlCommentsOnSchemas#OpenApiXmlCommentSupport.generated.verified.cs
@@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
+ using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Any;
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")]
@@ -265,12 +266,15 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
if (operationParameter is not null)
{
- operationParameter.Description = parameterComment.Description;
+ var targetOperationParameter = operationParameter is OpenApiParameterReference reference
+ ? reference.Target
+ : (OpenApiParameter)operationParameter;
+ targetOperationParameter.Description = parameterComment.Description;
if (parameterComment.Example is { } jsonString)
{
- operationParameter.Example = jsonString.Parse();
+ targetOperationParameter.Example = jsonString.Parse();
}
- operationParameter.Deprecated = parameterComment.Deprecated;
+ targetOperationParameter.Deprecated = parameterComment.Deprecated;
}
else
{
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs
index 9e401fdbe248..f660f6557121 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiRouteHandlerBuilderExtensionTests.cs
@@ -74,7 +74,7 @@ public void WithOpenApi_CanSetSchemaInOperationWithOverride()
_ = builder.MapDelete("/{id}", GetString)
.WithOpenApi(operation => new(operation)
{
- Parameters = new List() { new() { Schema = new() { Type = JsonSchemaType.Number } } }
+ Parameters = [new OpenApiParameter() { Schema = new OpenApiSchema() { Type = JsonSchemaType.Number } }]
});
var dataSource = GetBuilderEndpointDataSource(builder);
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs
index 27c0d8bd452d..86239cbc84ee 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs
@@ -5,8 +5,8 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.AspNetCore.OpenApi;
-using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.References;
public class CreateSchemaReferenceIdTests : OpenApiDocumentServiceTestBase
{
@@ -78,7 +78,7 @@ await VerifyOpenApiDocument(builder, options, document =>
var content = Assert.Single(requestBody.Content);
Assert.Equal("application/json", content.Key);
Assert.NotNull(content.Value.Schema);
- Assert.Equal("TodoSchema", content.Value.Schema.Reference.Id);
+ Assert.Equal("TodoSchema", ((OpenApiSchemaReference)content.Value.Schema).Reference.Id);
var schema = content.Value.Schema;
Assert.Equal(JsonSchemaType.Object, schema.Type);
Assert.Collection(schema.Properties,
@@ -90,7 +90,7 @@ await VerifyOpenApiDocument(builder, options, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -129,7 +129,7 @@ await VerifyOpenApiDocument(builder, options, document =>
Assert.NotNull(content.Value.Schema);
// Assert that no reference was created and the schema is inlined
var schema = content.Value.Schema;
- Assert.Null(schema.Reference);
+ Assert.IsType(schema);
Assert.Equal(JsonSchemaType.Object, schema.Type);
Assert.Collection(schema.Properties,
property =>
@@ -140,7 +140,7 @@ await VerifyOpenApiDocument(builder, options, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -187,7 +187,7 @@ await VerifyOpenApiDocument(builder, options, document =>
Assert.Equal("application/json", content.Key);
Assert.NotNull(content.Value.Schema);
var schema = content.Value.Schema;
- Assert.Null(schema.Reference);
+ Assert.IsNotType(schema);
// Assert that a reference was created for the TodoWithDueDate type
Assert.NotNull(response);
@@ -195,8 +195,8 @@ await VerifyOpenApiDocument(builder, options, document =>
Assert.Equal("application/json", responseContent.Key);
Assert.NotNull(responseContent.Value.Schema);
var responseSchema = responseContent.Value.Schema;
- Assert.NotNull(responseSchema.Reference);
- Assert.Equal("TodoWithDueDate", responseSchema.Reference.Id);
+ Assert.IsType(responseSchema);
+ Assert.Equal("TodoWithDueDate", ((OpenApiSchemaReference)responseSchema).Reference.Id);
});
}
@@ -230,7 +230,7 @@ await VerifyOpenApiDocument(builder, options, document =>
Assert.Equal("application/json", content.Key);
Assert.NotNull(content.Value.Schema);
var schema = content.Value.Schema;
- Assert.NotNull(schema.Reference);
+ Assert.IsType(schema);
// Assert that a reference was created for the TodoWithDueDate type
Assert.NotNull(response);
@@ -238,10 +238,10 @@ await VerifyOpenApiDocument(builder, options, document =>
Assert.Equal("application/json", responseContent.Key);
Assert.NotNull(responseContent.Value.Schema);
var responseSchema = responseContent.Value.Schema;
- Assert.NotNull(responseSchema.Reference);
+ Assert.IsType(responseSchema);
// Assert that the reference IDs are not the same (have been deduped)
- Assert.NotEqual(schema.Reference.Id, responseSchema.Reference.Id);
+ Assert.NotEqual(((OpenApiSchemaReference)schema).Reference.Id, ((OpenApiSchemaReference)responseSchema).Reference.Id);
// Assert that the referenced schemas are correct
var effectiveResponseSchema = responseSchema;
@@ -250,7 +250,7 @@ await VerifyOpenApiDocument(builder, options, document =>
property =>
{
Assert.Equal("dueDate", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("date-time", property.Value.Format);
},
property =>
@@ -261,7 +261,7 @@ await VerifyOpenApiDocument(builder, options, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -271,7 +271,7 @@ await VerifyOpenApiDocument(builder, options, document =>
property =>
{
Assert.Equal("createdAt", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("date-time", property.Value.Format);
});
@@ -286,7 +286,7 @@ await VerifyOpenApiDocument(builder, options, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -296,7 +296,7 @@ await VerifyOpenApiDocument(builder, options, document =>
property =>
{
Assert.Equal("createdAt", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("date-time", property.Value.Format);
});
});
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs
index 88ec906628b7..bbca1d365a02 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Operations.cs
@@ -182,62 +182,6 @@ await VerifyOpenApiDocument(builder, document =>
});
}
- [Fact]
- public async Task GetOpenApiOperation_EditingReferenceInOperationThrowsException()
- {
- // Arrange
- var builder = CreateBuilder();
-
- // Act
- builder.MapGet("/api/todos", () => { }).WithTags(["todos"]);
- var options = new OpenApiOptions();
- options.AddOperationTransformer((operation, context, cancellationToken) =>
- {
- foreach (var tag in operation.Tags)
- {
- tag.Name = "newTag"; // Should throw exception
- }
- return Task.CompletedTask;
- });
-
- // Assert
- var exception = await Assert.ThrowsAsync(() => VerifyOpenApiDocument(builder, options, _ => { }));
- Assert.Equal("Setting the value from the reference is not supported, use the target property instead.", exception.Message);
- }
-
- [Fact]
- public async Task GetOpenApiOperation_EditingTargetTagInOperationWorks()
- {
- // Arrange
- var builder = CreateBuilder();
-
- // Act
- builder.MapGet("/api/todos", () => { }).WithTags(["todos"]);
- var options = new OpenApiOptions();
- options.AddOperationTransformer((operation, context, cancellationToken) =>
- {
- foreach (var tag in operation.Tags)
- {
- tag.Target.Name = "newTag";
- }
- return Task.CompletedTask;
- });
-
- // Assert
- await VerifyOpenApiDocument(builder, options, document =>
- {
- var operation = document.Paths["/api/todos"].Operations[OperationType.Get];
- Assert.Collection(operation.Tags, tag =>
- {
- Assert.Equal("newTag", tag.Name);
- });
- Assert.Collection(document.Tags, tag =>
- {
- Assert.Equal("newTag", tag.Name);
- });
- });
- }
-
[Fact]
public async Task GetOpenApiOperation_CapturesEndpointNameAsOperationId()
{
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs
index 01a6445bd632..032720da5cc0 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.RequestBody.cs
@@ -434,7 +434,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -591,7 +591,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -616,7 +616,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("message", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
});
});
}
@@ -756,7 +756,7 @@ await VerifyOpenApiDocument(action, document =>
Assert.All(item.Schema.Properties,
property =>
{
- Assert.False(property.Value.Nullable);
+ Assert.False(property.Value.Type?.HasFlag(JsonSchemaType.Null));
});
}
});
@@ -836,17 +836,17 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("name", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("description", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("resume", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("binary", property.Value.Format);
});
}
@@ -974,7 +974,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -1038,7 +1038,7 @@ await VerifyOpenApiDocument(action, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -1048,7 +1048,7 @@ await VerifyOpenApiDocument(action, document =>
property =>
{
Assert.Equal("createdAt", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("date-time", property.Value.Format);
});
},
@@ -1057,7 +1057,7 @@ await VerifyOpenApiDocument(action, document =>
Assert.Collection(allOfItem.Properties, property =>
{
Assert.Equal("formFile", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("binary", property.Value.Format);
});
},
@@ -1066,7 +1066,7 @@ await VerifyOpenApiDocument(action, document =>
Assert.Collection(allOfItem.Properties, property =>
{
Assert.Equal("guid", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("uuid", property.Value.Format);
});
});
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs
index 6b208b51486d..61ef643260b6 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs
@@ -240,7 +240,7 @@ await VerifyOpenApiDocument(builder, document =>
}, property =>
{
Assert.Equal("message", property.Key);
- Assert.Equal( JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
});
});
}
@@ -274,7 +274,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("message", property.Key);
- Assert.Equal( JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
});
// Generates the 200 status code response with the `Todo` response type.
var okResponse = operation.Responses["200"];
@@ -291,7 +291,7 @@ await VerifyOpenApiDocument(builder, document =>
}, property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
}, property =>
{
Assert.Equal("completed", property.Key);
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentServiceTestsBase.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentServiceTestsBase.cs
index 15076da179ad..00aeaef2e6d5 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentServiceTestsBase.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentServiceTestsBase.cs
@@ -3,10 +3,12 @@
using System.Reflection;
using System.Text;
+using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
@@ -84,7 +86,11 @@ internal static OpenApiDocumentService CreateDocumentService(IEndpointRouteBuild
var openApiOptions = new Mock>();
openApiOptions.Setup(o => o.Get(It.IsAny())).Returns(new OpenApiOptions());
- var schemaService = new OpenApiSchemaService("Test", Options.Create(new Microsoft.AspNetCore.Http.Json.JsonOptions()), openApiOptions.Object);
+ var jsonOptions = new Microsoft.AspNetCore.Http.Json.JsonOptions();
+ // Set strict number handling by default to make integer type checks more straightforward
+ jsonOptions.SerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.Strict;
+
+ var schemaService = new OpenApiSchemaService("Test", Options.Create(jsonOptions), openApiOptions.Object);
((TestServiceProvider)builder.ServiceProvider).TestSchemaService = schemaService;
var documentService = new OpenApiDocumentService("Test", apiDescriptionGroupCollectionProvider, hostEnvironment, openApiOptions.Object, builder.ServiceProvider, new OpenApiTestServer());
((TestServiceProvider)builder.ServiceProvider).TestDocumentService = documentService;
@@ -125,7 +131,10 @@ internal static OpenApiDocumentService CreateDocumentService(IEndpointRouteBuild
provider.OnProvidersExecuted(context);
var apiDescriptionGroupCollectionProvider = CreateApiDescriptionGroupCollectionProvider(context.Results);
- var jsonOptions = builder.ServiceProvider.GetService>() ?? Options.Create(new Microsoft.AspNetCore.Http.Json.JsonOptions());
+ var defaultJsonOptions = new Microsoft.AspNetCore.Http.Json.JsonOptions();
+ // Set strict number handling by default to make integer type checks more straightforward
+ defaultJsonOptions.SerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.Strict;
+ var jsonOptions = builder.ServiceProvider.GetService>() ?? Options.Create(defaultJsonOptions);
var schemaService = new OpenApiSchemaService("Test", jsonOptions, options.Object);
((TestServiceProvider)builder.ServiceProvider).TestSchemaService = schemaService;
@@ -158,8 +167,13 @@ private static EndpointMetadataApiDescriptionProvider CreateEndpointMetadataApiD
internal static TestEndpointRouteBuilder CreateBuilder(IServiceCollection serviceCollection = null)
{
+ serviceCollection ??= new ServiceCollection();
+ serviceCollection.ConfigureHttpJsonOptions(options =>
+ {
+ options.SerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.Strict;
+ });
var serviceProvider = new TestServiceProvider();
- serviceProvider.SetInternalServiceProvider(serviceCollection ?? new ServiceCollection());
+ serviceProvider.SetInternalServiceProvider(serviceCollection);
return new TestEndpointRouteBuilder(new ApplicationBuilder(serviceProvider));
}
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs
index 2f8fddb13eb6..829f906f4b41 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs
@@ -3,7 +3,6 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
-using System.Globalization;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
@@ -14,6 +13,7 @@
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.References;
public partial class OpenApiSchemaServiceTests : OpenApiDocumentServiceTestBase
{
@@ -74,7 +74,6 @@ await VerifyOpenApiDocument(builder, document =>
var parameter = Assert.Single(operation.Parameters);
Assert.Equal(schemaType, parameter.Schema.Type);
Assert.Equal(schemaFormat, parameter.Schema.Format);
- Assert.False(parameter.Schema.Nullable);
});
}
@@ -361,7 +360,8 @@ await VerifyOpenApiDocument(builder, document =>
{
var operation = document.Paths["/api/{id}"].Operations[OperationType.Get];
var parameter = Assert.Single(operation.Parameters);
- verifySchema(parameter.Schema);
+ var schema = Assert.IsType(parameter.Schema);
+ verifySchema(schema);
});
}
@@ -390,7 +390,8 @@ await VerifyOpenApiDocument(builder, document =>
{
var operation = document.Paths["/api/{id}"].Operations[OperationType.Get];
var parameter = Assert.Single(operation.Parameters);
- verifySchema(parameter.Schema);
+ var schema = Assert.IsType(parameter.Schema);
+ verifySchema(schema);
});
}
@@ -418,7 +419,8 @@ await VerifyOpenApiDocument(builder, document =>
{
var operation = document.Paths["/api/{id}"].Operations[OperationType.Get];
var parameter = Assert.Single(operation.Parameters);
- verifySchema(parameter.Schema);
+ var schema = Assert.IsType(parameter.Schema);
+ verifySchema(schema);
});
}
@@ -481,9 +483,9 @@ await VerifyOpenApiDocument(builder, document =>
// When the element type is not nullable (int[] ints), the binding
// will produce [1, 2, 0, 4]
Assert.Equal(JsonSchemaType.Array, parameter.Schema.Type);
- Assert.Equal(JsonSchemaType.Array, parameter.Schema.Type);
- Assert.Equal(innerSchemaType, parameter.Schema.Items.Type);
- Assert.Equal(isNullable, parameter.Schema.Items.Nullable);
+
+ Assert.True(parameter.Schema.Items.Type?.HasFlag(innerSchemaType));
+ Assert.Equal(isNullable, parameter.Schema.Items.Type?.HasFlag(JsonSchemaType.Null));
});
}
@@ -619,8 +621,7 @@ static void AssertOpenApiDocument(OpenApiDocument document)
var operation = document.Paths["/api/with-enum"].Operations[OperationType.Get];
var parameter = Assert.Single(operation.Parameters);
var response = Assert.Single(operation.Responses).Value.Content["application/json"].Schema;
- Assert.NotNull(parameter.Schema.Reference);
- Assert.Equal(parameter.Schema.Reference.Id, response.Reference.Id);
+ Assert.Equal(((OpenApiSchemaReference)parameter.Schema).Reference.Id, ((OpenApiSchemaReference)response).Reference.Id);
var schema = parameter.Schema;
Assert.Collection(schema.Enum,
value =>
@@ -695,7 +696,7 @@ static void AssertOpenApiDocument(OpenApiDocument document)
Assert.Collection(schema.Properties, property =>
{
Assert.Equal("name", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
});
}
}
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs
index 53dbe46ca2c9..8002c603029c 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.PolymorphicSchemas.cs
@@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.References;
public partial class OpenApiSchemaServiceTests : OpenApiDocumentServiceTestBase
{
@@ -150,9 +151,9 @@ await VerifyOpenApiDocument(builder, document =>
// property associated with them.
Assert.Null(schema.Discriminator);
Assert.Collection(schema.AnyOf,
- schema => Assert.Equal("ColorPaintColor", schema.Reference.Id),
- schema => Assert.Equal("ColorFabricColor", schema.Reference.Id),
- schema => Assert.Equal("ColorBase", schema.Reference.Id));
+ schema => Assert.Equal("ColorPaintColor", ((OpenApiSchemaReference)schema).Reference.Id),
+ schema => Assert.Equal("ColorFabricColor", ((OpenApiSchemaReference)schema).Reference.Id),
+ schema => Assert.Equal("ColorBase", ((OpenApiSchemaReference)schema).Reference.Id));
// Assert schema with discriminator = "paint" has been inserted into the components
Assert.True(document.Components.Schemas.TryGetValue("ColorPaintColor", out var paintSchema));
Assert.Contains("$type", paintSchema.Properties.Keys);
@@ -202,9 +203,9 @@ await VerifyOpenApiDocument(builder, document =>
// type. In this scenario, we check that the base class is not included in the `anyOf`
// schema.
Assert.Collection(schema.AnyOf,
- schema => Assert.Equal("PetCat", schema.Reference.Id),
- schema => Assert.Equal("PetDog", schema.Reference.Id),
- schema => Assert.Equal("PetPet", schema.Reference.Id));
+ schema => Assert.Equal("PetCat", ((OpenApiSchemaReference)schema).Reference.Id),
+ schema => Assert.Equal("PetDog", ((OpenApiSchemaReference)schema).Reference.Id),
+ schema => Assert.Equal("PetPet", ((OpenApiSchemaReference)schema).Reference.Id));
// Assert schema with discriminator = "dog" has been inserted into the components
Assert.True(document.Components.Schemas.TryGetValue("PetDog", out var dogSchema));
Assert.Contains(schema.Discriminator.PropertyName, dogSchema.Properties.Keys);
@@ -242,9 +243,9 @@ await VerifyOpenApiDocument(builder, document =>
// property associated with them.
Assert.Null(schema.Discriminator);
Assert.Collection(schema.AnyOf,
- schema => Assert.Equal("OrganismAnimal", schema.Reference.Id),
- schema => Assert.Equal("OrganismPlant", schema.Reference.Id),
- schema => Assert.Equal("OrganismBase", schema.Reference.Id));
+ schema => Assert.Equal("OrganismAnimal", ((OpenApiSchemaReference)schema).Reference.Id),
+ schema => Assert.Equal("OrganismPlant", ((OpenApiSchemaReference)schema).Reference.Id),
+ schema => Assert.Equal("OrganismBase", ((OpenApiSchemaReference)schema).Reference.Id));
// Assert that schemas without discriminators have been inserted into the components
Assert.True(document.Components.Schemas.TryGetValue("OrganismAnimal", out var animalSchema));
Assert.DoesNotContain("$type", animalSchema.Properties.Keys);
@@ -271,7 +272,7 @@ await VerifyOpenApiDocument(builder, document =>
Assert.NotNull(operation.RequestBody);
var requestBody = operation.RequestBody.Content;
Assert.True(requestBody.TryGetValue("application/json", out var mediaType));
- Assert.Equal("Employee", mediaType.Schema.Reference.Id);
+ Assert.Equal("Employee", ((OpenApiSchemaReference)mediaType.Schema).Reference.Id);
var schema = mediaType.Schema;
// Assert that discriminator mappings are configured correctly for type.
Assert.Equal("$type", schema.Discriminator.PropertyName);
@@ -285,15 +286,15 @@ await VerifyOpenApiDocument(builder, document =>
);
// Assert that anyOf schemas use the correct reference IDs.
Assert.Collection(schema.AnyOf,
- schema => Assert.Equal("EmployeeManager", schema.Reference.Id),
- schema => Assert.Equal("EmployeeEmployee", schema.Reference.Id));
+ schema => Assert.Equal("EmployeeManager", ((OpenApiSchemaReference)schema).Reference.Id),
+ schema => Assert.Equal("EmployeeEmployee", ((OpenApiSchemaReference)schema).Reference.Id));
// Assert that schemas without discriminators have been inserted into the components
Assert.True(document.Components.Schemas.TryGetValue("EmployeeManager", out var managerSchema));
Assert.Equal("manager", managerSchema.Properties[schema.Discriminator.PropertyName].Enum.First().GetValue());
Assert.True(document.Components.Schemas.TryGetValue("EmployeeEmployee", out var employeeSchema));
Assert.Equal("employee", employeeSchema.Properties[schema.Discriminator.PropertyName].Enum.First().GetValue());
// Assert that the schema has a correct self-reference to the base-type. This points to the schema that contains the discriminator.
- Assert.Equal("Employee", employeeSchema.Properties["manager"].Reference.Id);
+ Assert.Equal("Employee", ((OpenApiSchemaReference)employeeSchema.Properties["manager"]).Reference.Id);
});
}
}
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs
index 03744160a9e9..bbd87f3afaa4 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.RequestBodySchemas.cs
@@ -11,6 +11,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.References;
public partial class OpenApiSchemaServiceTests : OpenApiDocumentServiceTestBase
{
@@ -44,7 +45,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -103,7 +104,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("name", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal(5, property.Value.MinLength);
Assert.Null(property.Value.Default);
},
@@ -144,7 +145,7 @@ await VerifyOpenApiDocument(builder, document =>
static OpenApiRequestBody GetRequestBodyForPath(OpenApiDocument document, string path)
{
var operation = document.Paths[path].Operations[OperationType.Post];
- return operation.RequestBody;
+ return operation.RequestBody as OpenApiRequestBody;
}
}
@@ -213,17 +214,17 @@ await VerifyOpenApiDocument(builder, document =>
var operation = document.Paths[$"/proposal"].Operations[OperationType.Post];
var requestBody = operation.RequestBody;
var schema = requestBody.Content["application/json"].Schema;
- Assert.Equal("Proposal", schema.Reference.Id);
+ Assert.Equal("Proposal", ((OpenApiSchemaReference)schema).Reference.Id);
var effectiveSchema = schema;
Assert.Collection(effectiveSchema.Properties,
property => {
Assert.Equal("proposalElement", property.Key);
- Assert.Equal("Proposal", property.Value.Reference.Id);
+ Assert.Equal("Proposal", ((OpenApiSchemaReference)property.Value).Reference.Id);
},
property => {
Assert.Equal("stream", property.Key);
var targetSchema = property.Value;
- Assert.Equal(JsonSchemaType.String, targetSchema.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, targetSchema.Type);
Assert.Equal("binary", targetSchema.Format);
});
});
@@ -254,7 +255,7 @@ await VerifyOpenApiDocument(builder, document =>
var enumerableTodoSchema = enumerableTodo.RequestBody.Content["application/json"].Schema;
var arrayTodoSchema = arrayTodo.RequestBody.Content["application/json"].Schema;
// Assert that both IEnumerable and Todo[] have items that map to the same schema
- Assert.Equal(enumerableTodoSchema.Items.Reference.Id, arrayTodoSchema.Items.Reference.Id);
+ Assert.Equal(((OpenApiSchemaReference)enumerableTodoSchema.Items).Reference.Id, ((OpenApiSchemaReference)arrayTodoSchema.Items).Reference.Id);
// Assert all types materialize as arrays
Assert.Equal(JsonSchemaType.Array, enumerableTodoSchema.Type);
Assert.Equal(JsonSchemaType.Array, arrayTodoSchema.Type);
@@ -275,7 +276,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -327,7 +328,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("make", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
});
});
}
@@ -361,7 +362,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("The title of the todo item.", property.Value.Description);
},
property =>
@@ -418,34 +419,29 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("nullableInt", property.Key);
- Assert.Equal( JsonSchemaType.Integer, property.Value.Type);
- Assert.True(property.Value.Nullable);
+ Assert.Equal(JsonSchemaType.Integer | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("nullableString", property.Key);
- Assert.Equal( JsonSchemaType.String, property.Value.Type);
- Assert.True(property.Value.Nullable);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("nullableBool", property.Key);
- Assert.Equal( JsonSchemaType.Boolean, property.Value.Type);
- Assert.True(property.Value.Nullable);
+ Assert.Equal(JsonSchemaType.Boolean | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("nullableDateTime", property.Key);
- Assert.Equal( JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("date-time", property.Value.Format);
- Assert.True(property.Value.Nullable);
},
property =>
{
Assert.Equal("nullableUri", property.Key);
- Assert.Equal( JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("uri", property.Value.Format);
- Assert.True(property.Value.Nullable);
});
});
}
@@ -465,18 +461,18 @@ 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);
+ Assert.Equal("NestedType", ((OpenApiSchemaReference)content.Value.Schema).Reference.Id);
var schema = content.Value.Schema;
Assert.Collection(schema.Properties,
property =>
{
Assert.Equal("name", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("nested", property.Key);
- Assert.Equal("NestedType", property.Value.Reference.Id);
+ Assert.Equal("NestedType", ((OpenApiSchemaReference)property.Value).Reference.Id);
});
});
}
@@ -510,18 +506,18 @@ 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);
+ Assert.Equal("NestedType", ((OpenApiSchemaReference)content.Value.Schema).Reference.Id);
var schema = content.Value.Schema;
Assert.Collection(schema.Properties,
property =>
{
Assert.Equal("name", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("nested", property.Key);
- Assert.Equal("NestedType", property.Value.Reference.Id);
+ Assert.Equal("NestedType", ((OpenApiSchemaReference)property.Value).Reference.Id);
});
});
}
@@ -578,19 +574,16 @@ await VerifyOpenApiDocument(builder, document =>
{
var property = schema.Properties["name"];
Assert.Equal(JsonSchemaType.String, property.Type);
- Assert.False(property.Nullable);
},
schema =>
{
var property = schema.Properties["number"];
Assert.Equal(JsonSchemaType.Integer, property.Type);
- Assert.False(property.Nullable);
},
schema =>
{
var property = schema.Properties["ids"];
Assert.Equal(JsonSchemaType.Array, property.Type);
- Assert.False(property.Nullable);
});
});
}
@@ -674,19 +667,19 @@ await VerifyOpenApiDocument(builder, document =>
var operation = document.Paths["/api"].Operations[OperationType.Post];
var requestBody = operation.RequestBody;
var content = Assert.Single(requestBody.Content);
- var schema = document.Components.Schemas[content.Value.Schema.Reference.Id];
+ var schema = content.Value.Schema;
Assert.Collection(schema.Properties,
property =>
{
Assert.Equal("selfReferenceList", property.Key);
Assert.Equal(JsonSchemaType.Null | JsonSchemaType.Array, property.Value.Type);
- Assert.Equal("Parent", property.Value.Items.Reference.Id);
+ Assert.Equal("Parent", ((OpenApiSchemaReference)property.Value.Items).Reference.Id);
},
property =>
{
Assert.Equal("selfReferenceDictionary", property.Key);
Assert.Equal(JsonSchemaType.Null | JsonSchemaType.Object, property.Value.Type);
- Assert.Equal("Parent", property.Value.AdditionalProperties.Reference.Id);
+ Assert.Equal("Parent", ((OpenApiSchemaReference)property.Value.AdditionalProperties).Reference.Id);
});
});
}
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs
index 7e3ca958936e..071b4f679c5c 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ResponseSchemas.cs
@@ -80,7 +80,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -128,7 +128,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("name", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
Assert.Equal(5, property.Value.MinLength);
},
property =>
@@ -172,7 +172,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -247,7 +247,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -257,8 +257,6 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("createdAt", property.Key);
- // DateTime schema appears twice in the document so we expect
- // this to map to a reference ID.
var dateTimeSchema = property.Value;
Assert.Equal(JsonSchemaType.String, dateTimeSchema.Type);
Assert.Equal("date-time", dateTimeSchema.Format);
@@ -304,7 +302,7 @@ await VerifyOpenApiDocument(builder, document =>
}, property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
}, property =>
{
Assert.Equal("completed", property.Key);
@@ -328,7 +326,7 @@ await VerifyOpenApiDocument(builder, document =>
}, property =>
{
Assert.Equal("message", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
});
});
});
@@ -369,7 +367,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("make", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
});
});
}
@@ -402,7 +400,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("name", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -419,7 +417,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -467,7 +465,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -545,7 +543,7 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -587,28 +585,28 @@ await VerifyOpenApiDocument(builder, document =>
property =>
{
Assert.Equal("type", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("status", property.Key);
- Assert.Equal(JsonSchemaType.Integer, property.Value.Type);
+ Assert.Equal(JsonSchemaType.Integer | JsonSchemaType.Null, property.Value.Type);
Assert.Equal("int32", property.Value.Format);
},
property =>
{
Assert.Equal("detail", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
Assert.Equal("instance", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
@@ -646,7 +644,6 @@ await VerifyOpenApiDocument(builder, document =>
{
Assert.Equal("object", property.Key);
Assert.Null(property.Value.Type);
- Assert.False(property.Value.Nullable);
},
property =>
{
@@ -681,7 +678,7 @@ await VerifyOpenApiDocument(actionDescriptor, document =>
property =>
{
Assert.Equal("title", property.Key);
- Assert.Equal(JsonSchemaType.String, property.Value.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, property.Value.Type);
},
property =>
{
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs
index 6e76e7e290f2..6fb07c0035bb 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/Implementations/OpenApiSchemaReferenceTransformerTests.cs
@@ -9,6 +9,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Writers;
public class OpenApiSchemaReferenceTransformerTests : OpenApiDocumentServiceTestBase
@@ -54,14 +55,17 @@ await VerifyOpenApiDocument(builder, document =>
// }
// }
// }
- Assert.Equal(schema.Reference, schema2.Reference);
+ Assert.Equal(JsonSchemaType.Object, schema.Type);
+ var value = Assert.Single(schema.Properties).Value;
+ Assert.Equal("IFormFile", ((OpenApiSchemaReference)value).Reference.Id);
- var effectiveSchema = schema;
- Assert.Equal(JsonSchemaType.Object, effectiveSchema.Type);
- Assert.Single(effectiveSchema.Properties);
- var effectivePropertySchema = effectiveSchema.Properties["value"];
- Assert.Equal(JsonSchemaType.String, effectivePropertySchema.Type);
- Assert.Equal("binary", effectivePropertySchema.Format);
+ Assert.Equal(JsonSchemaType.Object, schema2.Type);
+ var value2 = Assert.Single(schema2.Properties).Value;
+ Assert.Equal("IFormFile", ((OpenApiSchemaReference)value2).Reference.Id);
+
+ var effectiveSchema = ((OpenApiSchemaReference)value).Target;
+ Assert.Equal(JsonSchemaType.String, effectiveSchema.Type);
+ Assert.Equal("binary", effectiveSchema.Format);
});
}
@@ -102,7 +106,8 @@ await VerifyOpenApiDocument(builder, document =>
// }
// }
// }
- Assert.Equal(requestBodySchema.Reference.Id, responseSchema.Reference.Id);
+ // }
+ Assert.Equal(((OpenApiSchemaReference)requestBodySchema).Reference.Id, ((OpenApiSchemaReference)responseSchema).Reference.Id);
var effectiveSchema = requestBodySchema;
Assert.Equal(JsonSchemaType.Object, effectiveSchema.Type);
@@ -110,7 +115,7 @@ await VerifyOpenApiDocument(builder, document =>
var effectiveIdSchema = effectiveSchema.Properties["id"];
Assert.Equal(JsonSchemaType.Integer, effectiveIdSchema.Type);
var effectiveTitleSchema = effectiveSchema.Properties["title"];
- Assert.Equal(JsonSchemaType.String, effectiveTitleSchema.Type);
+ Assert.Equal(JsonSchemaType.String | JsonSchemaType.Null, effectiveTitleSchema.Type);
var effectiveCompletedSchema = effectiveSchema.Properties["completed"];
Assert.Equal(JsonSchemaType.Boolean, effectiveCompletedSchema.Type);
var effectiveCreatedAtSchema = effectiveSchema.Properties["createdAt"];
@@ -171,7 +176,7 @@ await VerifyOpenApiDocument(builder, document =>
Assert.Equal(JsonSchemaType.Array, requestBodySchema.Type);
Assert.Equal(JsonSchemaType.Object, requestBodySchema2.Type);
// Values of the list and dictionary point to the same reference ID
- Assert.Equal(requestBodySchema.Items.Reference.Id, requestBodySchema2.AdditionalProperties.Reference.Id);
+ Assert.Equal(((OpenApiSchemaReference)requestBodySchema.Items).Reference.Id, ((OpenApiSchemaReference)requestBodySchema2.AdditionalProperties).Reference.Id);
});
}
@@ -197,7 +202,7 @@ await VerifyOpenApiDocument(builder, document =>
var requestBodySchema2 = requestBody2.Schema;
// Todo parameter (second parameter) in allOf for each operation should point to the same reference ID.
- Assert.Equal(requestBodySchema.AllOf[1].Reference.Id, requestBodySchema2.AllOf[1].Reference.Id);
+ Assert.Equal(((OpenApiSchemaReference)requestBodySchema.AllOf[1]).Reference.Id, ((OpenApiSchemaReference)requestBodySchema2.AllOf[1]).Reference.Id);
// IFormFile parameter should use inline schema since it only appears once in the application.
Assert.Equal(JsonSchemaType.Object, requestBodySchema.AllOf[0].Type);
@@ -206,8 +211,8 @@ await VerifyOpenApiDocument(builder, document =>
// string parameter is not resolved to a top-level reference.
Assert.Equal(JsonSchemaType.Object, requestBodySchema2.AllOf[0].Type);
- Assert.Null(requestBodySchema.AllOf[1].Properties["title"].Reference);
- Assert.Null(requestBodySchema2.AllOf[1].Properties["title"].Reference);
+ Assert.IsNotType(requestBodySchema.AllOf[1].Properties["title"]);
+ Assert.IsNotType(requestBodySchema2.AllOf[1].Properties["title"]);
});
}
@@ -256,13 +261,13 @@ await VerifyOpenApiDocument(builder, document =>
// }
// Both list types should be inlined
- Assert.Null(requestBodySchema.Reference);
- Assert.Equal(requestBodySchema.Reference, requestBodySchema2.Reference);
+ Assert.IsNotType(requestBodySchema);
+ Assert.IsNotType(requestBodySchema2);
// And have an `array` type
Assert.Equal(JsonSchemaType.Array, requestBodySchema.Type);
// With an `items` sub-schema should consist of a $ref to Todo
- Assert.Equal("Todo", requestBodySchema.Items.Reference.Id);
- Assert.Equal(requestBodySchema.Items.Reference.Id, requestBodySchema2.Items.Reference.Id);
+ Assert.Equal("Todo", ((OpenApiSchemaReference)requestBodySchema.Items).Reference.Id);
+ Assert.Equal(((OpenApiSchemaReference)requestBodySchema.Items).Reference.Id, ((OpenApiSchemaReference)requestBodySchema2.Items).Reference.Id);
Assert.Equal(4, requestBodySchema.Items.Properties.Count);
});
}
@@ -293,7 +298,7 @@ await VerifyOpenApiDocument(builder, options, document =>
var getOperation = path.Operations[OperationType.Get];
var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema;
// Schemas are distinct because of applied transformer so no reference is used.
- Assert.NotEqual(requestSchema.Reference.Id, responseSchema.Reference.Id);
+ Assert.NotEqual(((OpenApiSchemaReference)requestSchema).Reference.Id, ((OpenApiSchemaReference)responseSchema).Reference.Id);
Assert.Equal("todo", ((OpenApiAny)requestSchema.Extensions["x-my-extension"]).Node.GetValue());
Assert.False(responseSchema.Extensions.TryGetValue("x-my-extension", out var _));
});
@@ -346,14 +351,14 @@ static void VerifyDocument(OpenApiDocument document)
// }
// Both container types should point to the same reference ID
- Assert.Equal("TodoListContainer", requestBodySchema.Reference.Id);
- Assert.Equal(requestBodySchema.Reference.Id, requestBodySchema2.Reference.Id);
+ Assert.Equal("TodoListContainer", ((OpenApiSchemaReference)requestBodySchema).Reference.Id);
+ Assert.Equal(((OpenApiSchemaReference)requestBodySchema).Reference.Id, ((OpenApiSchemaReference)requestBodySchema2).Reference.Id);
// The referenced schema should have an array type with items pointing to Todo
var effectiveSchema = requestBodySchema;
var todosProperty = effectiveSchema.Properties["todos"];
Assert.Equal(JsonSchemaType.Null | JsonSchemaType.Array, todosProperty.Type);
var itemsSchema = todosProperty.Items;
- Assert.Equal("Todo", itemsSchema.Reference.Id);
+ Assert.Equal("Todo", ((OpenApiSchemaReference)itemsSchema).Reference.Id);
Assert.Equal(4, itemsSchema.Properties.Count);
}
}
@@ -377,19 +382,19 @@ await VerifyOpenApiDocument(builder, document =>
var requestSchema = operation.RequestBody.Content["application/json"].Schema;
// Assert $ref used for top-level
- Assert.Equal("Level1", requestSchema.Reference.Id);
+ Assert.Equal("Level1", ((OpenApiSchemaReference)requestSchema).Reference.Id);
// Assert that $ref is used for Level1.Item2
var level1Schema = requestSchema;
- Assert.Equal("Level2", level1Schema.Properties["item2"].Reference.Id);
+ Assert.Equal("Level2", ((OpenApiSchemaReference)level1Schema.Properties["item2"]).Reference.Id);
// Assert that $ref is used for Level2.Item3
var level2Schema = level1Schema.Properties["item2"];
- Assert.Equal("Level3", level2Schema.Properties["item3"].Reference.Id);
+ Assert.Equal("Level3", ((OpenApiSchemaReference)level2Schema.Properties["item3"]).Reference.Id);
// Assert that no $ref is used for string property
var level3Schema = level2Schema.Properties["item3"];
- Assert.Null(level3Schema.Properties["terminate"].Reference);
+ Assert.IsNotType(level3Schema.Properties["terminate"]);
});
}
@@ -441,45 +446,18 @@ await VerifyOpenApiDocument(builder, document =>
var requestSchema = operation.RequestBody.Content["application/json"].Schema;
// Assert $ref used for top-level
- Assert.Equal("DeeplyNestedLevel1", requestSchema.Reference.Id);
+ Assert.Equal("DeeplyNestedLevel1", ((OpenApiSchemaReference)requestSchema).Reference.Id);
// Assert that $ref is used for all nested levels
var levelSchema = requestSchema;
for (var level = 2; level < 36; level++)
{
- Assert.Equal($"DeeplyNestedLevel{level}", levelSchema.Properties[$"item{level}"].Reference.Id);
+ Assert.Equal($"DeeplyNestedLevel{level}", ((OpenApiSchemaReference)levelSchema.Properties[$"item{level}"]).Reference.Id);
levelSchema = levelSchema.Properties[$"item{level}"];
}
});
}
- [Fact]
- public async Task SelfReferenceMapperOnlyOperatesOnSchemaReferenceTypes()
- {
- var builder = CreateBuilder();
-
- builder.MapGet("/todo", () => new Todo(1, "Item1", false, DateTime.Now));
-
- var options = new OpenApiOptions();
- options.AddSchemaTransformer((schema, context, cancellationToken) =>
- {
- if (context.JsonTypeInfo.Type == typeof(Todo))
- {
- schema.Reference = new OpenApiReference { Id = "#", Type = ReferenceType.Link };
- }
- return Task.CompletedTask;
- });
-
- await VerifyOpenApiDocument(builder, options, document =>
- {
- var operation = document.Paths["/todo"].Operations[OperationType.Get];
- var response = operation.Responses["200"].Content["application/json"];
- var responseSchema = response.Schema;
- Assert.Equal("#", responseSchema.Reference.Id);
- Assert.Equal(ReferenceType.Link, responseSchema.Reference.Type);
- });
- }
-
[Fact]
public async Task SupportsNestedSchemasWithSelfReference()
{
@@ -494,7 +472,7 @@ await VerifyOpenApiDocument(builder, document =>
var requestSchema = operation.RequestBody.Content["application/json"].Schema;
// Assert $ref used for top-level
- Assert.Equal("LocationContainer", requestSchema.Reference.Id);
+ Assert.Equal("LocationContainer", ((OpenApiSchemaReference)requestSchema).Reference.Id);
// Assert that only expected schema references are generated
Assert.Equal(3, document.Components.Schemas.Count);
@@ -571,7 +549,7 @@ await VerifyOpenApiDocument(builder, document =>
var requestSchema = operation.RequestBody.Content["application/json"].Schema;
// Assert $ref used for top-level
- Assert.Equal("ParentObject", requestSchema.Reference.Id);
+ Assert.Equal("ParentObject", ((OpenApiSchemaReference)requestSchema).Reference.Id);
// Assert that only two schemas are generated
Assert.Equal(2, document.Components.Schemas.Count);
@@ -640,13 +618,13 @@ await VerifyOpenApiDocument(builder, document =>
var requestSchema = operation.RequestBody.Content["application/json"].Schema;
// Assert $ref used for top-level
- Assert.Equal("Root", requestSchema.Reference.Id);
+ Assert.Equal("Root", ((OpenApiSchemaReference)requestSchema).Reference.Id);
// Assert that $ref is used for nested Item1
- Assert.Equal("Item", requestSchema.Properties["item1"].Reference.Id);
+ Assert.Equal("Item", ((OpenApiSchemaReference)requestSchema.Properties["item1"]).Reference.Id);
// Assert that $ref is used for nested Item2
- Assert.Equal("Item", requestSchema.Properties["item2"].Reference.Id);
+ Assert.Equal("Item", ((OpenApiSchemaReference)requestSchema.Properties["item2"]).Reference.Id);
});
}
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs
index 6c9857ac1709..699512213905 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs
@@ -9,6 +9,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.References;
public class SchemaTransformerTests : OpenApiDocumentServiceTestBase
{
@@ -505,7 +506,7 @@ await VerifyOpenApiDocument(builder, options, document =>
var path = document.Paths["/shape"];
var postOperation = path.Operations[OperationType.Post];
var requestSchema = postOperation.RequestBody.Content["application/json"].Schema;
- var triangleSubschema = Assert.Single(requestSchema.AnyOf.Where(s => s.Reference.Id == "ShapeTriangle"));
+ var triangleSubschema = Assert.Single(requestSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeTriangle"));
Assert.True(triangleSubschema.Extensions.TryGetValue("x-my-extension", out var _));
// Assert that the standalone `Triangle` type has been updated
@@ -580,13 +581,13 @@ await VerifyOpenApiDocument(builder, options, document =>
var getOperation = path.Operations[OperationType.Get];
var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema;
var itemSchema = responseSchema.Items;
- var triangleSubschema = Assert.Single(itemSchema.AnyOf.Where(s => s.Reference.Id == "ShapeTriangle"));
+ var triangleSubschema = Assert.Single(itemSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeTriangle"));
// Assert that the x-my-extension type is set to this-is-a-triangle
Assert.True(triangleSubschema.Extensions.TryGetValue("x-my-extension", out var triangleExtension));
Assert.Equal("this-is-a-triangle", ((OpenApiAny)triangleExtension).Node.GetValue());
// Assert that the `Square` type within the polymorphic type list has been updated
- var squareSubschema = Assert.Single(itemSchema.AnyOf.Where(s => s.Reference.Id == "ShapeSquare"));
+ var squareSubschema = Assert.Single(itemSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeSquare"));
// Assert that the x-my-extension type is set to this-is-a-square
Assert.True(squareSubschema.Extensions.TryGetValue("x-my-extension", out var squareExtension));
Assert.Equal("this-is-a-square", ((OpenApiAny)squareExtension).Node.GetValue());
@@ -621,13 +622,13 @@ await VerifyOpenApiDocument(builder, options, document =>
var getOperation = path.Operations[OperationType.Get];
var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema;
var someShapeSchema = responseSchema.Properties["someShape"];
- var triangleSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => s.Reference.Id == "ShapeTriangle"));
+ var triangleSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeTriangle"));
// Assert that the x-my-extension type is set to this-is-a-triangle
Assert.True(triangleSubschema.Extensions.TryGetValue("x-my-extension", out var triangleExtension));
Assert.Equal("this-is-a-triangle", ((OpenApiAny)triangleExtension).Node.GetValue());
// Assert that the `Square` type within the polymorphic type list has been updated
- var squareSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => s.Reference.Id == "ShapeSquare"));
+ var squareSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeSquare"));
// Assert that the x-my-extension type is set to this-is-a-square
Assert.True(squareSubschema.Extensions.TryGetValue("x-my-extension", out var squareExtension));
Assert.Equal("this-is-a-square", ((OpenApiAny)squareExtension).Node.GetValue());
@@ -662,13 +663,13 @@ await VerifyOpenApiDocument(builder, options, document =>
var getOperation = path.Operations[OperationType.Get];
var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema;
var someShapeSchema = responseSchema.Items.Properties["someShape"];
- var triangleSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => s.Reference.Id == "ShapeTriangle"));
+ var triangleSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeTriangle"));
// Assert that the x-my-extension type is set to this-is-a-triangle
Assert.True(triangleSubschema.Extensions.TryGetValue("x-my-extension", out var triangleExtension));
Assert.Equal("this-is-a-triangle", ((OpenApiAny)triangleExtension).Node.GetValue());
// Assert that the `Square` type within the polymorphic type list has been updated
- var squareSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => s.Reference.Id == "ShapeSquare"));
+ var squareSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeSquare"));
// Assert that the x-my-extension type is set to this-is-a-square
Assert.True(squareSubschema.Extensions.TryGetValue("x-my-extension", out var squareExtension));
Assert.Equal("this-is-a-square", ((OpenApiAny)squareExtension).Node.GetValue());
@@ -747,7 +748,7 @@ await VerifyOpenApiDocument(builder, options, document =>
var shapePath = document.Paths["/shape"];
var shapeOperation = shapePath.Operations[OperationType.Post];
var shapeRequestSchema = shapeOperation.RequestBody.Content["application/json"].Schema;
- var triangleSchema = Assert.Single(shapeRequestSchema.AnyOf.Where(s => s.Reference.Id == "ShapeTriangle"));
+ var triangleSchema = Assert.Single(shapeRequestSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeTriangle"));
Assert.True(((OpenApiAny)triangleSchema.Not.Extensions["modified-by-not-schema-transformer"]).Node.GetValue());
});
@@ -757,7 +758,8 @@ static void UseNotSchemaTransformer(OpenApiOptions options, Func