diff --git a/eng/Dependencies.props b/eng/Dependencies.props index a9dae44b5f1e..15355024250e 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -70,7 +70,7 @@ and are generated based on the last package release. - + diff --git a/eng/Versions.props b/eng/Versions.props index 59acf1c84549..1fb226b66838 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -339,8 +339,8 @@ $(XunitVersion) 2.8.2 5.2.2 - 2.0.0-preview.11 - 2.0.0-preview.11 + 2.0.0-preview.17 + 2.0.0-preview.17 6.0.322601 1.10.93 diff --git a/src/OpenApi/OpenApi.slnf b/src/OpenApi/OpenApi.slnf index 0e62a8e557ea..48954eeb5878 100644 --- a/src/OpenApi/OpenApi.slnf +++ b/src/OpenApi/OpenApi.slnf @@ -17,7 +17,6 @@ "src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj", "src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests\\Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests.csproj", "src\\OpenApi\\test\\Microsoft.AspNetCore.OpenApi.Tests\\Microsoft.AspNetCore.OpenApi.Tests.csproj", - "src\\OpenApi\\sample\\Sample.csproj", "src\\OpenApi\\perf\\Microbenchmarks\\Microsoft.AspNetCore.OpenApi.Microbenchmarks.csproj" ] } diff --git a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs index a730bcc8722e..6b6e7139e373 100644 --- a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs +++ b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs @@ -59,6 +59,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; + using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Any; @@ -341,9 +342,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name); if (operationParameter is not null) { - var targetOperationParameter = operationParameter is OpenApiParameterReference reference - ? reference.Target - : (OpenApiParameter)operationParameter; + var targetOperationParameter = UnwrapOpenApiParameter(operationParameter); targetOperationParameter.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { @@ -359,7 +358,12 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform requestBody.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { - foreach (var mediaType in requestBody.Content.Values) + var content = requestBody?.Content?.Values; + if (content is null) + { + continue; + } + foreach (var mediaType in content) { mediaType.Example = jsonString.Parse(); } @@ -383,6 +387,29 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform return Task.CompletedTask; } + + private static OpenApiParameter UnwrapOpenApiParameter(IOpenApiParameter sourceParameter) + { + if (sourceParameter is OpenApiParameterReference parameterReference) + { + if (parameterReference.Target is OpenApiParameter target) + { + return target; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } + else if (sourceParameter is OpenApiParameter directParameter) + { + return directParameter; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } } {{GeneratedCodeAttribute}} diff --git a/src/OpenApi/perf/Microbenchmarks/TransformersBenchmark.cs b/src/OpenApi/perf/Microbenchmarks/TransformersBenchmark.cs index d7fca2c1bcf5..b9c3267ca07f 100644 --- a/src/OpenApi/perf/Microbenchmarks/TransformersBenchmark.cs +++ b/src/OpenApi/perf/Microbenchmarks/TransformersBenchmark.cs @@ -102,6 +102,7 @@ public void SchemaTransformer_Setup() { _options.AddSchemaTransformer((schema, context, token) => { + schema.Extensions ??= []; if (context.JsonTypeInfo.Type == typeof(Todo) && context.ParameterDescription != null) { schema.Extensions["x-my-extension"] = new OpenApiAny(context.ParameterDescription.Name); @@ -175,6 +176,7 @@ private class SchemaTransformer : IOpenApiSchemaTransformer { public Task TransformAsync(OpenApiSchema schema, OpenApiSchemaTransformerContext context, CancellationToken cancellationToken) { + schema.Extensions ??= []; if (context.JsonTypeInfo.Type == typeof(Todo) && context.ParameterDescription != null) { schema.Extensions["x-my-extension"] = new OpenApiAny(context.ParameterDescription.Name); diff --git a/src/OpenApi/src/Extensions/ApiDescriptionExtensions.cs b/src/OpenApi/src/Extensions/ApiDescriptionExtensions.cs index 9e134604641a..eab68b3dfb56 100644 --- a/src/OpenApi/src/Extensions/ApiDescriptionExtensions.cs +++ b/src/OpenApi/src/Extensions/ApiDescriptionExtensions.cs @@ -4,30 +4,30 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Net.Http; using System.Text; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Routing.Patterns; -using Microsoft.OpenApi.Models; internal static class ApiDescriptionExtensions { /// - /// Maps the HTTP method of the ApiDescription to the OpenAPI . + /// Maps the HTTP method of the ApiDescription to the HttpMethod. /// - /// The ApiDescription to resolve an operation type from. - /// The associated with the given . - public static OperationType GetOperationType(this ApiDescription apiDescription) => + /// The ApiDescription to resolve an HttpMethod from. + /// The associated with the given . + public static HttpMethod GetHttpMethod(this ApiDescription apiDescription) => apiDescription.HttpMethod?.ToUpperInvariant() switch { - "GET" => OperationType.Get, - "POST" => OperationType.Post, - "PUT" => OperationType.Put, - "DELETE" => OperationType.Delete, - "PATCH" => OperationType.Patch, - "HEAD" => OperationType.Head, - "OPTIONS" => OperationType.Options, - "TRACE" => OperationType.Trace, + "GET" => HttpMethod.Get, + "POST" => HttpMethod.Post, + "PUT" => HttpMethod.Put, + "DELETE" => HttpMethod.Delete, + "PATCH" => HttpMethod.Patch, + "HEAD" => HttpMethod.Head, + "OPTIONS" => HttpMethod.Options, + "TRACE" => HttpMethod.Trace, _ => throw new InvalidOperationException($"Unsupported HTTP method: {apiDescription.HttpMethod}"), }; diff --git a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs index 49c8f97bf232..777913ccc567 100644 --- a/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs +++ b/src/OpenApi/src/Extensions/JsonNodeSchemaExtensions.cs @@ -386,7 +386,7 @@ internal static void MapPolymorphismOptionsToDiscriminator(this JsonNode schema, // that we hardcode here. We could use `OpenApiReference` to construct the reference and // serialize it but we use a hardcoded string here to avoid allocating a new object and // working around Microsoft.OpenApi's serialization libraries. - mappings[$"{discriminator}"] = $"#/components/schemas/{createSchemaReferenceId(context.TypeInfo)}{createSchemaReferenceId(jsonDerivedType)}"; + mappings[$"{discriminator}"] = $"{createSchemaReferenceId(context.TypeInfo)}{createSchemaReferenceId(jsonDerivedType)}"; } } schema[OpenApiSchemaKeywords.DiscriminatorKeyword] = polymorphismOptions.TypeDiscriminatorPropertyName; diff --git a/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs b/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs index cea73303ef25..46b8fab926a6 100644 --- a/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs +++ b/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; @@ -9,6 +10,7 @@ using Microsoft.AspNetCore.OpenApi; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models.Interfaces; +using Microsoft.OpenApi.Models.References; using OpenApiConstants = Microsoft.AspNetCore.OpenApi.OpenApiConstants; internal sealed partial class OpenApiJsonSchema @@ -256,12 +258,12 @@ public static void ReadProperty(ref Utf8JsonReader reader, string propertyName, case OpenApiSchemaKeywords.MinimumKeyword: reader.Read(); var minimum = reader.GetDecimal(); - schema.Minimum = minimum; + schema.Minimum = minimum.ToString(CultureInfo.InvariantCulture); break; case OpenApiSchemaKeywords.MaximumKeyword: reader.Read(); var maximum = reader.GetDecimal(); - schema.Maximum = maximum; + schema.Maximum = maximum.ToString(CultureInfo.InvariantCulture); break; case OpenApiSchemaKeywords.PatternKeyword: reader.Read(); @@ -302,25 +304,30 @@ public static void ReadProperty(ref Utf8JsonReader reader, string propertyName, var mappings = ReadDictionary(ref reader); if (mappings is not null) { - schema.Discriminator.Mapping = mappings; + schema.Discriminator ??= new OpenApiDiscriminator(); + foreach (var kvp in mappings) + { + schema.Discriminator.Mapping ??= []; + schema.Discriminator.Mapping[kvp.Key] = new OpenApiSchemaReference(kvp.Value); + } } break; case OpenApiConstants.SchemaId: reader.Read(); - schema.Annotations ??= new Dictionary(); - schema.Annotations.Add(OpenApiConstants.SchemaId, reader.GetString()); + schema.Metadata ??= []; + schema.Metadata.Add(OpenApiConstants.SchemaId, reader.GetString() ?? string.Empty); break; // OpenAPI does not support the `const` keyword in its schema implementation, so // we map it to its closest approximation, an enum with a single value, here. case OpenApiSchemaKeywords.ConstKeyword: reader.Read(); - schema.Enum = [ReadJsonNode(ref reader, out var constType)]; + schema.Enum = ReadJsonNode(ref reader, out var constType) is { } jsonNode ? [jsonNode] : []; schema.Type = constType; break; case OpenApiSchemaKeywords.RefKeyword: reader.Read(); - schema.Annotations ??= new Dictionary(); - schema.Annotations[OpenApiConstants.RefId] = reader.GetString(); + schema.Metadata ??= []; + schema.Metadata[OpenApiConstants.RefId] = reader.GetString() ?? string.Empty; break; default: reader.Skip(); diff --git a/src/OpenApi/src/Services/OpenApiConstants.cs b/src/OpenApi/src/Services/OpenApiConstants.cs index 8e5d29824514..8c34704cf2b6 100644 --- a/src/OpenApi/src/Services/OpenApiConstants.cs +++ b/src/OpenApi/src/Services/OpenApiConstants.cs @@ -1,7 +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 Microsoft.OpenApi.Models; +using System.Net.Http; namespace Microsoft.AspNetCore.OpenApi; @@ -14,19 +14,19 @@ internal static class OpenApiConstants 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 + // Since there's a finite set of HTTP methods that can be included in a given + // OpenApiPaths, we can pre-allocate an array of these methods and use a direct // lookup on the OpenApiPaths dictionary to avoid allocating an enumerator // over the KeyValuePairs in OpenApiPaths. - internal static readonly OperationType[] OperationTypes = [ - OperationType.Get, - OperationType.Post, - OperationType.Put, - OperationType.Delete, - OperationType.Options, - OperationType.Head, - OperationType.Patch, - OperationType.Trace + internal static readonly HttpMethod[] HttpMethods = [ + HttpMethod.Get, + HttpMethod.Post, + HttpMethod.Put, + HttpMethod.Delete, + HttpMethod.Options, + HttpMethod.Head, + HttpMethod.Patch, + HttpMethod.Trace ]; // Represents primitive types that should never be represented as // a schema reference and always inlined. diff --git a/src/OpenApi/src/Services/OpenApiDocumentService.cs b/src/OpenApi/src/Services/OpenApiDocumentService.cs index a358f56d08a9..3e39fc929c5a 100644 --- a/src/OpenApi/src/Services/OpenApiDocumentService.cs +++ b/src/OpenApi/src/Services/OpenApiDocumentService.cs @@ -10,6 +10,7 @@ using System.Globalization; using System.IO.Pipelines; using System.Linq; +using System.Net.Http; using System.Reflection; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server.Features; @@ -90,7 +91,11 @@ 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); + // Sort schemas by key name for better readability and consistency + // This works around an API change in OpenAPI.NET + document.Components.Schemas = new Dictionary( + document.Components.Schemas.OrderBy(kvp => kvp.Key), + StringComparer.Ordinal); } return document; } @@ -161,15 +166,15 @@ internal async Task ForEachOperationAsync( { foreach (var pathItem in document.Paths.Values) { - for (var i = 0; i < OpenApiConstants.OperationTypes.Length; i++) + for (var i = 0; i < OpenApiConstants.HttpMethods.Length; i++) { - var operationType = OpenApiConstants.OperationTypes[i]; - if (!pathItem.Operations.TryGetValue(operationType, out var operation)) + var httpMethod = OpenApiConstants.HttpMethods[i]; + if (pathItem.Operations is null || !pathItem.Operations.TryGetValue(httpMethod, out var operation)) { continue; } - if (operation.Annotations is { } annotations && + if (operation.Metadata is { } annotations && annotations.TryGetValue(OpenApiConstants.DescriptionId, out var descriptionId) && descriptionId is string descriptionIdString && TryGetCachedOperationTransformerContext(descriptionIdString, out var operationContext)) @@ -183,7 +188,7 @@ descriptionId is string descriptionIdString && // user in another operation transformer or if the lookup for operation transformer // context resulted in a cache miss. As an alternative here, we could just to implement // the "slow-path" and look up the ApiDescription associated with the OpenApiOperation - // using the OperationType and given path, but we'll avoid this for now. + // using the HttpMethod and given path, but we'll avoid this for now. throw new InvalidOperationException("Cached operation transformer context not found. Please ensure that the operation contains the `x-aspnetcore-id` extension attribute."); } } @@ -253,7 +258,7 @@ private async Task GetOpenApiPathsAsync( return paths; } - private async Task> GetOperationsAsync( + private async Task> GetOperationsAsync( IGrouping descriptions, OpenApiDocument document, IServiceProvider scopedServiceProvider, @@ -261,12 +266,12 @@ private async Task> GetOperationsAsy IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken) { - var operations = new Dictionary(); + var operations = new Dictionary(); foreach (var description in descriptions) { var operation = await GetOperationAsync(description, document, scopedServiceProvider, schemaTransformers, cancellationToken); - operation.Annotations ??= new Dictionary(); - operation.Annotations.Add(OpenApiConstants.DescriptionId, description.ActionDescriptor.Id); + operation.Metadata ??= []; + operation.Metadata.Add(OpenApiConstants.DescriptionId, description.ActionDescriptor.Id); var operationContext = new OpenApiOperationTransformerContext { @@ -278,7 +283,7 @@ private async Task> GetOperationsAsy }; _operationTransformerContextCache.TryAdd(description.ActionDescriptor.Id, operationContext); - operations[description.GetOperationType()] = operation; + operations[description.GetHttpMethod()] = operation; // Use index-based for loop to avoid allocating an enumerator with a foreach. for (var i = 0; i < operationTransformers.Length; i++) @@ -338,7 +343,7 @@ private static HashSet GetTags(ApiDescription description, HashSet tags = []; foreach (var tag in tagsMetadata.Tags) { - document.Tags ??= new HashSet(); + document.Tags ??= []; document.Tags.Add(new OpenApiTag { Name = tag }); tags.Add(new OpenApiTagReference(tag, document)); @@ -348,9 +353,9 @@ private static HashSet GetTags(ApiDescription description, // If no tags are specified, use the controller name as the tag. This effectively // allows us to group endpoints by the "resource" concept (e.g. users, todos, etc.) var controllerName = description.ActionDescriptor.RouteValues["controller"]; - document.Tags ??= new HashSet(); + document.Tags ??= []; document.Tags.Add(new OpenApiTag { Name = controllerName }); - return [new(controllerName, document)]; + return controllerName is not null ? [new(controllerName, document)] : []; } private async Task GetResponsesAsync( @@ -398,7 +403,7 @@ private async Task GetResponseAsync( var response = new OpenApiResponse { Description = apiResponseType.Description ?? ReasonPhrases.GetReasonPhrase(statusCode), - Content = new Dictionary() + Content = [] }; // ApiResponseFormats aggregates information about the supported response content types @@ -538,10 +543,11 @@ private async Task GetFormRequestBody( // serializing a form collection from an empty body. Instead, requiredness // must be set on a per-parameter basis. See below. Required = true, - Content = new Dictionary() + Content = [] }; - IOpenApiSchema schema = new OpenApiSchema { Type = JsonSchemaType.Object, Properties = new Dictionary() }; + var schema = new OpenApiSchema { Type = JsonSchemaType.Object, Properties = [] }; + IOpenApiSchema? complexTypeSchema = null; // 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. // @@ -568,10 +574,12 @@ private async Task GetFormRequestBody( { if (IsRequired(description)) { + schema.Required ??= []; schema.Required.Add(description.Name); } if (hasMultipleFormParameters) { + schema.AllOf ??= []; schema.AllOf.Add(new OpenApiSchema { Type = JsonSchemaType.Object, @@ -583,6 +591,7 @@ private async Task GetFormRequestBody( } else { + schema.Properties ??= []; schema.Properties[description.Name] = parameterSchema; } } @@ -600,14 +609,17 @@ private async Task GetFormRequestBody( // The form-binding implementation will capture them implicitly. if (isComplexType) { + schema.AllOf ??= []; schema.AllOf.Add(parameterSchema); } else { if (IsRequired(description)) { + schema.Required ??= []; schema.Required.Add(description.Name); } + schema.AllOf ??= []; schema.AllOf.Add(new OpenApiSchema { Type = JsonSchemaType.Object, @@ -622,14 +634,16 @@ private async Task GetFormRequestBody( { if (isComplexType) { - schema = parameterSchema; + complexTypeSchema = parameterSchema; } else { if (IsRequired(description)) { + schema.Required ??= []; schema.Required.Add(description.Name); } + schema.Properties ??= []; schema.Properties[description.Name] = parameterSchema; } } @@ -639,17 +653,19 @@ private async Task GetFormRequestBody( { if (hasMultipleFormParameters) { - var propertySchema = new OpenApiSchema { Type = JsonSchemaType.Object, Properties = new Dictionary() }; + var propertySchema = new OpenApiSchema { Type = JsonSchemaType.Object, Properties = [] }; foreach (var description in parameter) { propertySchema.Properties[description.Name] = await _componentService.GetOrCreateSchemaAsync(document, description.Type, scopedServiceProvider, schemaTransformers, description, cancellationToken: cancellationToken); } + schema.AllOf ??= []; schema.AllOf.Add(propertySchema); } else { foreach (var description in parameter) { + schema.Properties ??= []; schema.Properties[description.Name] = await _componentService.GetOrCreateSchemaAsync(document, description.Type, scopedServiceProvider, schemaTransformers, description, cancellationToken: cancellationToken); } } @@ -661,7 +677,7 @@ private async Task GetFormRequestBody( var contentType = requestFormat.MediaType; requestBody.Content[contentType] = new OpenApiMediaType { - Schema = schema + Schema = complexTypeSchema ?? schema }; } @@ -695,7 +711,7 @@ private async Task GetJsonRequestBody( var requestBody = new OpenApiRequestBody { Required = IsRequired(bodyParameter), - Content = new Dictionary(), + Content = [], Description = GetParameterDescriptionFromAttribute(bodyParameter) }; diff --git a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs index b2dbbfe3fd90..3781b0d420b3 100644 --- a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs +++ b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs @@ -145,14 +145,10 @@ internal async Task GetOrCreateSchemaAsync(OpenApiDocument docum 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."); + var schema = UnwrapOpenApiSchema(inputSchema); - if (schema.Annotations is not null && - schema.Annotations.TryGetValue(OpenApiConstants.SchemaId, out var resolvedBaseSchemaId)) + if (schema.Metadata is not null && + schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var resolvedBaseSchemaId)) { if (schema.AnyOf is { Count: > 0 }) { @@ -206,9 +202,9 @@ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument documen // 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.Annotations is not null && - schema.Annotations.ContainsKey(OpenApiConstants.RefId) && - schema.Annotations.TryGetValue(OpenApiConstants.SchemaId, out var schemaId) && + if (schema.Metadata is not null && + schema.Metadata.ContainsKey(OpenApiConstants.RefId) && + schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var schemaId) && schemaId is string schemaIdString) { return document.AddOpenApiSchemaByReference(schemaIdString, schema); @@ -218,15 +214,15 @@ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument documen // 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.Annotations is not null && - !schema.Annotations.ContainsKey(OpenApiConstants.RefId) && - schema.Annotations.TryGetValue(OpenApiConstants.SchemaId, out var referenceId) && + if (schema is OpenApiSchema && schema.Metadata is not null && + !schema.Metadata.ContainsKey(OpenApiConstants.RefId) && + schema.Metadata.TryGetValue(OpenApiConstants.SchemaId, out var referenceId) && referenceId is string referenceIdString) { var targetReferenceId = baseSchemaId is not null ? $"{baseSchemaId}{referenceIdString}" : referenceIdString; - if (targetReferenceId is not null) + if (!string.IsNullOrEmpty(targetReferenceId)) { return document.AddOpenApiSchemaByReference(targetReferenceId, schema); } @@ -235,6 +231,29 @@ internal static IOpenApiSchema ResolveReferenceForSchema(OpenApiDocument documen return schema; } + private static OpenApiSchema UnwrapOpenApiSchema(IOpenApiSchema sourceSchema) + { + if (sourceSchema is OpenApiSchemaReference schemaReference) + { + if (schemaReference.Target is OpenApiSchema target) + { + return target; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiSchema)} or {nameof(OpenApiSchemaReference)}."); + } + } + else if (sourceSchema is OpenApiSchema directSchema) + { + return directSchema; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiSchema)} or {nameof(OpenApiSchemaReference)}."); + } + } + internal async Task ApplySchemaTransformersAsync(OpenApiDocument? document, IOpenApiSchema schema, Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, CancellationToken cancellationToken = default) { if (schemaTransformers.Length == 0) @@ -268,11 +287,7 @@ private async Task InnerApplySchemaTransformersAsync(IOpenApiSchema inputSchema, 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."); + var schema = UnwrapOpenApiSchema(inputSchema); 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/AdditionalTextsTests.Schemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs index b30dee1a8c3e..d63d608c86a4 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/AdditionalTextsTests.Schemas.cs @@ -1,9 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Globalization; +using System.Net.Http; using System.Text.Json.Nodes; -using Microsoft.OpenApi.Models; namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests; @@ -194,31 +193,31 @@ public static void ExternalMethod(string name = "Tester") { } await SnapshotTestHelper.Verify(source, generator, references, out var compilation, out var additionalAssemblies); await SnapshotTestHelper.VerifyOpenApi(compilation, additionalAssemblies, document => { - var path = document.Paths["/todo"].Operations[OperationType.Post]; + var path = document.Paths["/todo"].Operations[HttpMethod.Post]; var todo = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This is a todo item.", todo.Description); - path = document.Paths["/project"].Operations[OperationType.Post]; + path = document.Paths["/project"].Operations[HttpMethod.Post]; var project = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The project that contains Todo items.", project.Description); - path = document.Paths["/board"].Operations[OperationType.Post]; + path = document.Paths["/board"].Operations[HttpMethod.Post]; var board = path.RequestBody.Content["application/json"].Schema; Assert.Equal("An item on the board.", board.Description); - path = document.Paths["/project-record"].Operations[OperationType.Post]; + path = document.Paths["/project-record"].Operations[HttpMethod.Post]; project = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The name of the project.", project.Properties["name"].Description); Assert.Equal("The description of the project.", project.Properties["description"].Description); - path = document.Paths["/todo-with-description"].Operations[OperationType.Post]; + path = document.Paths["/todo-with-description"].Operations[HttpMethod.Post]; todo = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The identifier of the todo.", todo.Properties["id"].Description); Assert.Equal("The name of the todo.", todo.Properties["name"].Description); Assert.Equal("Another description of the todo.", todo.Properties["description"].Description); - path = document.Paths["/type-with-examples"].Operations[OperationType.Post]; + path = document.Paths["/type-with-examples"].Operations[HttpMethod.Post]; var typeWithExamples = path.RequestBody.Content["application/json"].Schema; var booleanTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["booleanType"].Example); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs index 72e6382a2d56..cb3dce2068d4 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/CompletenessTests.cs @@ -1,9 +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.Globalization; -using System.Text.Json.Nodes; -using Microsoft.OpenApi.Models; +using System.Net.Http; namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests; @@ -444,42 +442,42 @@ public static T GetGenericValue(T para) await SnapshotTestHelper.Verify(source, generator, out var compilation); await SnapshotTestHelper.VerifyOpenApi(compilation, document => { - var path = document.Paths["/example-class"].Operations[OperationType.Post]; + var path = document.Paths["/example-class"].Operations[HttpMethod.Post]; var exampleClass = path.RequestBody.Content["application/json"].Schema; Assert.Equal("Every class and member should have a one sentence\nsummary describing its purpose.", exampleClass.Description, ignoreLineEndingDifferences: true); Assert.Equal("The `Label` property represents a label\nfor this instance.", exampleClass.Properties["label"].Description, ignoreLineEndingDifferences: true); - path = document.Paths["/person"].Operations[OperationType.Post]; + path = document.Paths["/person"].Operations[HttpMethod.Post]; var person = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This is an example of a positional record.", person.Description); Assert.Equal("This tag will apply to the primary constructor parameter.", person.Properties["firstName"].Description); Assert.Equal("This tag will apply to the primary constructor parameter.", person.Properties["lastName"].Description); - path = document.Paths["/derived-class"].Operations[OperationType.Post]; + path = document.Paths["/derived-class"].Operations[HttpMethod.Post]; var derivedClass = path.RequestBody.Content["application/json"].Schema; Assert.Equal("A summary about this class.", derivedClass.Description); - path = document.Paths["/main-class"].Operations[OperationType.Post]; + path = document.Paths["/main-class"].Operations[HttpMethod.Post]; var mainClass = path.RequestBody.Content["application/json"].Schema; Assert.Equal("A summary about this class.", mainClass.Description); - path = document.Paths["/implementing-class"].Operations[OperationType.Post]; + path = document.Paths["/implementing-class"].Operations[HttpMethod.Post]; var implementingClass = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This interface would describe all the methods in\nits contract.", implementingClass.Description, ignoreLineEndingDifferences: true); - path = document.Paths["/inherit-only-returns"].Operations[OperationType.Post]; + path = document.Paths["/inherit-only-returns"].Operations[HttpMethod.Post]; var inheritOnlyReturns = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This class shows hows you can \"inherit\" the doc\ncomments from one method in another method.", inheritOnlyReturns.Description, ignoreLineEndingDifferences: true); - path = document.Paths["/inherit-all-but-remarks"].Operations[OperationType.Post]; + path = document.Paths["/inherit-all-but-remarks"].Operations[HttpMethod.Post]; var inheritAllButRemarks = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This class shows an example of sharing comments across methods.", inheritAllButRemarks.Description); - path = document.Paths["/generic-class"].Operations[OperationType.Post]; + path = document.Paths["/generic-class"].Operations[HttpMethod.Post]; var genericClass = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This is a generic class.", genericClass.Description); - path = document.Paths["/generic-parent"].Operations[OperationType.Post]; + path = document.Paths["/generic-parent"].Operations[HttpMethod.Post]; var genericParent = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This class validates the behavior for mapping\ngeneric types to open generics for use in\ntypeof expressions.", genericParent.Description, ignoreLineEndingDifferences: true); Assert.Equal("This property is a nullable value type.", genericParent.Properties["id"].Description); @@ -488,7 +486,7 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => Assert.Equal("This property is a tuple with a generic type inside.", genericParent.Properties["tupleWithGenericProp"].Description); Assert.Equal("This property is a tuple with a nested generic type inside.", genericParent.Properties["tupleWithNestedGenericProp"].Description); - path = document.Paths["/params-and-param-refs"].Operations[OperationType.Post]; + path = document.Paths["/params-and-param-refs"].Operations[HttpMethod.Post]; var paramsAndParamRefs = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This shows examples of typeparamref and typeparam tags", paramsAndParamRefs.Description); }); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests.csproj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests.csproj index ff3ca4db6229..3cd2cc963d3e 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests.csproj +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests.csproj @@ -10,7 +10,6 @@ - diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.Controllers.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.Controllers.cs index b88c0e34d2db..5e13b2a06f6b 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.Controllers.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.Controllers.cs @@ -1,7 +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 Microsoft.OpenApi.Models; +using System.Net.Http; namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests; @@ -81,18 +81,18 @@ public record Todo(int Id, string Title, bool Completed); await SnapshotTestHelper.Verify(source, generator, out var compilation); await SnapshotTestHelper.VerifyOpenApi(compilation, document => { - var path = document.Paths["/Test"].Operations[OperationType.Get]; + var path = document.Paths["/Test"].Operations[HttpMethod.Get]; Assert.Equal("A summary of the action.", path.Summary); Assert.Equal("A description of the action.", path.Description); - var path2 = document.Paths["/Test2"].Operations[OperationType.Get]; + var path2 = document.Paths["/Test2"].Operations[HttpMethod.Get]; Assert.Equal("The name of the person.", path2.Parameters[0].Description); Assert.Equal("Returns the greeting.", path2.Responses["200"].Description); - var path2again = document.Paths["/Test2/HelloByInt"].Operations[OperationType.Get]; + var path2again = document.Paths["/Test2/HelloByInt"].Operations[HttpMethod.Get]; Assert.Equal("The id associated with the request.", path2again.Parameters[0].Description); - var path3 = document.Paths["/Test2"].Operations[OperationType.Post]; + var path3 = document.Paths["/Test2"].Operations[HttpMethod.Post]; Assert.Equal("The todo to insert into the database.", path3.RequestBody.Description); }); } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs index 73d46fa6f2f4..c7e873481d02 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Net.Http; using System.Text.Json.Nodes; -using Microsoft.OpenApi.Models; namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests; @@ -231,68 +231,68 @@ public Example(Func function, object? state) : base(function, stat await SnapshotTestHelper.Verify(source, generator, out var compilation); await SnapshotTestHelper.VerifyOpenApi(compilation, document => { - var path = document.Paths["/1"].Operations[OperationType.Get]; + var path = document.Paths["/1"].Operations[HttpMethod.Get]; Assert.Equal("A summary of the action.", path.Summary); Assert.Equal("A description of the action.", path.Description); - var path2 = document.Paths["/2"].Operations[OperationType.Get]; + var path2 = document.Paths["/2"].Operations[HttpMethod.Get]; Assert.Equal("The name of the person.", path2.Parameters[0].Description); Assert.Equal("Returns the greeting.", path2.Responses["200"].Description); - var path3 = document.Paths["/3"].Operations[OperationType.Get]; + var path3 = document.Paths["/3"].Operations[HttpMethod.Get]; Assert.Equal("The name of the person.", path3.Parameters[0].Description); var example = Assert.IsAssignableFrom(path3.Parameters[0].Example); Assert.Equal("\"Testy McTester\"", example.ToJsonString()); - var path4 = document.Paths["/4"].Operations[OperationType.Get]; + var path4 = document.Paths["/4"].Operations[HttpMethod.Get]; var response = path4.Responses["404"]; Assert.Equal("Indicates that the value was not found.", response.Description); - var path5 = document.Paths["/5"].Operations[OperationType.Get]; + var path5 = document.Paths["/5"].Operations[HttpMethod.Get]; Assert.Equal("Indicates that the value was not found.", path5.Responses["404"].Description); Assert.Equal("Indicates that the value is even.", path5.Responses["200"].Description); Assert.Equal("Indicates that the value is less than 50.", path5.Responses["201"].Description); - var path6 = document.Paths["/6"].Operations[OperationType.Post]; + var path6 = document.Paths["/6"].Operations[HttpMethod.Post]; Assert.Equal("Creates a new user.", path6.Summary); Assert.Contains("Sample request:", path6.Description); var userParam = path6.RequestBody.Content["application/json"]; var userExample = Assert.IsAssignableFrom(userParam.Example); Assert.Equal("johndoe", userExample["username"].GetValue()); - var path7 = document.Paths["/7"].Operations[OperationType.Put]; + var path7 = document.Paths["/7"].Operations[HttpMethod.Put]; var idParam = path7.Parameters.First(p => p.Name == "id"); Assert.True(idParam.Deprecated); Assert.Equal("Legacy ID parameter - use uuid instead.", idParam.Description); - var path8 = document.Paths["/8"].Operations[OperationType.Get]; + var path8 = document.Paths["/8"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get8.", path8.Summary); - var path9 = document.Paths["/9"].Operations[OperationType.Get]; + var path9 = document.Paths["/9"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get9.", path9.Summary); - var path10 = document.Paths["/10"].Operations[OperationType.Get]; + var path10 = document.Paths["/10"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get10.", path10.Summary); - var path11 = document.Paths["/11"].Operations[OperationType.Get]; + var path11 = document.Paths["/11"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get11.", path11.Summary); - var path12 = document.Paths["/12"].Operations[OperationType.Get]; + var path12 = document.Paths["/12"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get12.", path12.Summary); - var path13 = document.Paths["/13"].Operations[OperationType.Get]; + var path13 = document.Paths["/13"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get13.", path13.Summary); - var path14 = document.Paths["/14"].Operations[OperationType.Get]; + var path14 = document.Paths["/14"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get14.", path14.Summary); - var path15 = document.Paths["/15"].Operations[OperationType.Get]; + var path15 = document.Paths["/15"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get15.", path15.Summary); - var path16 = document.Paths["/16"].Operations[OperationType.Post]; + var path16 = document.Paths["/16"].Operations[HttpMethod.Post]; Assert.Equal("A summary of Post16.", path16.Summary); - var path17 = document.Paths["/17"].Operations[OperationType.Get]; + var path17 = document.Paths["/17"].Operations[HttpMethod.Get]; Assert.Equal("A summary of Get17.", path17.Summary); }); } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs index 1caa65378abd..290100cdbfc3 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/SchemaTests.cs @@ -1,9 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Globalization; +using System.Net.Http; using System.Text.Json.Nodes; -using Microsoft.OpenApi.Models; namespace Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests; @@ -181,36 +180,36 @@ internal class User : IUser await SnapshotTestHelper.Verify(source, generator, out var compilation); await SnapshotTestHelper.VerifyOpenApi(compilation, document => { - var path = document.Paths["/todo"].Operations[OperationType.Post]; + var path = document.Paths["/todo"].Operations[HttpMethod.Post]; var todo = path.RequestBody.Content["application/json"].Schema; Assert.Equal("This is a todo item.", todo.Description); - path = document.Paths["/project"].Operations[OperationType.Post]; + path = document.Paths["/project"].Operations[HttpMethod.Post]; var project = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The project that contains Todo items.", project.Description); - path = document.Paths["/board"].Operations[OperationType.Post]; + path = document.Paths["/board"].Operations[HttpMethod.Post]; var board = path.RequestBody.Content["application/json"].Schema; Assert.Equal("An item on the board.", board.Description); - path = document.Paths["/protected-internal-element"].Operations[OperationType.Post]; + path = document.Paths["/protected-internal-element"].Operations[HttpMethod.Post]; var element = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The unique identifier for the element.", element.Properties["name"].Description); Assert.Equal("Can find this XML comment.", element.Description); - path = document.Paths["/project-record"].Operations[OperationType.Post]; + path = document.Paths["/project-record"].Operations[HttpMethod.Post]; project = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The name of the project.", project.Properties["name"].Description); Assert.Equal("The description of the project.", project.Properties["description"].Description); - path = document.Paths["/todo-with-description"].Operations[OperationType.Post]; + path = document.Paths["/todo-with-description"].Operations[HttpMethod.Post]; todo = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The identifier of the todo.", todo.Properties["id"].Description); Assert.Equal("The name of the todo.", todo.Properties["name"].Description); Assert.Equal("Another description of the todo.", todo.Properties["description"].Description); - path = document.Paths["/type-with-examples"].Operations[OperationType.Post]; + path = document.Paths["/type-with-examples"].Operations[HttpMethod.Post]; var typeWithExamples = path.RequestBody.Content["application/json"].Schema; var booleanTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["booleanType"].Example); @@ -255,7 +254,7 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document => var uriTypeExample = Assert.IsAssignableFrom(typeWithExamples.Properties["uriType"].Example); Assert.Equal("https://example.com", uriTypeExample.GetValue()); - path = document.Paths["/user"].Operations[OperationType.Post]; + path = document.Paths["/user"].Operations[HttpMethod.Post]; var user = path.RequestBody.Content["application/json"].Schema; Assert.Equal("The unique identifier for the user.", user.Properties["id"].Description); Assert.Equal("The user's display name.", user.Properties["name"].Description); 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 1eb5f70cee6a..3d2702fbe1f1 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 @@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; + using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Any; @@ -323,9 +324,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name); if (operationParameter is not null) { - var targetOperationParameter = operationParameter is OpenApiParameterReference reference - ? reference.Target - : (OpenApiParameter)operationParameter; + var targetOperationParameter = UnwrapOpenApiParameter(operationParameter); targetOperationParameter.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { @@ -341,7 +340,12 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform requestBody.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { - foreach (var mediaType in requestBody.Content.Values) + var content = requestBody?.Content?.Values; + if (content is null) + { + continue; + } + foreach (var mediaType in content) { mediaType.Example = jsonString.Parse(); } @@ -365,6 +369,29 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform return Task.CompletedTask; } + + private static OpenApiParameter UnwrapOpenApiParameter(IOpenApiParameter sourceParameter) + { + if (sourceParameter is OpenApiParameterReference parameterReference) + { + if (parameterReference.Target is OpenApiParameter target) + { + return target; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } + else if (sourceParameter is OpenApiParameter directParameter) + { + return directParameter; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } } [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs index 2232587cb744..945ae11f52cf 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/snapshots/AdditionalTextsTests.CanHandleXmlForSchemasInAdditionalTexts#OpenApiXmlCommentSupport.generated.verified.cs @@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; + using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Any; @@ -352,9 +353,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name); if (operationParameter is not null) { - var targetOperationParameter = operationParameter is OpenApiParameterReference reference - ? reference.Target - : (OpenApiParameter)operationParameter; + var targetOperationParameter = UnwrapOpenApiParameter(operationParameter); targetOperationParameter.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { @@ -370,7 +369,12 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform requestBody.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { - foreach (var mediaType in requestBody.Content.Values) + var content = requestBody?.Content?.Values; + if (content is null) + { + continue; + } + foreach (var mediaType in content) { mediaType.Example = jsonString.Parse(); } @@ -394,6 +398,29 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform return Task.CompletedTask; } + + private static OpenApiParameter UnwrapOpenApiParameter(IOpenApiParameter sourceParameter) + { + if (sourceParameter is OpenApiParameterReference parameterReference) + { + if (parameterReference.Target is OpenApiParameter target) + { + return target; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } + else if (sourceParameter is OpenApiParameter directParameter) + { + return directParameter; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } } [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] 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 7748d372f620..391c98725057 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 @@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; + using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Any; @@ -442,9 +443,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name); if (operationParameter is not null) { - var targetOperationParameter = operationParameter is OpenApiParameterReference reference - ? reference.Target - : (OpenApiParameter)operationParameter; + var targetOperationParameter = UnwrapOpenApiParameter(operationParameter); targetOperationParameter.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { @@ -460,7 +459,12 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform requestBody.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { - foreach (var mediaType in requestBody.Content.Values) + var content = requestBody?.Content?.Values; + if (content is null) + { + continue; + } + foreach (var mediaType in content) { mediaType.Example = jsonString.Parse(); } @@ -484,6 +488,29 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform return Task.CompletedTask; } + + private static OpenApiParameter UnwrapOpenApiParameter(IOpenApiParameter sourceParameter) + { + if (sourceParameter is OpenApiParameterReference parameterReference) + { + if (parameterReference.Target is OpenApiParameter target) + { + return target; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } + else if (sourceParameter is OpenApiParameter directParameter) + { + return directParameter; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } } [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] 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 7420c2923583..2b2e1e1d01fe 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 @@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; + using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Any; @@ -327,9 +328,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name); if (operationParameter is not null) { - var targetOperationParameter = operationParameter is OpenApiParameterReference reference - ? reference.Target - : (OpenApiParameter)operationParameter; + var targetOperationParameter = UnwrapOpenApiParameter(operationParameter); targetOperationParameter.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { @@ -345,7 +344,12 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform requestBody.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { - foreach (var mediaType in requestBody.Content.Values) + var content = requestBody?.Content?.Values; + if (content is null) + { + continue; + } + foreach (var mediaType in content) { mediaType.Example = jsonString.Parse(); } @@ -369,6 +373,29 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform return Task.CompletedTask; } + + private static OpenApiParameter UnwrapOpenApiParameter(IOpenApiParameter sourceParameter) + { + if (sourceParameter is OpenApiParameterReference parameterReference) + { + if (parameterReference.Target is OpenApiParameter target) + { + return target; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } + else if (sourceParameter is OpenApiParameter directParameter) + { + return directParameter; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } } [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] 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 6336e09f5b89..3ee9ad39a98a 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 @@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; + using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Any; @@ -345,9 +346,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name); if (operationParameter is not null) { - var targetOperationParameter = operationParameter is OpenApiParameterReference reference - ? reference.Target - : (OpenApiParameter)operationParameter; + var targetOperationParameter = UnwrapOpenApiParameter(operationParameter); targetOperationParameter.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { @@ -363,7 +362,12 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform requestBody.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { - foreach (var mediaType in requestBody.Content.Values) + var content = requestBody?.Content?.Values; + if (content is null) + { + continue; + } + foreach (var mediaType in content) { mediaType.Example = jsonString.Parse(); } @@ -387,6 +391,29 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform return Task.CompletedTask; } + + private static OpenApiParameter UnwrapOpenApiParameter(IOpenApiParameter sourceParameter) + { + if (sourceParameter is OpenApiParameterReference parameterReference) + { + if (parameterReference.Target is OpenApiParameter target) + { + return target; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } + else if (sourceParameter is OpenApiParameter directParameter) + { + return directParameter; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } } [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] 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 f0179d4e5ef1..ae28560f6351 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 @@ -41,6 +41,7 @@ namespace Microsoft.AspNetCore.OpenApi.Generated using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.Extensions.DependencyInjection; using Microsoft.OpenApi.Models; + using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Any; @@ -353,9 +354,7 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name); if (operationParameter is not null) { - var targetOperationParameter = operationParameter is OpenApiParameterReference reference - ? reference.Target - : (OpenApiParameter)operationParameter; + var targetOperationParameter = UnwrapOpenApiParameter(operationParameter); targetOperationParameter.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { @@ -371,7 +370,12 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform requestBody.Description = parameterComment.Description; if (parameterComment.Example is { } jsonString) { - foreach (var mediaType in requestBody.Content.Values) + var content = requestBody?.Content?.Values; + if (content is null) + { + continue; + } + foreach (var mediaType in content) { mediaType.Example = jsonString.Parse(); } @@ -395,6 +399,29 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform return Task.CompletedTask; } + + private static OpenApiParameter UnwrapOpenApiParameter(IOpenApiParameter sourceParameter) + { + if (sourceParameter is OpenApiParameterReference parameterReference) + { + if (parameterReference.Target is OpenApiParameter target) + { + return target; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } + else if (sourceParameter is OpenApiParameter directParameter) + { + return directParameter; + } + else + { + throw new InvalidOperationException($"The input schema must be an {nameof(OpenApiParameter)} or {nameof(OpenApiParameterReference)}."); + } + } } [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.AspNetCore.OpenApi.SourceGenerators, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60", "42.42.42.42")] diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/ApiDescriptionExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/ApiDescriptionExtensionsTests.cs index cc0d4e872c9a..d016dcf0ca25 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/ApiDescriptionExtensionsTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/ApiDescriptionExtensionsTests.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Net.Http; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.OpenApi.Models; public class ApiDescriptionExtensionsTests { @@ -31,17 +31,25 @@ public void MapRelativePathToItemPath_ReturnsItemPathForApiDescription(string re Assert.Equal(expectedItemPath, itemPath); } + public static class HttpMethodTestData + { + public static IEnumerable TestCases => new List + { + new object[] { "GET", HttpMethod.Get }, + new object[] { "POST", HttpMethod.Post }, + new object[] { "PUT", HttpMethod.Put }, + new object[] { "DELETE", HttpMethod.Delete }, + new object[] { "PATCH", HttpMethod.Patch }, + new object[] { "HEAD", HttpMethod.Head }, + new object[] { "OPTIONS", HttpMethod.Options }, + new object[] { "TRACE", HttpMethod.Trace }, + new object[] { "gEt", HttpMethod.Get }, // Test case-insensitivity + }; + } + [Theory] - [InlineData("GET", OperationType.Get)] - [InlineData("POST", OperationType.Post)] - [InlineData("PUT", OperationType.Put)] - [InlineData("DELETE", OperationType.Delete)] - [InlineData("PATCH", OperationType.Patch)] - [InlineData("HEAD", OperationType.Head)] - [InlineData("OPTIONS", OperationType.Options)] - [InlineData("TRACE", OperationType.Trace)] - [InlineData("gEt", OperationType.Get)] - public void ToOperationType_ReturnsOperationTypeForApiDescription(string httpMethod, OperationType expectedOperationType) + [MemberData(nameof(HttpMethodTestData.TestCases), MemberType = typeof(HttpMethodTestData))] + public void GetHttpMethod_ReturnsHttpMethodForApiDescription(string httpMethod, HttpMethod expectedHttpMethod) { // Arrange var apiDescription = new ApiDescription @@ -50,16 +58,16 @@ public void ToOperationType_ReturnsOperationTypeForApiDescription(string httpMet }; // Act - var operationType = apiDescription.GetOperationType(); + var result = apiDescription.GetHttpMethod(); // Assert - Assert.Equal(expectedOperationType, operationType); + Assert.Equal(expectedHttpMethod, result); } [Theory] [InlineData("UNKNOWN")] [InlineData("unknown")] - public void ToOperationType_ThrowsForUnknownHttpMethod(string methodName) + public void GetHttpMethod_ThrowsForUnknownHttpMethod(string methodName) { // Arrange var apiDescription = new ApiDescription @@ -68,7 +76,7 @@ public void ToOperationType_ThrowsForUnknownHttpMethod(string methodName) }; // Act & Assert - var exception = Assert.Throws(() => apiDescription.GetOperationType()); + var exception = Assert.Throws(() => apiDescription.GetHttpMethod()); Assert.Equal($"Unsupported HTTP method: {methodName}", exception.Message); } } diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs index 59813644f53c..c3cccd690850 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Extensions/OpenApiEndpointRouteBuilderExtensionsTests.cs @@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.OpenApi; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Reader; using System.Text; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Microsoft.AspNetCore.OpenApi.Tests.csproj b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Microsoft.AspNetCore.OpenApi.Tests.csproj index 25e04bc98a6f..c6a84055d2f3 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Microsoft.AspNetCore.OpenApi.Tests.csproj +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Microsoft.AspNetCore.OpenApi.Tests.csproj @@ -13,7 +13,7 @@ - + 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 86239cbc84ee..f249e323c719 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/CreateSchemaReferenceIdTests.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.Net.Http; using System.Text.Json.Serialization.Metadata; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.InternalTesting; @@ -33,7 +34,7 @@ string createReferenceId(JsonTypeInfo jsonTypeInfo) // Assert await VerifyOpenApiDocument(builder, options, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -46,8 +47,8 @@ await VerifyOpenApiDocument(builder, options, document => item => Assert.Equal("square", item.Key) ); Assert.Collection(schema.Discriminator.Mapping, - item => Assert.Equal("#/components/schemas/MyShapeMyTriangle", item.Value), - item => Assert.Equal("#/components/schemas/MyShapeMySquare", item.Value) + item => Assert.Equal("#/components/schemas/MyShapeMyTriangle", item.Value.Reference.ReferenceV3), + item => Assert.Equal("#/components/schemas/MyShapeMySquare", item.Value.Reference.ReferenceV3) ); // Assert the schemas with the discriminator have been inserted into the components Assert.True(document.Components.Schemas.TryGetValue("MyShapeMyTriangle", out var triangleSchema)); @@ -71,7 +72,7 @@ public async Task GeneratesSchemaForPoco_WithSchemaReferenceIdCustomization() // Assert await VerifyOpenApiDocument(builder, options, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; Assert.NotNull(requestBody); @@ -120,7 +121,7 @@ public async Task GeneratesInlineSchemaForPoco_WithCustomNullId() // Assert await VerifyOpenApiDocument(builder, options, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; Assert.NotNull(requestBody); @@ -177,7 +178,7 @@ public async Task CanCallDefaultImplementationFromCustomOne() await VerifyOpenApiDocument(builder, options, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var response = operation.Responses["200"]; @@ -220,7 +221,7 @@ public async Task HandlesDuplicateSchemaReferenceIdsGeneratedByOverload() await VerifyOpenApiDocument(builder, options, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var response = operation.Responses["200"]; diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentProviderTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentProviderTests.cs index 1e66df59ff6a..16348c1d514d 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentProviderTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentProviderTests.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.OpenApi; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Readers; using static Microsoft.AspNetCore.OpenApi.Tests.OpenApiOperationGeneratorTests; public class OpenApiDocumentProviderTests : OpenApiDocumentServiceTestBase 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 bbca1d365a02..7ba82cba22ce 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 @@ -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.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -23,7 +24,7 @@ public async Task GetOpenApiOperation_CapturesSummary() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Equal(summary, operation.Summary); }); } @@ -41,7 +42,7 @@ public async Task GetOpenApiOperation_CapturesLastSummary() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Equal(summary + "1", operation.Summary); }); } @@ -59,7 +60,7 @@ public async Task GetOpenApiOperation_CapturesDescription() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Equal(description, operation.Description); }); } @@ -77,7 +78,7 @@ public async Task GetOpenApiOperation_CapturesDescriptionLastDescription() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Equal(description + "1", operation.Description); }); } @@ -94,7 +95,7 @@ public async Task GetOpenApiOperation_CapturesTags() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Collection(operation.Tags, tag => { Assert.Equal("todos", tag.Name); @@ -118,7 +119,7 @@ public async Task GetOpenApiOperation_CapturesTagsLastTags() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Collection(operation.Tags, tag => { Assert.Equal("todos", tag.Name); @@ -142,7 +143,7 @@ public async Task GetOpenApiOperation_SetsDefaultValueForTags() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Collection(document.Tags, tag => { Assert.Equal(nameof(OpenApiDocumentServiceTests), tag.Name); @@ -194,7 +195,7 @@ public async Task GetOpenApiOperation_CapturesEndpointNameAsOperationId() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Equal("GetTodos", operation.OperationId); }); @@ -212,7 +213,7 @@ public async Task GetOpenApiOperation_CapturesEndpointNameAttributeAsOperationId // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Equal("GetTodos", operation.OperationId); }); @@ -227,7 +228,7 @@ public async Task GetOpenApiOperation_CapturesRouteAttributeAsOperationId() // Assert await VerifyOpenApiDocument(action, document => { - var operation = document.Paths["/api/todos"].Operations[OperationType.Get]; + var operation = document.Paths["/api/todos"].Operations[HttpMethod.Get]; Assert.Equal("GetTodos", operation.OperationId); }); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs index 98829bf822d9..ec7d9f8cc5d6 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Parameters.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.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -23,15 +24,15 @@ public async Task GetOpenApiParameters_GeneratesParameterLocationCorrectly() // Assert await VerifyOpenApiDocument(builder, document => { - var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations[OperationType.Get].Parameters); + var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations[HttpMethod.Get].Parameters); Assert.Equal("id", pathParameter.Name); Assert.Equal(ParameterLocation.Path, pathParameter.In); - var queryParameter = Assert.Single(document.Paths["/api/todos"].Operations[OperationType.Get].Parameters); + var queryParameter = Assert.Single(document.Paths["/api/todos"].Operations[HttpMethod.Get].Parameters); Assert.Equal("id", queryParameter.Name); Assert.Equal(ParameterLocation.Query, queryParameter.In); - var headerParameter = Assert.Single(document.Paths["/api"].Operations[OperationType.Get].Parameters); + var headerParameter = Assert.Single(document.Paths["/api"].Operations[HttpMethod.Get].Parameters); Assert.Equal("X-Header", headerParameter.Name); Assert.Equal(ParameterLocation.Header, headerParameter.In); }); @@ -52,13 +53,13 @@ public async Task GetOpenApiParameters_RouteParametersAreAlwaysRequired() // Assert await VerifyOpenApiDocument(builder, document => { - var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations[OperationType.Get].Parameters!); + var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("id", pathParameter.Name); Assert.True(pathParameter.Required); - var guidParameter = Assert.Single(document.Paths["/api/todos/{guid}"].Operations[OperationType.Get].Parameters!); + var guidParameter = Assert.Single(document.Paths["/api/todos/{guid}"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("guid", guidParameter.Name); Assert.True(guidParameter.Required); - var isCompletedParameter = Assert.Single(document.Paths["/api/todos/{isCompleted}"].Operations[OperationType.Get].Parameters!); + var isCompletedParameter = Assert.Single(document.Paths["/api/todos/{isCompleted}"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("isCompleted", isCompletedParameter.Name); Assert.True(isCompletedParameter.Required); }); @@ -78,13 +79,13 @@ public async Task GetOpenApiParameters_SetsRequirednessForQueryParameters() // Assert await VerifyOpenApiDocument(builder, document => { - var queryParameter = Assert.Single(document.Paths["/api/todos"].Operations[OperationType.Get].Parameters!); + var queryParameter = Assert.Single(document.Paths["/api/todos"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("id", queryParameter.Name); Assert.True(queryParameter.Required); - var nullableQueryParameter = Assert.Single(document.Paths["/api/users"].Operations[OperationType.Get].Parameters!); + var nullableQueryParameter = Assert.Single(document.Paths["/api/users"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("id", nullableQueryParameter.Name); Assert.False(nullableQueryParameter.Required); - var defaultQueryParameter = Assert.Single(document.Paths["/api/projects"].Operations[OperationType.Get].Parameters!); + var defaultQueryParameter = Assert.Single(document.Paths["/api/projects"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("id", defaultQueryParameter.Name); Assert.False(defaultQueryParameter.Required); }); @@ -104,13 +105,13 @@ public async Task GetOpenApiParameters_SetsRequirednessForHeaderParameters() // Assert await VerifyOpenApiDocument(builder, document => { - var headerParameter = Assert.Single(document.Paths["/api/todos"].Operations[OperationType.Get].Parameters!); + var headerParameter = Assert.Single(document.Paths["/api/todos"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("X-Header", headerParameter.Name); Assert.True(headerParameter.Required); - var nullableHeaderParameter = Assert.Single(document.Paths["/api/users"].Operations[OperationType.Get].Parameters!); + var nullableHeaderParameter = Assert.Single(document.Paths["/api/users"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("X-Header", nullableHeaderParameter.Name); Assert.False(nullableHeaderParameter.Required); - var defaultHeaderParameter = Assert.Single(document.Paths["/api/projects"].Operations[OperationType.Get].Parameters!); + var defaultHeaderParameter = Assert.Single(document.Paths["/api/projects"].Operations![HttpMethod.Get].Parameters!); Assert.Equal("X-Header", defaultHeaderParameter.Name); Assert.False(defaultHeaderParameter.Required); }); @@ -133,13 +134,13 @@ public async Task GetOpenApiParameters_RouteParametersAreAlwaysRequired_Nullabil // Assert await VerifyOpenApiDocument(builder, document => { - var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations[OperationType.Get].Parameters); + var pathParameter = Assert.Single(document.Paths["/api/todos/{id}"].Operations[HttpMethod.Get].Parameters); Assert.Equal("id", pathParameter.Name); Assert.True(pathParameter.Required); - var guidParameter = Assert.Single(document.Paths["/api/todos/{guid}"].Operations[OperationType.Get].Parameters); + var guidParameter = Assert.Single(document.Paths["/api/todos/{guid}"].Operations[HttpMethod.Get].Parameters); Assert.Equal("guid", guidParameter.Name); Assert.True(guidParameter.Required); - var isCompletedParameter = Assert.Single(document.Paths["/api/todos/{isCompleted}"].Operations[OperationType.Get].Parameters); + var isCompletedParameter = Assert.Single(document.Paths["/api/todos/{isCompleted}"].Operations[HttpMethod.Get].Parameters); Assert.Equal("isCompleted", isCompletedParameter.Name); Assert.True(isCompletedParameter.Required); }); @@ -159,9 +160,9 @@ public async Task GetOpenApiRequestBody_SkipsRequestBodyParameters() // Assert await VerifyOpenApiDocument(builder, document => { - var usersOperation = document.Paths["/api/users"].Operations[OperationType.Post]; + var usersOperation = document.Paths["/api/users"].Operations[HttpMethod.Post]; Assert.Null(usersOperation.Parameters); - var todosOperation = document.Paths["/api/todos"].Operations[OperationType.Post]; + var todosOperation = document.Paths["/api/todos"].Operations[HttpMethod.Post]; Assert.Null(todosOperation.Parameters); }); } @@ -183,12 +184,12 @@ public async Task GetOpenApiParameters_SkipsDisallowedHeaders() // Assert await VerifyOpenApiDocument(builder, document => { - Assert.Null(document.Paths["/api/accept"].Operations[OperationType.Get].Parameters); - Assert.Null(document.Paths["/api/accept-lower"].Operations[OperationType.Get].Parameters); - Assert.Null(document.Paths["/api/authorization"].Operations[OperationType.Get].Parameters); - Assert.Null(document.Paths["/api/authorization-lower"].Operations[OperationType.Get].Parameters); - Assert.Null(document.Paths["/api/content-type"].Operations[OperationType.Get].Parameters); - Assert.Null(document.Paths["/api/content-type-lower"].Operations[OperationType.Get].Parameters); + Assert.Null(document.Paths["/api/accept"].Operations[HttpMethod.Get].Parameters); + Assert.Null(document.Paths["/api/accept-lower"].Operations[HttpMethod.Get].Parameters); + Assert.Null(document.Paths["/api/authorization"].Operations[HttpMethod.Get].Parameters); + Assert.Null(document.Paths["/api/authorization-lower"].Operations[HttpMethod.Get].Parameters); + Assert.Null(document.Paths["/api/content-type"].Operations[HttpMethod.Get].Parameters); + Assert.Null(document.Paths["/api/content-type-lower"].Operations[HttpMethod.Get].Parameters); }); } @@ -199,7 +200,7 @@ public async Task GetOpenApiParameters_ToleratesCustomBindingSource() await VerifyOpenApiDocument(action, document => { - var operation = document.Paths["/custom-binding"].Operations[OperationType.Get]; + var operation = document.Paths["/custom-binding"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal("model", parameter.Name); Assert.Equal(ParameterLocation.Query, parameter.In); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs index a74353f762f0..8cf7525f7d51 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Paths.cs @@ -1,9 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Routing; -using Microsoft.OpenApi.Models; public partial class OpenApiDocumentServiceTests : OpenApiDocumentServiceTestBase { @@ -27,7 +27,7 @@ await VerifyOpenApiDocument(builder, document => Assert.Collection(path.Value.Operations.OrderBy(o => o.Key), operation => { - Assert.Equal(OperationType.Get, operation.Key); + Assert.Equal(HttpMethod.Get, operation.Key); }); }, path => @@ -36,7 +36,7 @@ await VerifyOpenApiDocument(builder, document => Assert.Collection(path.Value.Operations.OrderBy(o => o.Key), operation => { - Assert.Equal(OperationType.Get, operation.Key); + Assert.Equal(HttpMethod.Get, operation.Key); }); }); }); @@ -84,15 +84,9 @@ await VerifyOpenApiDocument(builder, document => path => { Assert.Equal("/api/todos", path.Key); - Assert.Collection(path.Value.Operations.OrderBy(o => o.Key), - operation => - { - Assert.Equal(OperationType.Get, operation.Key); - }, - operation => - { - Assert.Equal(OperationType.Post, operation.Key); - }); + Assert.Equal(2, path.Value.Operations.Count); + Assert.Contains(HttpMethod.Get, path.Value.Operations); + Assert.Contains(HttpMethod.Post, path.Value.Operations); } ); }); @@ -116,23 +110,11 @@ await VerifyOpenApiDocument(builder, document => path => { Assert.Equal("/api/todos/{id}", path.Key); - Assert.Collection(path.Value.Operations.OrderBy(o => o.Key), - operation => - { - Assert.Equal(OperationType.Get, operation.Key); - }, - operation => - { - Assert.Equal(OperationType.Put, operation.Key); - }, - operation => - { - Assert.Equal(OperationType.Post, operation.Key); - }, - operation => - { - Assert.Equal(OperationType.Patch, operation.Key); - }); + Assert.Equal(4, path.Value.Operations.Count); + Assert.Contains(HttpMethod.Get, path.Value.Operations); + Assert.Contains(HttpMethod.Put, path.Value.Operations); + Assert.Contains(HttpMethod.Post, path.Value.Operations); + Assert.Contains(HttpMethod.Patch, path.Value.Operations); } ); }); @@ -155,15 +137,9 @@ await VerifyOpenApiDocument(builder, document => path => { Assert.Equal("/api/todos/{id}", path.Key); - Assert.Collection(path.Value.Operations.OrderBy(o => o.Key), - operation => - { - Assert.Equal(OperationType.Get, operation.Key); - }, - operation => - { - Assert.Equal(OperationType.Post, operation.Key); - }); + Assert.Equal(2, path.Value.Operations.Count); + Assert.Contains(HttpMethod.Get, path.Value.Operations); + Assert.Contains(HttpMethod.Post, path.Value.Operations); } ); }); 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 032720da5cc0..cd4328ef1712 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO.Pipelines; +using System.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.InternalTesting; @@ -32,7 +33,7 @@ public async Task GetRequestBody_HandlesIFormFile(bool withAttribute) await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.True(operation.RequestBody.Required); Assert.NotNull(operation.RequestBody.Content); @@ -70,18 +71,18 @@ public async Task GetRequestBody_HandlesIFormFileOptionality(bool isOptional) await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations![HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.True(operation.RequestBody.Required); - var schema = operation.RequestBody.Content["multipart/form-data"].Schema; + var schema = operation.RequestBody.Content!["multipart/form-data"].Schema; Assert.NotNull(schema); if (!isOptional) { - Assert.Contains("formFile", schema.Required); + Assert.Contains("formFile", schema.Required!); } else { - Assert.DoesNotContain("formFile", schema.Required); + Assert.Null(schema.Required); } }); } @@ -109,7 +110,7 @@ public async Task GetRequestBody_HandlesIFormFileCollection(bool withAttribute) await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.True(operation.RequestBody.Required); Assert.NotNull(operation.RequestBody.Content); @@ -148,18 +149,18 @@ public async Task GetRequestBody_HandlesIFormFileCollectionOptionality(bool isOp await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations![HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.True(operation.RequestBody.Required); - var schema = operation.RequestBody.Content["multipart/form-data"].Schema; + var schema = operation.RequestBody.Content!["multipart/form-data"].Schema; Assert.NotNull(schema); if (!isOptional) { - Assert.Contains("formFile", schema.Required); + Assert.Contains("formFile", schema.Required!); } else { - Assert.DoesNotContain("formFile", schema.Required); + Assert.Null(schema.Required); } }); } @@ -178,7 +179,7 @@ public async Task GetRequestBody_MultipleFormFileParameters() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = Assert.Single(operation.RequestBody.Content); @@ -218,7 +219,7 @@ public async Task GetRequestBody_IFormFileHandlesAcceptsMetadata() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = Assert.Single(operation.RequestBody.Content); @@ -245,7 +246,7 @@ public async Task GetRequestBody_IFormFileHandlesConsumesAttribute() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = Assert.Single(operation.RequestBody.Content); @@ -272,7 +273,7 @@ public async Task GetRequestBody_HandlesJsonBody() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.False(operation.RequestBody.Required); Assert.NotNull(operation.RequestBody.Content); @@ -304,7 +305,7 @@ public async Task GetRequestBody_HandlesJsonBodyOptionality(bool isOptional) await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations![HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.Equal(!isOptional, operation.RequestBody.Required); }); @@ -325,7 +326,7 @@ public async Task GetRequestBody_HandlesJsonBodyWithAttribute() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.False(operation.RequestBody.Required); Assert.NotNull(operation.RequestBody.Content); @@ -347,7 +348,7 @@ public async Task GetRequestBody_HandlesJsonBodyWithAcceptsMetadata() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = Assert.Single(operation.RequestBody.Content); @@ -368,7 +369,7 @@ public async Task GetRequestBody_HandlesJsonBodyWithConsumesAttribute() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = Assert.Single(operation.RequestBody.Content); @@ -389,7 +390,7 @@ public async Task GetOpenApiRequestBody_SetsNullRequestBodyWithNoParameters() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.Null(operation.RequestBody); }); } @@ -408,7 +409,7 @@ public async Task GetOpenApiRequestBody_HandlesFromFormWithPoco() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; @@ -465,7 +466,7 @@ public async Task GetOpenApiRequestBody_HandlesFromFormWithRequiredPrimitive() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; @@ -512,7 +513,7 @@ public async Task GetOpenApiRequestBody_HandlesFromFormWithPoco_MvcAction() await VerifyOpenApiDocument(action, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; @@ -567,7 +568,7 @@ public async Task GetOpenApiRequestBody_HandlesMultipleFormWithPoco() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Post]; + var operation = paths.Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; @@ -633,7 +634,7 @@ public async Task GetOpenApiRequestBody_HandlesMultipleFormWithPoco_MvcAction() await VerifyOpenApiDocument(action, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; @@ -701,7 +702,7 @@ public async Task GetOpenApiRequestBody_HandlesFromFormWithPocoSingleProp_MvcAct await VerifyOpenApiDocument(action, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; @@ -741,7 +742,7 @@ public async Task GetOpenApiRequestBody_HandlesFromFormWithNullableProperties_Mv await VerifyOpenApiDocument(action, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; @@ -783,7 +784,7 @@ public async Task GetOpenApiRequestBody_HandlesFormModelWithFile_MvcAction() await VerifyOpenApiDocument(action, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; var item = Assert.Single(content.Values); @@ -825,7 +826,7 @@ public async Task GetOpenApiRequestBody_HandlesFormModelWithFile() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; foreach (var item in content.Values) @@ -866,7 +867,7 @@ public async Task GetOpenApiRequestBody_HandlesFormWithPrimitives_MvcAction(stri await VerifyOpenApiDocument(action, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; var item = Assert.Single(content.Values); @@ -925,7 +926,7 @@ public async Task GetOpenApiRequestBody_HandlesFormWithPrimitives(Delegate reque await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; foreach (var item in content.Values) @@ -956,7 +957,7 @@ public async Task GetOpenApiRequestBody_HandlesFormWithMultipleMixedTypes() await VerifyOpenApiDocument(builder, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; foreach (var item in content.Values) @@ -1020,7 +1021,7 @@ public async Task GetOpenApiRequestBody_HandlesFormWithMultipleMixedTypes_MvcAct await VerifyOpenApiDocument(action, document => { var paths = Assert.Single(document.Paths.Values); - var operation = paths.Operations[OperationType.Get]; + var operation = paths.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody.Content); var content = operation.RequestBody.Content; foreach (var item in content.Values) @@ -1092,7 +1093,7 @@ await VerifyOpenApiDocument(builder, document => { foreach (var path in document.Paths) { - var operation = path.Value.Operations[OperationType.Get]; + var operation = path.Value.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody.Content); var content = Assert.Single(operation.RequestBody.Content); Assert.Equal("application/octet-stream", content.Key); @@ -1117,7 +1118,7 @@ public async Task GetOpenApiRequestBody_HandlesStreamAndPipeReader_MvcAction() static void VerifyDocument(OpenApiDocument document) { var path = Assert.Single(document.Paths); - var operation = path.Value.Operations[OperationType.Get]; + var operation = path.Value.Operations[HttpMethod.Get]; Assert.NotNull(operation.RequestBody.Content); var content = Assert.Single(operation.RequestBody.Content); Assert.Equal("application/octet-stream", content.Key); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiGeneratorTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiGeneratorTests.cs index 45deea36b9d4..bac1c7869c66 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiGeneratorTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiGeneratorTests.cs @@ -85,7 +85,7 @@ static void AssertCustomRequestFormat(OpenApiOperation operation) { Assert.NotNull(operation.Parameters); Assert.Empty(operation.Parameters); - var content = operation.RequestBody.Content.Keys.FirstOrDefault(); + var content = operation.RequestBody.Content!.Keys.FirstOrDefault(); Assert.Equal("application/custom", content); } @@ -105,9 +105,9 @@ public void AddsMultipleRequestFormatsFromMetadata() Assert.NotNull(operation.Parameters); Assert.Empty(operation.Parameters); - var content = operation.RequestBody.Content; + var content = operation.RequestBody.Content!; Assert.Equal(2, content.Count); - Assert.Equal(new[] { "application/custom0", "application/custom1" }, content.Keys); + Assert.Equal(["application/custom0", "application/custom1"], content.Keys); } [Fact] @@ -137,8 +137,8 @@ public void AddsMultipleRequestFormatsFromMetadataWithRequiredBodyParameter() var request = operation.RequestBody; Assert.NotNull(request); - Assert.Equal("application/custom0", request.Content.First().Key); - Assert.Equal("application/custom1", request.Content.Last().Key); + Assert.Equal("application/custom0", request.Content!.First().Key); + Assert.Equal("application/custom1", request.Content!.Last().Key); Assert.True(request.Required); Assert.NotNull(operation.Parameters); Assert.Empty(operation.Parameters); @@ -335,7 +335,7 @@ static void AssertPathParameter(OpenApiOperation operation) { var param = Assert.Single(operation.Parameters); Assert.Equal(ParameterLocation.Path, param.In); - Assert.Empty(param.Content); + Assert.Null(param.Content); } AssertPathParameter(GetOpenApiOperation((int foo) => { }, "/{foo}")); @@ -349,7 +349,7 @@ static void AssertPathParameter(OpenApiOperation operation) { var param = Assert.Single(operation.Parameters); Assert.Equal(ParameterLocation.Path, param.In); - Assert.Empty(param.Content); + Assert.Null(param.Content); } AssertPathParameter(GetOpenApiOperation((TryParseStringRecord foo) => { }, pattern: "/{foo}")); } @@ -361,7 +361,7 @@ static void AssertPathParameter(OpenApiOperation operation) { var param = Assert.Single(operation.Parameters); Assert.Equal(ParameterLocation.Path, param.In); - Assert.Empty(param.Content); + Assert.Null(param.Content); } AssertPathParameter(GetOpenApiOperation((int? foo) => { }, "/{foo}")); @@ -375,7 +375,7 @@ static void AssertPathParameter(OpenApiOperation operation) { var param = Assert.Single(operation.Parameters); Assert.Equal(ParameterLocation.Path, param.In); - Assert.Empty(param.Content); + Assert.Null(param.Content); } AssertPathParameter(GetOpenApiOperation((TryParseStringRecordStruct foo) => { }, pattern: "/{foo}")); } @@ -387,7 +387,7 @@ static void AssertQueryParameter(OpenApiOperation operation, JsonSchemaType type { var param = Assert.Single(operation.Parameters); Assert.Equal(ParameterLocation.Query, param.In); - Assert.Empty(param.Content); + Assert.Null(param.Content); } AssertQueryParameter(GetOpenApiOperation((int foo) => { }, "/"), JsonSchemaType.Integer); @@ -406,7 +406,7 @@ public void AddsFromHeaderParameterAsHeader() var param = Assert.Single(operation.Parameters); Assert.Equal(ParameterLocation.Header, param.In); - Assert.Empty(param.Content); + Assert.Null(param.Content); } [Fact] @@ -430,7 +430,7 @@ static void AssertBodyParameter(OpenApiOperation operation, string expectedName, { var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("application/json", content.Key); Assert.NotNull(operation.Parameters); Assert.Empty(operation.Parameters); @@ -453,17 +453,17 @@ public void AddsMultipleParameters() Assert.Equal("foo", fooParam.Name); Assert.Equal(ParameterLocation.Path, fooParam.In); Assert.True(fooParam.Required); - Assert.Empty(fooParam.Content); + Assert.Null(fooParam.Content); var barParam = operation.Parameters[1]; Assert.Equal("bar", barParam.Name); Assert.Equal(ParameterLocation.Query, barParam.In); Assert.True(barParam.Required); - Assert.Empty(barParam.Content); + Assert.Null(barParam.Content); var fromBodyParam = operation.RequestBody; Assert.NotNull(fromBodyParam); - var fromBodyContent = Assert.Single(fromBodyParam.Content); + var fromBodyContent = Assert.Single(fromBodyParam.Content!); Assert.Equal("application/json", fromBodyContent.Key); Assert.True(fromBodyParam.Required); } @@ -481,14 +481,14 @@ static void AssertParameters(OpenApiOperation operation, string capturedName = " Assert.Equal(capturedName, param.Name); Assert.Equal(ParameterLocation.Path, param.In); Assert.True(param.Required); - Assert.Empty(param.Content); + Assert.Null(param.Content); }, param => { Assert.Equal("Bar", param.Name); Assert.Equal(ParameterLocation.Query, param.In); Assert.True(param.Required); - Assert.Empty(param.Content); + Assert.Null(param.Content); } ); } @@ -513,13 +513,13 @@ public void TestParameterIsRequired() Assert.Equal("foo", fooParam.Name); Assert.Equal(ParameterLocation.Path, fooParam.In); Assert.True(fooParam.Required); - Assert.Empty(fooParam.Content); + Assert.Null(fooParam.Content); var barParam = operation.Parameters[1]; Assert.Equal("bar", barParam.Name); Assert.Equal(ParameterLocation.Query, barParam.In); Assert.False(barParam.Required); - Assert.Empty(barParam.Content); + Assert.Null(barParam.Content); } [Fact] @@ -675,7 +675,7 @@ public void HandleAcceptsMetadataWithNoParams() Assert.NotNull(operation.Parameters); Assert.Empty(operation.Parameters); Assert.Collection( - requestBody.Content, + requestBody.Content!, parameter => { Assert.Equal("application/json", parameter.Key); @@ -699,7 +699,7 @@ public void HandleAcceptsMetadataWithTypeParameter() // Assert var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.False(requestBody.Required); Assert.NotNull(operation.Parameters); Assert.Empty(operation.Parameters); @@ -716,7 +716,7 @@ public void HandleDefaultIAcceptsMetadataForRequiredBodyParameter() // Assert var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("application/json", content.Key); Assert.True(requestBody.Required); Assert.NotNull(operation.Parameters); @@ -732,7 +732,7 @@ public void HandleDefaultIAcceptsMetadataForOptionalBodyParameter() // Assert var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("application/json", content.Key); Assert.False(requestBody.Required); Assert.NotNull(operation.Parameters); @@ -748,7 +748,7 @@ public void HandleIAcceptsMetadataWithConsumesAttributeAndInferredOptionalFromBo // Assert var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("application/xml", content.Key); Assert.False(requestBody.Required); Assert.NotNull(operation.Parameters); @@ -764,7 +764,7 @@ public void HandleDefaultIAcceptsMetadataForRequiredFormFileParameter() // Assert var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("multipart/form-data", content.Key); Assert.True(requestBody.Required); Assert.NotNull(operation.Parameters); @@ -780,7 +780,7 @@ public void HandleDefaultIAcceptsMetadataForOptionalFormFileParameter() // Assert var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("multipart/form-data", content.Key); Assert.False(requestBody.Required); Assert.NotNull(operation.Parameters); @@ -796,7 +796,7 @@ public void AddsMultipartFormDataRequestFormatWhenFormFileSpecified() // Assert var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("multipart/form-data", content.Key); Assert.True(requestBody.Required); Assert.NotNull(operation.Parameters); @@ -811,7 +811,7 @@ public void HasMultipleRequestFormatsWhenFormFileSpecifiedWithConsumesAttribute( var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = requestBody.Content; + var content = requestBody.Content!; Assert.Equal(2, content.Count); @@ -834,12 +834,12 @@ public void TestIsRequiredFromFormFile() Assert.NotNull(operation1.RequestBody); var fromFileParam0 = operation0.RequestBody; - var fromFileParam0ContentType = Assert.Single(fromFileParam0.Content.Values); + var fromFileParam0ContentType = Assert.Single(fromFileParam0.Content!.Values); Assert.Equal("multipart/form-data", fromFileParam0.Content.Keys.SingleOrDefault()); Assert.True(fromFileParam0.Required); var fromFileParam1 = operation1.RequestBody; - var fromFileParam1ContentType = Assert.Single(fromFileParam1.Content.Values); + var fromFileParam1ContentType = Assert.Single(fromFileParam1.Content!.Values); Assert.Equal("multipart/form-data", fromFileParam1.Content.Keys.SingleOrDefault()); Assert.False(fromFileParam1.Required); } @@ -851,7 +851,7 @@ static void AssertFormFileParameter(OpenApiOperation operation, JsonSchemaType e { var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("multipart/form-data", content.Key); Assert.NotNull(operation.Parameters); Assert.Empty(operation.Parameters); @@ -875,7 +875,7 @@ static void AssertFormFileCollection(Delegate handler, string expectedName) // Assert var requestBody = operation.RequestBody; Assert.NotNull(requestBody); - var content = Assert.Single(requestBody.Content); + var content = Assert.Single(requestBody.Content!); Assert.Equal("multipart/form-data", content.Key); Assert.True(requestBody.Required); Assert.NotNull(operation.Parameters); 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 829f906f4b41..cf0b7b018902 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,6 +3,8 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Net.Http; using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; @@ -70,7 +72,7 @@ public async Task GetOpenApiParameters_HandlesRouteParameterWithPrimitiveType(De // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/{id}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{id}"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(schemaType, parameter.Schema.Type); Assert.Equal(schemaFormat, parameter.Schema.Format); @@ -98,7 +100,7 @@ public async Task GetOpenApiParameters_HandlesRouteParameterWithParsableType(Del // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/{id}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{id}"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(schemaType, parameter.Schema.Type); Assert.Equal(schemaFormat, parameter.Schema.Format); @@ -141,12 +143,12 @@ public async Task GetOpenApiParameters_HandlesRouteParameterWithRouteConstraint( await VerifyOpenApiDocument(builder, document => { var path = Assert.Single(document.Paths); - var operation = path.Value.Operations[OperationType.Get]; + var operation = path.Value.Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(type, parameter.Schema.Type); Assert.Equal(format, parameter.Schema.Format); - Assert.Equal(minimum, parameter.Schema.Minimum); - Assert.Equal(maximum, parameter.Schema.Maximum); + Assert.Equal(minimum?.ToString(CultureInfo.InvariantCulture), parameter.Schema.Minimum); + Assert.Equal(maximum?.ToString(CultureInfo.InvariantCulture), parameter.Schema.Maximum); Assert.Equal(minLength, parameter.Schema.MinLength); Assert.Equal(maxLength, parameter.Schema.MaxLength); Assert.Equal(pattern, parameter.Schema.Pattern); @@ -190,10 +192,10 @@ public async Task GetOpenApiParameters_HandlesRouteParametersWithDefaultValue(De // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/{id}"].Operations[OperationType.Post]; + var operation = document.Paths!["/api/{id}"].Operations![HttpMethod.Post]; var parameter = Assert.Single(operation.Parameters ?? []); - var openApiDefault = parameter.Schema.Default; - assert(openApiDefault); + var openApiDefault = parameter.Schema!.Default; + assert(openApiDefault!); }); } #nullable restore @@ -211,10 +213,10 @@ public async Task GetOpenApiParameters_HandlesEnumParameterWithoutConverter() // consumed as integer await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(JsonSchemaType.Integer, parameter.Schema.Type); - Assert.Empty(parameter.Schema.Enum); + Assert.Null(parameter.Schema.Enum); }); } @@ -231,7 +233,7 @@ public async Task GetOpenApiParameters_HandlesEnumParameterWithConverter() // are serialized with the `enum` value in the schema. await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Null(parameter.Schema.Type); Assert.Collection(parameter.Schema.Enum, @@ -262,7 +264,7 @@ public async Task GetOpenApiParameters_HandlesRouteParameterFromAsParameters() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/{id}/{date}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{id}/{date}"].Operations[HttpMethod.Get]; Assert.Collection(operation.Parameters, parameter => { Assert.Equal("id", parameter.Name); @@ -287,7 +289,7 @@ public async Task GetOpenApiParameters_HandlesRouteParametersWithMvcModelBinding // Assert await VerifyOpenApiDocument(action, document => { - var operation = document.Paths["/api/{id}/{date}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{id}/{date}"].Operations[HttpMethod.Get]; Assert.Collection(operation.Parameters, parameter => { Assert.Equal("Id", parameter.Name); @@ -312,7 +314,7 @@ public async Task GetOpenApiParameters_HandlesRouteParametersWithValidationsInMv // Assert await VerifyOpenApiDocument(action, document => { - var operation = document.Paths["/api/{id}/{name}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{id}/{name}"].Operations[HttpMethod.Get]; Assert.Collection(operation.Parameters, parameter => { Assert.Equal("Id", parameter.Name); @@ -335,7 +337,9 @@ await VerifyOpenApiDocument(action, document => [([MaxLength(5)] int[] ids) => {}, (OpenApiSchema schema) => Assert.Equal(5, schema.MaxItems)], [([MinLength(2)] int[] id) => {}, (OpenApiSchema schema) => Assert.Equal(2, schema.MinItems)], [([Length(4, 8)] int[] id) => {}, (OpenApiSchema schema) => { Assert.Equal(4, schema.MinItems); Assert.Equal(8, schema.MaxItems); }], - [([Range(4, 8)]int id) => {}, (OpenApiSchema schema) => { Assert.Equal(4, schema.Minimum); Assert.Equal(8, schema.Maximum); }], + [([Range(4, 8)]int id) => {}, (OpenApiSchema schema) => { Assert.Equal("4", schema.Minimum); Assert.Equal("8", schema.Maximum); }], + [([Range(1234, 5678)]int id) => {}, (OpenApiSchema schema) => { Assert.Equal("1234", schema.Minimum); Assert.Equal("5678", schema.Maximum); }], + [([Range(1234.56, 7891.1)] decimal id) => {}, (OpenApiSchema schema) => { Assert.Equal("1234.56", schema.Minimum); Assert.Equal("7891.1", schema.Maximum); }], [([Range(typeof(DateTime), "2024-02-01", "2024-02-031")] DateTime id) => {}, (OpenApiSchema schema) => { Assert.Null(schema.Minimum); Assert.Null(schema.Maximum); }], [([StringLength(10)] string name) => {}, (OpenApiSchema schema) => { Assert.Equal(10, schema.MaxLength); Assert.Equal(0, schema.MinLength); }], [([StringLength(10, MinimumLength = 5)] string name) => {}, (OpenApiSchema schema) => { Assert.Equal(10, schema.MaxLength); Assert.Equal(5, schema.MinLength); }], @@ -358,7 +362,7 @@ public async Task GetOpenApiParameters_HandlesRouteParameterWithValidationAttrib // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/{id}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{id}"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); var schema = Assert.IsType(parameter.Schema); verifySchema(schema); @@ -367,11 +371,11 @@ await VerifyOpenApiDocument(builder, document => public static object[][] RouteParametersWithRangeAttributes => [ - [([Range(4, 8)] int id) => {}, (OpenApiSchema schema) => { Assert.Equal(4, schema.Minimum); Assert.Equal(8, schema.Maximum); }], - [([Range(int.MinValue, int.MaxValue)] int id) => {}, (OpenApiSchema schema) => { Assert.Equal(int.MinValue, schema.Minimum); Assert.Equal(int.MaxValue, schema.Maximum); }], - [([Range(0, double.MaxValue)] double id) => {}, (OpenApiSchema schema) => { Assert.Equal(0, schema.Minimum); Assert.Null(schema.Maximum); }], - [([Range(typeof(double), "0", "1.79769313486232E+308")] double id) => {}, (OpenApiSchema schema) => { Assert.Equal(0, schema.Minimum); Assert.Null(schema.Maximum); }], - [([Range(typeof(long), "-9223372036854775808", "9223372036854775807")] long id) => {}, (OpenApiSchema schema) => { Assert.Equal(long.MinValue, schema.Minimum); Assert.Equal(long.MaxValue, schema.Maximum); }], + [([Range(4, 8)] int id) => {}, (OpenApiSchema schema) => { Assert.Equal("4", schema.Minimum); Assert.Equal("8", schema.Maximum); }], + [([Range(int.MinValue, int.MaxValue)] int id) => {}, (OpenApiSchema schema) => { Assert.Equal(int.MinValue.ToString(CultureInfo.InvariantCulture), schema.Minimum); Assert.Equal(int.MaxValue.ToString(CultureInfo.InvariantCulture), schema.Maximum); }], + [([Range(0, double.MaxValue)] double id) => {}, (OpenApiSchema schema) => { Assert.Equal("0", schema.Minimum); Assert.Null(schema.Maximum); }], + [([Range(typeof(double), "0", "1.79769313486232E+308")] double id) => {}, (OpenApiSchema schema) => { Assert.Equal("0", schema.Minimum); Assert.Null(schema.Maximum); }], + [([Range(typeof(long), "-9223372036854775808", "9223372036854775807")] long id) => {}, (OpenApiSchema schema) => { Assert.Equal(long.MinValue.ToString(CultureInfo.InvariantCulture), schema.Minimum); Assert.Equal(long.MaxValue.ToString(CultureInfo.InvariantCulture), schema.Maximum); }], [([Range(typeof(DateTime), "2024-02-01", "2024-02-031")] DateTime id) => {}, (OpenApiSchema schema) => { Assert.Null(schema.Minimum); Assert.Null(schema.Maximum); }], ]; @@ -388,7 +392,7 @@ public async Task GetOpenApiParameters_HandlesRouteParametersWithRangeAttributes // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/{id}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{id}"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); var schema = Assert.IsType(parameter.Schema); verifySchema(schema); @@ -398,9 +402,9 @@ await VerifyOpenApiDocument(builder, document => public static object[][] RouteParametersWithRangeAttributes_CultureInfo => [ [([Range(typeof(DateTime), "2024-02-01", "2024-02-031")] DateTime id) => {}, (OpenApiSchema schema) => { Assert.Null(schema.Minimum); Assert.Null(schema.Maximum); }], - [([Range(typeof(decimal), "1,99", "3,99")] decimal id) => {}, (OpenApiSchema schema) => { Assert.Equal(1.99m, schema.Minimum); Assert.Equal(3.99m, schema.Maximum); }], - [([Range(typeof(decimal), "1,99", "3,99", ParseLimitsInInvariantCulture = true)] decimal id) => {}, (OpenApiSchema schema) => { Assert.Equal(199, schema.Minimum); Assert.Equal(399, schema.Maximum); }], - [([Range(1000, 2000)] int id) => {}, (OpenApiSchema schema) => { Assert.Equal(1000, schema.Minimum); Assert.Equal(2000, schema.Maximum); }] + [([Range(typeof(decimal), "1,99", "3,99")] decimal id) => {}, (OpenApiSchema schema) => { Assert.Equal("1.99", schema.Minimum); Assert.Equal("3.99", schema.Maximum); }], + [([Range(typeof(decimal), "1,99", "3,99", ParseLimitsInInvariantCulture = true)] decimal id) => {}, (OpenApiSchema schema) => { Assert.Equal("199", schema.Minimum); Assert.Equal("399", schema.Maximum); }], + [([Range(1000, 2000)] int id) => {}, (OpenApiSchema schema) => { Assert.Equal("1000", schema.Minimum); Assert.Equal("2000", schema.Maximum); }] ]; [Theory] @@ -417,7 +421,7 @@ public async Task GetOpenApiParameters_HandlesRouteParametersWithRangeAttributes // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api/{id}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{id}"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); var schema = Assert.IsType(parameter.Schema); verifySchema(schema); @@ -442,7 +446,7 @@ await VerifyOpenApiDocument(builder, document => { foreach (var path in document.Paths.Values) { - var operation = path.Operations[OperationType.Get]; + var operation = path.Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.True(parameter.Required); } @@ -475,7 +479,7 @@ public async Task GetOpenApiParameters_HandlesArrayBasedTypes(Delegate requestHa // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); // Array items can be serialized to nullable values when the element // type is nullable. For example, array-of-ints?ints=1&ints=2&ints=&ints=4 @@ -501,7 +505,7 @@ public async Task GetOpenApiParameters_HandlesParametersWithDescriptionAttribute // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal("The ID of the entity", parameter.Description); }); @@ -549,7 +553,7 @@ public async Task SupportsParametersWithTypeConverter() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); Assert.NotNull(operation.RequestBody.Content["application/json"]); @@ -587,7 +591,7 @@ public async Task SupportsParameterWithDynamicType() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.NotNull(operation.RequestBody.Content); Assert.NotNull(operation.RequestBody.Content["application/json"]); @@ -618,7 +622,7 @@ public async Task SupportsParameterWithEnumType(bool useAction) static void AssertOpenApiDocument(OpenApiDocument document) { - var operation = document.Paths["/api/with-enum"].Operations[OperationType.Get]; + var operation = document.Paths["/api/with-enum"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); var response = Assert.Single(operation.Responses).Value.Content["application/json"].Schema; Assert.Equal(((OpenApiSchemaReference)parameter.Schema).Reference.Id, ((OpenApiSchemaReference)response).Reference.Id); @@ -651,7 +655,7 @@ public async Task SupportsMvcActionWithAmbientRouteParameter() // Assert await VerifyOpenApiDocument(action, document => { - var operation = document.Paths["/api/with-ambient-route-param/{versionId}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/with-ambient-route-param/{versionId}"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(JsonSchemaType.String, parameter.Schema.Type); }); @@ -684,7 +688,7 @@ public async Task SupportsRouteParameterWithCustomTryParse(bool useAction) static void AssertOpenApiDocument(OpenApiDocument document) { // Parameter is a plain-old string when it comes from the route or query - var operation = document.Paths["/api/{student}"].Operations[OperationType.Get]; + var operation = document.Paths["/api/{student}"].Operations[HttpMethod.Get]; var parameter = Assert.Single(operation.Parameters); Assert.Equal(JsonSchemaType.String, parameter.Schema.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 8002c603029c..a222c5e3083a 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 @@ -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.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; @@ -20,7 +21,7 @@ public async Task HandlesPolymorphicTypeWithMappingsAndStringDiscriminator() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -33,8 +34,8 @@ await VerifyOpenApiDocument(builder, document => item => Assert.Equal("square", item.Key) ); Assert.Collection(schema.Discriminator.Mapping, - item => Assert.Equal("#/components/schemas/ShapeTriangle", item.Value), - item => Assert.Equal("#/components/schemas/ShapeSquare", item.Value) + item => Assert.Equal("#/components/schemas/ShapeTriangle", item.Value.Reference.ReferenceV3), + item => Assert.Equal("#/components/schemas/ShapeSquare", item.Value.Reference.ReferenceV3) ); // Assert the schemas with the discriminator have been inserted into the components Assert.True(document.Components.Schemas.TryGetValue("ShapeTriangle", out var triangleSchema)); @@ -57,7 +58,7 @@ public async Task HandlesPolymorphicTypeWithMappingsAndIntegerDiscriminator() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -71,9 +72,9 @@ await VerifyOpenApiDocument(builder, document => item => Assert.Equal("2", item.Key) ); Assert.Collection(schema.Discriminator.Mapping, - item => Assert.Equal("#/components/schemas/WeatherForecastBaseWeatherForecastWithCity", item.Value), - item => Assert.Equal("#/components/schemas/WeatherForecastBaseWeatherForecastWithTimeSeries", item.Value), - item => Assert.Equal("#/components/schemas/WeatherForecastBaseWeatherForecastWithLocalNews", item.Value) + item => Assert.Equal("#/components/schemas/WeatherForecastBaseWeatherForecastWithCity", item.Value.Reference.ReferenceV3), + item => Assert.Equal("#/components/schemas/WeatherForecastBaseWeatherForecastWithTimeSeries", item.Value.Reference.ReferenceV3), + item => Assert.Equal("#/components/schemas/WeatherForecastBaseWeatherForecastWithLocalNews", item.Value.Reference.ReferenceV3) ); // Assert schema with discriminator = 0 has been inserted into the components Assert.True(document.Components.Schemas.TryGetValue("WeatherForecastBaseWeatherForecastWithCity", out var citySchema)); @@ -102,7 +103,7 @@ public async Task HandlesPolymorphicTypesWithCustomPropertyName() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -115,8 +116,8 @@ await VerifyOpenApiDocument(builder, document => item => Assert.Equal("teacher", item.Key) ); Assert.Collection(schema.Discriminator.Mapping, - item => Assert.Equal("#/components/schemas/PersonStudent", item.Value), - item => Assert.Equal("#/components/schemas/PersonTeacher", item.Value) + item => Assert.Equal("#/components/schemas/PersonStudent", item.Value.Reference.ReferenceV3), + item => Assert.Equal("#/components/schemas/PersonTeacher", item.Value.Reference.ReferenceV3) ); // Assert schema with discriminator = 0 has been inserted into the components Assert.True(document.Components.Schemas.TryGetValue("PersonStudent", out var citySchema)); @@ -141,7 +142,7 @@ public async Task HandlesPolymorphicTypesWithNonAbstractBaseClassWithNoDiscrimin // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -180,7 +181,7 @@ public async Task HandlesPolymorphicTypesWithNonAbstractBaseClassAndDiscriminato // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -193,9 +194,9 @@ await VerifyOpenApiDocument(builder, document => item => Assert.Equal("pet", item.Key) ); Assert.Collection(schema.Discriminator.Mapping, - item => Assert.Equal("#/components/schemas/PetCat", item.Value), - item => Assert.Equal("#/components/schemas/PetDog", item.Value), - item => Assert.Equal("#/components/schemas/PetPet", item.Value) + item => Assert.Equal("#/components/schemas/PetCat", item.Value.Reference.ReferenceV3), + item => Assert.Equal("#/components/schemas/PetDog", item.Value.Reference.ReferenceV3), + item => Assert.Equal("#/components/schemas/PetPet", item.Value.Reference.ReferenceV3) ); // OpenAPI requires that derived types in a polymorphic schema _always_ have a discriminator // property associated with them. STJ permits the discriminator to be omitted from the @@ -233,7 +234,7 @@ public async Task HandlesPolymorphicTypesWithNoExplicitDiscriminators() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -268,7 +269,7 @@ public async Task HandlesPolymorphicTypesWithSelfReference() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -281,8 +282,8 @@ await VerifyOpenApiDocument(builder, document => item => Assert.Equal("employee", item.Key) ); Assert.Collection(schema.Discriminator.Mapping, - item => Assert.Equal("#/components/schemas/EmployeeManager", item.Value), - item => Assert.Equal("#/components/schemas/EmployeeEmployee", item.Value) + item => Assert.Equal("#/components/schemas/EmployeeManager", item.Value.Reference.ReferenceV3), + item => Assert.Equal("#/components/schemas/EmployeeEmployee", item.Value.Reference.ReferenceV3) ); // Assert that anyOf schemas use the correct reference IDs. Assert.Collection(schema.AnyOf, 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 e62eff7d94df..9d2576b8cbf7 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 @@ -4,6 +4,7 @@ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.IO.Pipelines; +using System.Net.Http; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; using Microsoft.AspNetCore.Builder; @@ -27,7 +28,7 @@ public async Task GetOpenApiRequestBody_GeneratesSchemaForPoco() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; Assert.NotNull(requestBody); @@ -84,7 +85,7 @@ public async Task GetOpenApiRequestBody_GeneratesSchemaForPoco_WithValidationAtt // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; Assert.NotNull(requestBody); @@ -97,8 +98,8 @@ await VerifyOpenApiDocument(builder, document => { Assert.Equal("id", property.Key); Assert.Equal(JsonSchemaType.Integer, property.Value.Type); - Assert.Equal(1, property.Value.Minimum); - Assert.Equal(100, property.Value.Maximum); + Assert.Equal("1", property.Value.Minimum); + Assert.Equal("100", property.Value.Maximum); Assert.Null(property.Value.Default); }, property => @@ -164,7 +165,7 @@ await VerifyOpenApiDocument(builder, document => static OpenApiRequestBody GetRequestBodyForPath(OpenApiDocument document, string path) { - var operation = document.Paths[path].Operations[OperationType.Post]; + var operation = document.Paths[path].Operations[HttpMethod.Post]; return operation.RequestBody as OpenApiRequestBody; } } @@ -181,7 +182,7 @@ public async Task GetOpenApiRequestBody_RespectsRequiredAttributeOnBodyPropertie // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/required-properties"].Operations[OperationType.Post]; + var operation = document.Paths["/required-properties"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var content = Assert.Single(requestBody.Content); var schema = content.Value.Schema; @@ -208,7 +209,7 @@ await VerifyOpenApiDocument(builder, document => { foreach (var path in paths) { - var operation = document.Paths[$"/{path}"].Operations[OperationType.Post]; + var operation = document.Paths[$"/{path}"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var effectiveSchema = requestBody.Content["application/octet-stream"].Schema; @@ -231,7 +232,7 @@ public async Task GetOpenApiRequestBody_GeneratesSchemaForFilesInRecursiveType() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths[$"/proposal"].Operations[OperationType.Post]; + var operation = document.Paths[$"/proposal"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var schema = requestBody.Content["application/json"].Schema; Assert.Equal("Proposal", ((OpenApiSchemaReference)schema).Reference.Id); @@ -264,9 +265,9 @@ public async Task GetOpenApiRequestBody_GeneratesSchemaForListOf() // Assert await VerifyOpenApiDocument(builder, document => { - var enumerableTodo = document.Paths["/enumerable-todo"].Operations[OperationType.Post]; - var arrayTodo = document.Paths["/array-todo"].Operations[OperationType.Post]; - var arrayParsable = document.Paths["/array-parsable"].Operations[OperationType.Get]; + var enumerableTodo = document.Paths["/enumerable-todo"].Operations[HttpMethod.Post]; + var arrayTodo = document.Paths["/array-todo"].Operations[HttpMethod.Post]; + var arrayParsable = document.Paths["/array-parsable"].Operations[HttpMethod.Get]; Assert.NotNull(enumerableTodo.RequestBody); Assert.NotNull(arrayTodo.RequestBody); @@ -325,13 +326,13 @@ public async Task GetOpenApiRequestBody_HandlesPolymorphicRequestWithoutDiscrimi // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); var schema = mediaType.Schema; Assert.Equal(JsonSchemaType.Object, schema.Type); - Assert.Empty(schema.AnyOf); + Assert.Null(schema.AnyOf); Assert.Collection(schema.Properties, property => { @@ -365,7 +366,7 @@ public async Task GetOpenApiRequestBody_HandlesDescriptionAttributeOnProperties( // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); var requestBody = operation.RequestBody.Content; Assert.True(requestBody.TryGetValue("application/json", out var mediaType)); @@ -413,7 +414,7 @@ public async Task GetOpenApiRequestBody_HandlesDescriptionAttributeOnParameter() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.NotNull(operation.RequestBody); Assert.Equal("The todo item to create.", operation.RequestBody.Description); }); @@ -431,7 +432,7 @@ public async Task GetOpenApiRequestBody_HandlesNullableProperties() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var content = Assert.Single(requestBody.Content); var schema = content.Value.Schema; @@ -478,7 +479,7 @@ public async Task SupportsNestedTypes() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var content = Assert.Single(requestBody.Content); Assert.Equal("NestedType", ((OpenApiSchemaReference)content.Value.Schema).Reference.Id); @@ -523,7 +524,7 @@ public async Task SupportsNestedTypes_WithNoAttributeProvider() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var content = Assert.Single(requestBody.Content); Assert.Equal("NestedType", ((OpenApiSchemaReference)content.Value.Schema).Reference.Id); @@ -588,7 +589,7 @@ public async Task ExcludesNullabilityInFormParameters() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; Assert.Collection(operation.RequestBody.Content["application/x-www-form-urlencoded"].Schema.AllOf, schema => { @@ -620,7 +621,7 @@ public async Task SupportsClassWithJsonUnmappedMemberHandlingDisallowed() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var content = Assert.Single(requestBody.Content); var schema = content.Value.Schema; @@ -646,7 +647,7 @@ public async Task SupportsClassWithJsonUnmappedMemberHandlingSkipped() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var content = Assert.Single(requestBody.Content); var schema = content.Value.Schema; @@ -684,7 +685,7 @@ public async Task SupportsTypesWithSelfReferencedProperties() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody; var content = Assert.Single(requestBody.Content); var schema = content.Value.Schema; 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 630e419eff1d..a59bfb28d1d5 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.Net.Http; using System.Text.Json.Nodes; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -43,7 +44,7 @@ public async Task GetOpenApiResponse_HandlesResponsesWithPrimitiveTypes(Delegate // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue(contentType, out var mediaType)); @@ -64,7 +65,7 @@ public async Task GetOpenApiResponse_HandlesPocoResponse() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); @@ -108,7 +109,7 @@ public async Task GetOpenApiResponse_GeneratesSchemaForPoco_WithValidationAttrib // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Get]; + var operation = document.Paths["/"].Operations[HttpMethod.Get]; var response = operation.Responses["200"]; Assert.NotNull(response); @@ -122,8 +123,8 @@ await VerifyOpenApiDocument(builder, document => { Assert.Equal("id", property.Key); Assert.Equal(JsonSchemaType.Integer, property.Value.Type); - Assert.Equal(1, property.Value.Minimum); - Assert.Equal(100, property.Value.Maximum); + Assert.Equal("1", property.Value.Minimum); + Assert.Equal("100", property.Value.Maximum); }, property => { @@ -178,7 +179,7 @@ public async Task GetOpenApiResponse_HandlesNullablePocoResponse() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); @@ -222,7 +223,7 @@ public async Task GetOpenApiResponse_RespectsRequiredAttributeOnBodyProperties() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/required-properties"].Operations[OperationType.Post]; + var operation = document.Paths["/required-properties"].Operations[HttpMethod.Post]; var response = operation.Responses["200"]; var content = Assert.Single(response.Content); var schema = content.Value.Schema; @@ -244,7 +245,7 @@ public async Task GetOpenApiResponse_HandlesInheritedTypeResponse() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); @@ -298,7 +299,7 @@ public async Task GetOpenApiResponse_HandlesGenericResponse() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); @@ -366,13 +367,13 @@ public async Task GetOpenApiResponse_HandlesPolymorphicResponseWithoutDiscrimina // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); var schema = mediaType.Schema; Assert.Equal(JsonSchemaType.Object, schema.Type); - Assert.Empty(schema.AnyOf); + Assert.Null(schema.AnyOf); Assert.Collection(schema.Properties, property => { @@ -406,7 +407,7 @@ public async Task GetOpenApiResponse_HandlesResultOfAnonymousType() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Get]; + var operation = document.Paths["/api"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); @@ -468,7 +469,7 @@ public async Task GetOpenApiResponse_HandlesListOf() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Get]; + var operation = document.Paths["/"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); @@ -517,7 +518,7 @@ public async Task GetOpenApiResponse_HandlesGenericType() // OpenAPI 3.1.0. await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Get]; + var operation = document.Paths["/"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); @@ -597,7 +598,7 @@ public async Task GetOpenApiResponse_HandlesValidationProblem() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Get]; + var operation = document.Paths["/"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/problem+json", out var mediaType)); @@ -655,7 +656,7 @@ public async Task GetOpenApiResponse_SupportsObjectTypeProperty() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Get]; + var operation = document.Paths["/"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); @@ -685,7 +686,7 @@ public async Task GetOpenApiResponse_SupportsProducesWithProducesResponseTypeOnC await VerifyOpenApiDocument(actionDescriptor, document => { - var operation = document.Paths["/"].Operations[OperationType.Get]; + var operation = document.Paths["/"].Operations[HttpMethod.Get]; var responses = Assert.Single(operation.Responses); var response = responses.Value; Assert.True(response.Content.TryGetValue("application/json", out var mediaType)); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/CustomSchemaTransformerTests.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/CustomSchemaTransformerTests.cs index 44f41f3bd0bf..9a98cd71ec0f 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/CustomSchemaTransformerTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/CustomSchemaTransformerTests.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.Net.Http; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.AspNetCore.Builder; @@ -32,7 +33,7 @@ public async Task CustomSchemaTransformer_CanInsertSchemaIntoDocumentFromOperati operation.Responses["500"] = new OpenApiResponse { Description = "Error", - Content = + Content = new() { ["application/problem+json"] = new OpenApiMediaType { @@ -99,7 +100,7 @@ public async Task GetOrCreateSchema_AddsSchemasForMultipleResponseTypes() operation.Responses["200"] = new OpenApiResponse { Description = "Success", - Content = + Content = new() { ["application/json"] = new OpenApiMediaType { @@ -111,7 +112,7 @@ public async Task GetOrCreateSchema_AddsSchemasForMultipleResponseTypes() operation.Responses["400"] = new OpenApiResponse { Description = "Bad Request", - Content = + Content = new() { ["application/problem+json"] = new OpenApiMediaType { @@ -157,6 +158,7 @@ public async Task GetOrCreateSchema_CanBeUsedInSchemaTransformer() context.Document.AddComponent("TriangleExample", exampleSchema); // Add a reference to the example in the shape schema + schema.Extensions ??= []; schema.Extensions["x-example-component"] = new OpenApiAny("#/components/schemas/TriangleExample"); schema.Description = "A shape with an example reference"; } @@ -231,7 +233,7 @@ await VerifyOpenApiDocument(builder, options, (document) => Assert.Equal(JsonSchemaType.Integer, limitSchema.Type); // Operation should now have 4 parameters (2 original + 2 custom) - var operation = document.Paths["/items"].Operations[OperationType.Post]; + var operation = document.Paths["/items"].Operations[HttpMethod.Post]; Assert.Equal(4, operation.Parameters.Count); }); } @@ -261,7 +263,7 @@ public async Task GetOrCreateSchema_WorksWithNestedTypes() ["200"] = new OpenApiResponse { Description = "Success", - Content = + Content = new() { ["application/json"] = new OpenApiMediaType { @@ -272,7 +274,8 @@ public async Task GetOrCreateSchema_WorksWithNestedTypes() } }; - path.Operations[OperationType.Get] = operation; + path.Operations ??= []; + path.Operations[HttpMethod.Get] = operation; document.Paths["/nested"] = path; }); @@ -318,6 +321,7 @@ public async Task GetOrCreateSchemaAsync_AppliesOtherSchemaTransformers() { if (context.JsonTypeInfo.Type == typeof(Product)) { + schema.Required ??= []; schema.Required.Add("name"); schema.Required.Add("price"); transformerApplied = true; @@ -345,7 +349,7 @@ public async Task GetOrCreateSchemaAsync_AppliesOtherSchemaTransformers() operation.Responses["200"] = new OpenApiResponse { Description = "A product", - Content = + Content = new() { ["application/json"] = new OpenApiMediaType { @@ -413,7 +417,7 @@ public async Task GetOrCreateSchemaAsync_HandlesConcurrentRequests() operation.Responses["200"] = new OpenApiResponse { Description = "Concurrent schema generation test", - Content = + Content = new() { ["application/json"] = new OpenApiMediaType { @@ -466,7 +470,7 @@ public async Task GetOrCreateSchemaAsync_RespectsJsonSerializerOptions() operation.Responses["200"] = new OpenApiResponse { Description = "User with custom JSON options", - Content = + Content = new() { ["application/json"] = new OpenApiMediaType { 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 3370ff749a32..2fa5cce2989d 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 @@ -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.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.InternalTesting; @@ -27,11 +28,11 @@ public async Task IdenticalParameterTypesAreStoredWithSchemaReference() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var parameter = operation.RequestBody.Content["multipart/form-data"]; var schema = parameter.Schema; - var operation2 = document.Paths["/api-2"].Operations[OperationType.Post]; + var operation2 = document.Paths["/api-2"].Operations[HttpMethod.Post]; var parameter2 = operation2.RequestBody.Content["multipart/form-data"]; var schema2 = parameter2.Schema; @@ -81,7 +82,7 @@ public async Task TodoInRequestBodyAndResponseUsesSchemaReference() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody.Content["application/json"]; var requestBodySchema = requestBody.Schema; @@ -136,11 +137,11 @@ public async Task SameTypeInDictionaryAndListTypesUsesReferenceIds() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody.Content["application/json"]; var requestBodySchema = requestBody.Schema; - var operation2 = document.Paths["/api-2"].Operations[OperationType.Post]; + var operation2 = document.Paths["/api-2"].Operations[HttpMethod.Post]; var requestBody2 = operation2.RequestBody.Content["application/json"]; var requestBodySchema2 = requestBody2.Schema; @@ -193,11 +194,11 @@ public async Task SameTypeInAllOfReferenceGetsHandledCorrectly() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody.Content["multipart/form-data"]; var requestBodySchema = requestBody.Schema; - var operation2 = document.Paths["/api-2"].Operations[OperationType.Post]; + var operation2 = document.Paths["/api-2"].Operations[HttpMethod.Post]; var requestBody2 = operation2.RequestBody.Content["multipart/form-data"]; var requestBodySchema2 = requestBody2.Schema; @@ -229,11 +230,11 @@ public async Task DifferentTypesWithSameSchemaMapToSameReferenceId() // Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.RequestBody.Content["application/json"]; var requestBodySchema = requestBody.Schema; - var operation2 = document.Paths["/api-2"].Operations[OperationType.Post]; + var operation2 = document.Paths["/api-2"].Operations[HttpMethod.Post]; var requestBody2 = operation2.RequestBody.Content["application/json"]; var requestBodySchema2 = requestBody2.Schema; @@ -285,6 +286,7 @@ public async Task TypeModifiedWithSchemaTransformerMapsToDifferentReferenceId() { if (context.JsonTypeInfo.Type == typeof(Todo) && context.ParameterDescription is not null) { + schema.Extensions ??= []; schema.Extensions["x-my-extension"] = new OpenApiAny(context.ParameterDescription.Name); } return Task.CompletedTask; @@ -293,9 +295,9 @@ public async Task TypeModifiedWithSchemaTransformerMapsToDifferentReferenceId() await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; // Schemas are distinct because of applied transformer so no reference is used. Assert.NotEqual(((OpenApiSchemaReference)requestSchema).Reference.Id, ((OpenApiSchemaReference)responseSchema).Reference.Id); @@ -321,11 +323,11 @@ public static async Task ProducesStableSchemaRefsForListOf() static void VerifyDocument(OpenApiDocument document) { - var operation = document.Paths["/api"].Operations[OperationType.Post]; + var operation = document.Paths["/api"].Operations[HttpMethod.Post]; var requestBody = operation.Responses["200"].Content["application/json"]; var requestBodySchema = requestBody.Schema; - var operation2 = document.Paths["/api-2"].Operations[OperationType.Post]; + var operation2 = document.Paths["/api-2"].Operations[HttpMethod.Post]; var requestBody2 = operation2.Responses["200"].Content["application/json"]; var requestBodySchema2 = requestBody2.Schema; @@ -378,7 +380,7 @@ public async Task SupportsRefMappingInDeeplyNestedTypes() await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -442,7 +444,7 @@ public async Task SupportsDeeplyNestedSchemaWithConfiguredMaxDepth() // Act & Assert await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -468,7 +470,7 @@ public async Task SupportsNestedSchemasWithSelfReference() await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -545,7 +547,7 @@ public async Task SupportsListNestedSchemasWithSelfReference() await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -614,7 +616,7 @@ public async Task SupportsMultiplePropertiesWithSameType() await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -641,16 +643,16 @@ public async Task SupportsListOfNestedSchemasWithSelfReference() await VerifyOpenApiDocument(builder, document => { - var listOperation = document.Paths["/list"].Operations[OperationType.Post]; + var listOperation = document.Paths["/list"].Operations[HttpMethod.Post]; var listRequestSchema = listOperation.RequestBody.Content["application/json"].Schema; - var arrayOperation = document.Paths["/array"].Operations[OperationType.Post]; + var arrayOperation = document.Paths["/array"].Operations[HttpMethod.Post]; var arrayRequestSchema = arrayOperation.RequestBody.Content["application/json"].Schema; - var dictionaryOperation = document.Paths["/dictionary"].Operations[OperationType.Post]; + var dictionaryOperation = document.Paths["/dictionary"].Operations[HttpMethod.Post]; var dictionaryRequestSchema = dictionaryOperation.RequestBody.Content["application/json"].Schema; - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -691,7 +693,7 @@ public async Task ResolvesListBasedReferencesCorrectly() await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -738,7 +740,7 @@ public async Task SupportsListOfClassInSelfReferentialSchema() await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/"].Operations[OperationType.Post]; + var operation = document.Paths["/"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -776,7 +778,7 @@ public async Task UsesSameReferenceForSameTypeInDifferentLocations() await VerifyOpenApiDocument(builder, document => { - var operation = document.Paths["/parent-object"].Operations[OperationType.Post]; + var operation = document.Paths["/parent-object"].Operations[HttpMethod.Post]; var requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for top-level @@ -789,7 +791,7 @@ await VerifyOpenApiDocument(builder, document => var childSchema = requestSchema.Properties["children"].Items; Assert.Equal("ParentObject", ((OpenApiSchemaReference)childSchema.Properties["parent"]).Reference.Id); - operation = document.Paths["/list"].Operations[OperationType.Post]; + operation = document.Paths["/list"].Operations[HttpMethod.Post]; requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for items in the list definition @@ -800,7 +802,7 @@ await VerifyOpenApiDocument(builder, document => childSchema = parentSchema.Properties["children"].Items; Assert.Equal("ParentObject", ((OpenApiSchemaReference)childSchema.Properties["parent"]).Reference.Id); - operation = document.Paths["/dictionary"].Operations[OperationType.Post]; + operation = document.Paths["/dictionary"].Operations[HttpMethod.Post]; requestSchema = operation.RequestBody.Content["application/json"].Schema; // Assert $ref used for items in the dictionary definition 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 9cec9411254e..6cc281bf6b9a 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Transformers/SchemaTransformerTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Globalization; +using System.Net.Http; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.InternalTesting; using Microsoft.AspNetCore.Mvc; @@ -154,12 +155,14 @@ public async Task SchemaTransformer_RunsInRegisteredOrder() var options = new OpenApiOptions(); options.AddSchemaTransformer((schema, context, cancellationToken) => { + schema.Extensions ??= []; schema.Extensions["x-my-extension"] = new OpenApiAny("1"); schema.Format = "1"; return Task.CompletedTask; }); options.AddSchemaTransformer((schema, context, cancellationToken) => { + schema.Extensions ??= []; Assert.Equal("1", ((OpenApiAny)schema.Extensions["x-my-extension"]).Node.GetValue()); schema.Extensions["x-my-extension"] = new OpenApiAny("2"); return Task.CompletedTask; @@ -186,6 +189,7 @@ public async Task SchemaTransformer_OnTypeModifiesBothRequestAndResponse() { if (context.JsonTypeInfo.Type == typeof(Todo)) { + schema.Extensions ??= []; schema.Extensions["x-my-extension"] = new OpenApiAny("1"); } return Task.CompletedTask; @@ -194,10 +198,10 @@ public async Task SchemaTransformer_OnTypeModifiesBothRequestAndResponse() await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal("1", ((OpenApiAny)requestSchema.Extensions["x-my-extension"]).Node.GetValue()); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal("1", ((OpenApiAny)responseSchema.Extensions["x-my-extension"]).Node.GetValue()); }); @@ -224,10 +228,10 @@ public async Task SchemaTransformer_WithDescriptionOnlyModifiesParameter() await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal("todo", ((OpenApiAny)requestSchema.Extensions["x-my-extension"]).Node.GetValue()); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.False(responseSchema.Extensions.TryGetValue("x-my-extension", out var _)); }); @@ -247,10 +251,10 @@ public async Task SchemaTransformer_SupportsActivatedTransformers() await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal("1", ((OpenApiAny)requestSchema.Extensions["x-my-extension"]).Node.GetValue()); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal("1", ((OpenApiAny)responseSchema.Extensions["x-my-extension"]).Node.GetValue()); }); @@ -270,10 +274,10 @@ public async Task SchemaTransformer_SupportsInstanceTransformers() await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal("1", ((OpenApiAny)requestSchema.Extensions["x-my-extension"]).Node.GetValue()); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal("1", ((OpenApiAny)responseSchema.Extensions["x-my-extension"]).Node.GetValue()); }); @@ -297,21 +301,21 @@ public async Task SchemaTransformer_SupportsActivatedTransformerWithSingletonDep await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; value = ((OpenApiAny)requestSchema.Extensions["x-my-extension"]).Node.GetValue(); Assert.Equal(Dependency.InstantiationCount.ToString(CultureInfo.InvariantCulture), value); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal(value, ((OpenApiAny)responseSchema.Extensions["x-my-extension"]).Node.GetValue()); }); await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal(value, ((OpenApiAny)requestSchema.Extensions["x-my-extension"]).Node.GetValue()); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal(value, ((OpenApiAny)responseSchema.Extensions["x-my-extension"]).Node.GetValue()); }); @@ -333,20 +337,20 @@ public async Task SchemaTransformer_SupportsActivatedTransformerWithTransientDep await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.True(requestSchema.Extensions.ContainsKey("x-my-extension")); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.True(responseSchema.Extensions.ContainsKey("x-my-extension")); }); await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.True(requestSchema.Extensions.ContainsKey("x-my-extension")); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.True(responseSchema.Extensions.ContainsKey("x-my-extension")); }); @@ -370,10 +374,10 @@ public async Task SchemaTransformer_SupportsDisposableActivatedTransformer() await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal("Schema Description", requestSchema.Description); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal("Schema Description", responseSchema.Description); }); @@ -396,10 +400,10 @@ public async Task SchemaTransformer_SupportsAsyncDisposableActivatedTransformer( await VerifyOpenApiDocument(builder, options, document => { var path = Assert.Single(document.Paths.Values); - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal("Schema Description", requestSchema.Description); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal("Schema Description", responseSchema.Description); }); @@ -429,12 +433,12 @@ await VerifyOpenApiDocument(builder, options, document => { // Assert that parameter schema has been update var path = Assert.Single(document.Paths.Values); - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Parameters[0].Schema; Assert.Equal("modified-number-format", responseSchema.Format); // Assert that property in request body schema has been updated - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal("modified-number-format", requestSchema.Properties["id"].Format); }); @@ -464,19 +468,19 @@ await VerifyOpenApiDocument(builder, options, document => { // Assert that the schema represent list elements has been modified var path = document.Paths["/list"]; - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal("modified-number-format", responseSchema.Items.Format); // Assert that top-level schema associated with the standalone integer has been updated path = document.Paths["/single"]; - getOperation = path.Operations[OperationType.Get]; + getOperation = path.Operations[HttpMethod.Get]; responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal("modified-number-format", responseSchema.Format); // Assert that the schema represent dictionary values has been modified path = document.Paths["/dictionary"]; - getOperation = path.Operations[OperationType.Get]; + getOperation = path.Operations[HttpMethod.Get]; responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.Equal("modified-number-format", responseSchema.AdditionalProperties.Format); }); @@ -495,6 +499,7 @@ public async Task SchemaTransformer_CanModifyPolymorphicChildSchemas() { if (context.JsonTypeInfo.Type == typeof(Triangle)) { + schema.Extensions ??= []; schema.Extensions["x-my-extension"] = new OpenApiAny("this-is-a-triangle"); } return Task.CompletedTask; @@ -504,14 +509,14 @@ await VerifyOpenApiDocument(builder, options, document => { // Assert that the polymorphic sub-type `Triangle` has been updated var path = document.Paths["/shape"]; - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/json"].Schema; 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 path = document.Paths["/triangle"]; - postOperation = path.Operations[OperationType.Post]; + postOperation = path.Operations[HttpMethod.Post]; requestSchema = postOperation.RequestBody.Content["application/json"].Schema; Assert.Equal("this-is-a-triangle", ((OpenApiAny)requestSchema.Extensions["x-my-extension"]).Node.GetValue()); }); @@ -539,14 +544,14 @@ await VerifyOpenApiDocument(builder, options, document => { // Assert that the `id` property in the `Todo` within the array has been updated var path = document.Paths["/list-of-todo"]; - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; var itemSchema = responseSchema.Items; Assert.Equal("modified-number-format", itemSchema.Properties["id"].Format); // Assert that the integer type within the list has been updated var otherPath = document.Paths["/list-of-int"]; - var otherGetOperation = otherPath.Operations[OperationType.Get]; + var otherGetOperation = otherPath.Operations[HttpMethod.Get]; var otherResponseSchema = otherGetOperation.Responses["200"].Content["application/json"].Schema; var otherItemSchema = otherResponseSchema.Items; Assert.Equal("modified-number-format", otherItemSchema.Format); @@ -563,6 +568,7 @@ public async Task SchemaTransformer_CanModifyListOfPolymorphicTypes() var options = new OpenApiOptions(); options.AddSchemaTransformer((schema, context, cancellationToken) => { + schema.Extensions ??= []; if (context.JsonTypeInfo.Type == typeof(Triangle)) { schema.Extensions["x-my-extension"] = new OpenApiAny("this-is-a-triangle"); @@ -578,7 +584,7 @@ await VerifyOpenApiDocument(builder, options, document => { // Assert that the `Triangle` type within the list has been updated var path = document.Paths["/list"]; - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; var itemSchema = responseSchema.Items; var triangleSubschema = Assert.Single(itemSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeTriangle")); @@ -604,6 +610,7 @@ public async Task SchemaTransformer_CanModifyPolymorphicTypesInProperties() var options = new OpenApiOptions(); options.AddSchemaTransformer((schema, context, cancellationToken) => { + schema.Extensions ??= []; if (context.JsonTypeInfo.Type == typeof(Triangle)) { schema.Extensions["x-my-extension"] = new OpenApiAny("this-is-a-triangle"); @@ -619,7 +626,7 @@ await VerifyOpenApiDocument(builder, options, document => { // Assert that the `Triangle` type within the list has been updated var path = document.Paths["/list"]; - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; var someShapeSchema = responseSchema.Properties["someShape"]; var triangleSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeTriangle")); @@ -645,6 +652,7 @@ public async Task SchemaTransformer_CanModifyDeeplyNestedPolymorphicTypesInPrope var options = new OpenApiOptions(); options.AddSchemaTransformer((schema, context, cancellationToken) => { + schema.Extensions ??= []; if (context.JsonTypeInfo.Type == typeof(Triangle)) { schema.Extensions["x-my-extension"] = new OpenApiAny("this-is-a-triangle"); @@ -660,7 +668,7 @@ await VerifyOpenApiDocument(builder, options, document => { // Assert that the `Triangle` type within the list has been updated var path = document.Paths["/list"]; - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; var someShapeSchema = responseSchema.Items.Properties["someShape"]; var triangleSubschema = Assert.Single(someShapeSchema.AnyOf.Where(s => ((OpenApiSchemaReference)s).Reference.Id == "ShapeTriangle")); @@ -700,7 +708,7 @@ public async Task SchemaTransformers_CanModifyMultipleFormParameters() await VerifyOpenApiDocument(builder, options, document => { var path = document.Paths["/todo"]; - var postOperation = path.Operations[OperationType.Post]; + var postOperation = path.Operations[HttpMethod.Post]; var requestSchema = postOperation.RequestBody.Content["application/x-www-form-urlencoded"].Schema; Assert.Equal(2, requestSchema.AllOf.Count); var todoSchema = requestSchema.AllOf[0]; @@ -733,6 +741,7 @@ public async Task SchemaTransformers_CanImplementNotSchemaIndependently() }); UseNotSchemaTransformer(options, (schema, context, cancellationToken) => { + schema.Extensions ??= []; schema.Extensions["modified-by-not-schema-transformer"] = new OpenApiAny(true); return Task.CompletedTask; }); @@ -741,12 +750,12 @@ public async Task SchemaTransformers_CanImplementNotSchemaIndependently() await VerifyOpenApiDocument(builder, options, document => { var path = document.Paths["/todo"]; - var getOperation = path.Operations[OperationType.Get]; + var getOperation = path.Operations[HttpMethod.Get]; var responseSchema = getOperation.Responses["200"].Content["application/json"].Schema; Assert.True(((OpenApiAny)responseSchema.Not.Extensions["modified-by-not-schema-transformer"]).Node.GetValue()); var shapePath = document.Paths["/shape"]; - var shapeOperation = shapePath.Operations[OperationType.Post]; + var shapeOperation = shapePath.Operations[HttpMethod.Post]; var shapeRequestSchema = shapeOperation.RequestBody.Content["application/json"].Schema; 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()); @@ -758,7 +767,7 @@ static void UseNotSchemaTransformer(OpenApiOptions options, Func - +