question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

SwaggerDiscriminator Attribute: discriminator field moved from paths to components

See original GitHub issue

After updating from 5.6.3 to 6.x (tested both 6.1.0 and 6.0.7) when using the SwaggerDiscriminator attribute the “discriminator” field moved from paths/<path>/responses/<..>/content/schema into /components/schemas/BaseType.

I.e. in the old code we had:

"responses": {
  "200": {
	"content": {
	  "application/json": {
		"schema": {
		  "oneOf": [
			{
			  "$ref": "#/components/schemas/Foo"
			},
			{
			  "$ref": "#/components/schemas/Bar"
			}
		  ],
		  "discriminator": {
			"propertyName": "type",
			"mapping": {
			  "Foo": "#/components/schemas/Foo",
			  "Bar": "#/components/schemas/Bar"
			}
		  }
		}
	  }
	}
  }
}

now this has become

"responses": {
  "200": {
	"content": {
	  "application/json": {
		"schema": {
		  "oneOf": [
			{
			  "$ref": "#/components/schemas/Foo"
			},
			{
			  "$ref": "#/components/schemas/Bar"
			}
		  ]
		}
	  }
	}
  }
}
// components
  "FooBase": {
	"required": [
	  "type"
	],
	"type": "object",
	"properties": {
	  "type": {
		"type": "string",
		"nullable": true,
		"readOnly": true
	  }
	},
	"additionalProperties": false,
	"discriminator": {
	  "propertyName": "type",
	  "mapping": {
		"Foo": "#/components/schemas/Foo",
		"Bar": "#/components/schemas/Bar"
	  }
	}
  }

and the discriminator and mapping can only be found under components of the base type. I assume this is an intentional change?

I can see the point of not repeating the discriminator mappings repeatedly, but it seems rather cumbersome to find the discriminator and mapping with the new structure (basically find the common allOf() parent of all the types referenced in oneOf I think? Not sure this would work for more complicated inheritance hierarchies though).

The sample shown at swagger.io has the discriminator under the response part as it was done in the old version though.

Reproduction

Using the basic ASP.NET Core 3.1 template with the following controller:

    [ApiController]
    [Route("[controller]")]
    public class LogsController : ControllerBase
    {
        [HttpGet]
        [ProducesResponseType(typeof(FooBase), StatusCodes.Status200OK)]
        public FooBase Get()
        {
            return new Foo();
        }
    }

    [SwaggerDiscriminator("type")]
    [SwaggerSubType(typeof(Foo), DiscriminatorValue = nameof(Foo))]
    [SwaggerSubType(typeof(Bar), DiscriminatorValue = nameof(Bar))]
    public abstract class FooBase
    {
        public string Type { get; }

        public FooBase(string type)
        {
            Type = type;
        }
    }

    public class Foo : FooBase
    {
        public Foo() : base(nameof(Foo)) { }
    }

    public class Bar : FooBase
    {
        public Bar() : base(nameof(Bar)) { }
    }

and those changes added to the Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
        // standard template 
	services.AddSwaggerGen(c =>
	{
		c.SwaggerDoc("v1",
			new OpenApiInfo
			{
				Title = "My API - V1",
				Version = "v1"
			}
		);
		c.EnableAnnotations(true, true);
	});
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
        // standard template 
	app.UseSwagger();
	app.UseSwaggerUI(c =>
	{
		c.SwaggerEndpoint("v1/swagger.json", "My API V1");
	});
}

results in different swagger.jsons depending on whether 5.6.3 or 6.1.0 is used.

Swagger.json 5.6.3
{
  "openapi": "3.0.1",
  "info": {
    "title": "My API - V1",
    "version": "v1"
  },
  "paths": {
    "/Logs": {
      "get": {
        "tags": [
          "Logs"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Foo"
                    },
                    {
                      "$ref": "#/components/schemas/Bar"
                    }
                  ],
                  "discriminator": {
                    "propertyName": "type",
                    "mapping": {
                      "Foo": "#/components/schemas/Foo",
                      "Bar": "#/components/schemas/Bar"
                    }
                  }
                }
              },
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Foo"
                    },
                    {
                      "$ref": "#/components/schemas/Bar"
                    }
                  ],
                  "discriminator": {
                    "propertyName": "type",
                    "mapping": {
                      "Foo": "#/components/schemas/Foo",
                      "Bar": "#/components/schemas/Bar"
                    }
                  }
                }
              },
              "text/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Foo"
                    },
                    {
                      "$ref": "#/components/schemas/Bar"
                    }
                  ],
                  "discriminator": {
                    "propertyName": "type",
                    "mapping": {
                      "Foo": "#/components/schemas/Foo",
                      "Bar": "#/components/schemas/Bar"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Bar": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/FooBase"
          }
        ],
        "additionalProperties": false
      },
      "FooBase": {
        "required": [
          "type"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          }
        },
        "additionalProperties": false
      },
      "Foo": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/FooBase"
          }
        ],
        "additionalProperties": false
      }
    }
  }
}
Swagger.json 6.1.0
{
  "openapi": "3.0.1",
  "info": {
    "title": "My API - V1",
    "version": "v1"
  },
  "paths": {
    "/Logs": {
      "get": {
        "tags": [
          "Logs"
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "text/plain": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Foo"
                    },
                    {
                      "$ref": "#/components/schemas/Bar"
                    }
                  ]
                }
              },
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Foo"
                    },
                    {
                      "$ref": "#/components/schemas/Bar"
                    }
                  ]
                }
              },
              "text/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/Foo"
                    },
                    {
                      "$ref": "#/components/schemas/Bar"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Bar": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/FooBase"
          }
        ],
        "additionalProperties": false
      },
      "Foo": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/FooBase"
          }
        ],
        "additionalProperties": false
      },
      "FooBase": {
        "required": [
          "type"
        ],
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true,
            "readOnly": true
          }
        },
        "additionalProperties": false,
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "Foo": "#/components/schemas/Foo",
            "Bar": "#/components/schemas/Bar"
          }
        }
      }
    }
  }
}

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:11
  • Comments:9

github_iconTop GitHub Comments

1reaction
danielcrabtreecommented, Jul 15, 2023

I have submitted a Pull Request #2683 that fixes this bug.

1reaction
cdrfenixcommented, Mar 1, 2023

This issue is breaking the openapi typescript fetch client generator and others. Any news on this?.

Read more comments on GitHub >

github_iconTop Results From Across the Web

OpenAPI / Swagger 3.0: Default discriminator value
I tried using default value to get it to work. However, the resulting code includes shadowed properties (same property on the child and...
Read more >
Ahoy
Swagger does not obey FromHeader attribute in header as specified in .net core $ ... SwaggerDiscriminator Attribute: discriminator field moved from paths to...
Read more >
OpenAPI Specification - Version 3.0.3
The OpenAPI document MUST contain at least one paths field, a components field or a webhooks field. An OpenAPI document uses and conforms...
Read more >
Branching model - Bitbucket Cloud
This view is read-only. The branching model settings can be changed using the settings API. The returned object: Always has a development property....
Read more >
Schema generation rules · GitBook
The discriminator property indicates which subtype is used whenever an instance of the base type is found. The discriminator's possible values are the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found