diff --git a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/OperationFilters/ParamToParameterFilter.cs b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/OperationFilters/ParamToParameterFilter.cs index fd6a032..cabb471 100644 --- a/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/OperationFilters/ParamToParameterFilter.cs +++ b/src/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration/OperationFilters/ParamToParameterFilter.cs @@ -64,6 +64,7 @@ public IList Apply( { var inValue = paramElement.Attribute(KnownXmlStrings.In)?.Value.Trim(); var name = paramElement.Attribute(KnownXmlStrings.Name)?.Value.Trim(); + var mediaType = paramElement.Attribute(KnownXmlStrings.Type)?.Value ?? ""; if (settings.RemoveRoslynDuplicateStringFromParamName) { @@ -145,10 +146,20 @@ public IList Apply( In = parameterLocation, Description = description, Required = parameterLocation == ParameterLocation.Path || Convert.ToBoolean(isRequired), - Schema = schema, Examples = examples.Any() ? examples : null }; + // If media type is specified add parameter as content for complex serialization + if (!string.IsNullOrEmpty(mediaType)) { + openApiParameter.Content = new Dictionary + { + [mediaType] = new OpenApiMediaType { Schema = schema } + }; + } + else { + openApiParameter.Schema = schema; + } + operation.Parameters.Add(openApiParameter); } } diff --git a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis/Controllers/SampleV7Controller.cs b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis/Controllers/SampleV7Controller.cs new file mode 100644 index 0000000..f768cf1 --- /dev/null +++ b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis/Controllers/SampleV7Controller.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See License.txt in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using System.Threading.Tasks; +using System.Web.Http; +using Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.Contracts; + +namespace Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis.Controllers +{ + /// + /// Sample v7 controller. + /// + public class SampleV7Controller : ApiController + { + /// + /// Sample get 1 + /// + /// Sample V7 + /// GET + /// https://myapi.sample.com/V7/samples/{id}?queryString={queryString} + /// Query param 1 with no media type + /// Query param as application/json content + /// The object id + /// Sample object retrieved + /// Bad request + [HttpGet] + [Route("V7/samples/{id}")] + public async Task SampleGet1(string id, string queryString = null) { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis.csproj b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis.csproj index a08e411..6d5238c 100644 --- a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis.csproj +++ b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis.csproj @@ -118,6 +118,7 @@ + Global.asax diff --git a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.csproj b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.csproj index d8d66db..67f348a 100644 --- a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.csproj +++ b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.csproj @@ -44,6 +44,12 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/Input/AnnotationInQueryParamMediaType.xml b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/Input/AnnotationInQueryParamMediaType.xml new file mode 100644 index 0000000..c4496bd --- /dev/null +++ b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/Input/AnnotationInQueryParamMediaType.xml @@ -0,0 +1,89 @@ + + + + Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis + + + + + Responsible for route configuration + + + + + Registers routes + + Route collection + + + + Web config. + + + Test security + bearer + JWT + + + Test security + + https://example.com/api/oauth/dialog + https://example.com/api/oauth/dialog + + Example flow description + + + + + Test security + https://example.com/api/oauth/dialog + + Scope 1 description + + + Scope 2 description + + + + + + Register the configuration, services, and routes. + + HTTP Configuration + + + + Sample v7 controller. + + + + + Sample get 1 + + Sample V7 + GET + https://myapi.sample.com/V7/samples/{id}?queryString={queryString} + Query param 1 with no media type + + Query param as application/json content + + The object id + + Sample object retrieved + + + Bad request + + + + + Web API Application. + + + + + Start application. + + + + diff --git a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/OpenApiDocumentGeneratorTest.cs b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/OpenApiDocumentGeneratorTest.cs index f352eaf..ebfa7f4 100644 --- a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/OpenApiDocumentGeneratorTest.cs +++ b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/OpenApiDocumentGeneratorTest.cs @@ -1094,6 +1094,32 @@ public static IEnumerable GetTestCasesForValidDocumentationShouldRetur OutputDirectory, "AnnotationWithRuntimeSerialization.Json") }; + + // Valid XML document with object type in param tags and set with media type application/json + yield return new object[] + { + "Object Type in Param Tags", + new List + { + Path.Combine(InputDirectory, "AnnotationInQueryParamMediaType.xml"), + Path.Combine(InputDirectory, + "Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.Contracts.xml") + }, + new List + { + Path.Combine( + InputDirectory, + "Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis.dll"), + Path.Combine( + InputDirectory, + "Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.Contracts.dll") + }, + "1.0.0", + 1, + Path.Combine( + OutputDirectory, + "AnnotationInQueryParamMediaType.Json") + }; } [Theory] diff --git a/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/Output/AnnotationInQueryParamMediaType.json b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/Output/AnnotationInQueryParamMediaType.json new file mode 100644 index 0000000..9498d9b --- /dev/null +++ b/test/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests/OpenApiDocumentGeneratorTests/Output/AnnotationInQueryParamMediaType.json @@ -0,0 +1,161 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.SampleApis", + "version": "1.0.0" + }, + "servers": [ + { + "url": "https://myapi.sample.com" + } + ], + "paths": { + "/V7/samples/{id}": { + "get": { + "tags": [ + "Sample V7" + ], + "summary": "Sample get 1", + "operationId": "getV7SamplesById", + "parameters": [ + { + "name": "queryString", + "in": "query", + "description": "Query param 1 with no media type", + "schema": { + "type": "string" + } + }, + { + "name": "sampleObjectInQuery2", + "in": "query", + "description": "Query param as application/json content", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.Contracts.SampleObject1" + } + } + } + }, + { + "name": "id", + "in": "path", + "description": "The object id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Sample object retrieved", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.Contracts.SampleObject1" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Microsoft.OpenApi.CSharpAnnotations.DocumentGeneration.Tests.Contracts.SampleObject1": { + "required": [ + "samplePropertyString3", + "samplePropertyString4", + "samplePropertyEnum" + ], + "type": "object", + "properties": { + "samplePropertyBool": { + "type": "boolean", + "description": "Gets or sets the sample property bool" + }, + "samplePropertyStringInt": { + "type": "integer", + "description": "Gets or sets the sample property int", + "format": "int32" + }, + "samplePropertyString1": { + "type": "string", + "description": "Gets or sets the sample property string 1" + }, + "samplePropertyString2": { + "type": "string", + "description": "Gets or sets the sample property string 2" + }, + "samplePropertyString3": { + "type": "string", + "description": "Gets or sets the sample property string 3" + }, + "samplePropertyString4": { + "type": "string", + "description": "Gets or sets the sample property string 4" + }, + "samplePropertyEnum": { + "enum": [ + "SampleEnumValue1", + "SampleEnumValue2" + ], + "type": "string", + "description": "Gets or sets the sample property enum" + } + } + } + }, + "securitySchemes": { + "http-bearer": { + "type": "http", + "description": "Test security", + "scheme": "bearer", + "bearerFormat": "JWT" + }, + "oauth": { + "type": "oauth2", + "description": "Test security", + "flows": { + "implicit": { + "authorizationUrl": "https://example.com/api/oauth/dialog", + "refreshUrl": "https://example.com/api/oauth/dialog", + "scopes": { + "scope1": "Example flow description" + } + } + } + }, + "openIdConnect": { + "type": "openIdConnect", + "description": "Test security", + "openIdConnectUrl": "https://example.com/api/oauth/dialog" + } + } + }, + "security": [ + { + "http-bearer": [], + "oauth": [ + "scope1" + ], + "openIdConnect": [ + "scope1", + "scope2" + ] + } + ] +} \ No newline at end of file