-
Notifications
You must be signed in to change notification settings - Fork 280
Description
Hi,
I have a pre-existing API that is exposed as OpenAPI using Swashbuckle for ASP.NET Core. I have a bunch of vendor extensions that get added to the API definition via Swashbuckle's operation filters, schema filters etc.
When migrating to Swashbuckle v5 (and therefore OpenAPI.NET v.1.1.4) and serializing to V2 format (for backward compatibility with clients expecting OpenAPI/Swagger v2 format), I get duplicate keys in my JSON.
I've been able to get a fairly small repro of the problem. Consider the following OpenApiDocument which is generated by Swashbuckle. Note that the parameters in the operation have a vendor extension called my-extension and the OpenApiSchemas that hang off of them also have the same extension called my-extension:
public static OpenApiDocument DuplicateExtensions = new OpenApiDocument
{
Info = new OpenApiInfo
{
Version = "1.0.0",
Title = "Swagger Petstore (Simple)",
Description = "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
},
Servers = new List<OpenApiServer>
{
new OpenApiServer
{
Url = "http://petstore.swagger.io/api"
}
},
Paths = new OpenApiPaths
{
["/add/{operand1}/{operand2}"] = new OpenApiPathItem
{
Operations = new Dictionary<OperationType, OpenApiOperation>
{
[OperationType.Get] = new OpenApiOperation
{
OperationId = "addByOperand1AndByOperand2",
Parameters = new List<OpenApiParameter>
{
new OpenApiParameter
{
Name = "operand1",
In = ParameterLocation.Path,
Description = "The first operand",
Required = true,
Schema = new OpenApiSchema
{
Type = "integer",
Extensions = new Dictionary<string, IOpenApiExtension>
{
["my-extension"] = new Any.OpenApiInteger(4),
}
},
Extensions = new Dictionary<string, IOpenApiExtension>
{
["my-extension"] = new Any.OpenApiInteger(4),
}
},
new OpenApiParameter
{
Name = "operand2",
In = ParameterLocation.Path,
Description = "The second operand",
Required = true,
Schema = new OpenApiSchema
{
Type = "integer",
Extensions = new Dictionary<string, IOpenApiExtension>
{
["my-extension"] = new Any.OpenApiInteger(4),
}
},
Extensions = new Dictionary<string, IOpenApiExtension>
{
["my-extension"] = new Any.OpenApiInteger(4),
}
},
},
Responses = new OpenApiResponses
{
["200"] = new OpenApiResponse
{
Description = "pet response",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/json"] = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "array",
Items = PetSchema
}
},
}
}
}
}
}
}
}
};
When this is serialized to V2 format, my-extension appears twice on each parameter. I believe this is down to how the V2 format "flattens" part of the structure:
{
"swagger": "2.0",
"info": {
"title": "Swagger Petstore (Simple)",
"description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
"version": "1.0.0"
},
"host": "petstore.swagger.io",
"basePath": "/api",
"schemes": [
"http"
],
"paths": {
"/add/{operand1}/{operand2}": {
"get": {
"operationId": "addByOperand1AndByOperand2",
"produces": [
"application/json"
],
"parameters": [{
"in": "path",
"name": "operand1",
"description": "The first operand",
"required": true,
"type": "integer",
"my-extension": 4,
"my-extension": 4
}, {
"in": "path",
"name": "operand2",
"description": "The second operand",
"required": true,
"type": "integer",
"my-extension": 4,
"my-extension": 4
}
],
"responses": {
"200": {
"description": "pet response",
"schema": {
"type": "array",
"items": {
"required": [
"id",
"name"
],
"type": "object",
"properties": {
"id": {
"format": "int64",
"type": "integer"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
When serializing the same input to V3 format everything is fine as there is no flattening in the new structure, so the extension appears at both the parameter and the schema level:
{
"openapi": "3.0.1",
"info": {
"title": "Swagger Petstore (Simple)",
"description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification",
"version": "1.0.0"
},
"servers": [{
"url": "http://petstore.swagger.io/api"
}
],
"paths": {
"/add/{operand1}/{operand2}": {
"get": {
"operationId": "addByOperand1AndByOperand2",
"parameters": [{
"name": "operand1",
"in": "path",
"description": "The first operand",
"required": true,
"schema": {
"type": "integer",
"my-extension": 4
},
"my-extension": 4
}, {
"name": "operand2",
"in": "path",
"description": "The second operand",
"required": true,
"schema": {
"type": "integer",
"my-extension": 4
},
"my-extension": 4
}
],
"responses": {
"200": {
"description": "pet response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"required": [
"id",
"name"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
The presence of duplicate keys breaks SwaggerUI.
I have a proposed fix, which I will send a PR for shortly.
Thank you,
Mark