Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C# Enums aren't shown as dropdowns in Swagger UI #3645

Open
skovalyova opened this issue Sep 23, 2021 · 13 comments
Open

C# Enums aren't shown as dropdowns in Swagger UI #3645

skovalyova opened this issue Sep 23, 2021 · 13 comments

Comments

@skovalyova
Copy link

Environment
.NET 5, NSwag AspNet.Core 13.13.2, NSwag.MsBuild 13.13.2

Steps

Let's imagine an API endpoint that accepts a model with property of enum type:

public async Task<ActionResult> SearchAsync([FromQuery] SearchQuery request)
{
    return Ok();
}
public class SearchQuery
{
    public SearchDirection Direction { get; set; } = SearchDirection.Next;

  // Other model properties
}
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum SearchDirection
{
    Prev = 0,
    Next = 1,
}

It is generated as the following definition in specification:

{
        "name": "Direction",
        "in": "query",
        "schema": {
          "example": "Next",
          "oneOf": [
            {
              "$ref": "#/components/schemas/SearchDirection"
            }
          ]
        },
        "x-position": 1
}
  "schemas": {
    "SearchDirection": {
        "type": "string",
        "description": "",
        "x-enumNames": [
          "Prev",
          "Next"
        ],
        "enum": [
          "Prev",
          "Next"
        ]
      }
  }

Expected result
Property of enum type is displayed in Swagger UI as dropdown.

Actual result
Property is displayed as text input field:
image

Notes
When I move enum to the top level like this, it works as expected and is displayed as a dropdown:

public async Task<ActionResult> SearchAsync([FromQuery] SearchDirection searchDirection)
@RicoSuter
Copy link
Owner

Isnt this a problem with Swagger UI?
Or is this fixed in the latest Swagger UI and we need to bundle a newer version?

@skovalyova
Copy link
Author

I'm not sure whose problem it is.

@alvinhao666
Copy link

This problem also occurs when the enum is nullable

@ryanolsonx
Copy link

This doesn't work in all of the different Swagger UIs I've used (Swagger UI and RapiDoc).

In looking at the documentation (OpenApi spec and swagger.io about enums), it doesn't appear that any usage of enum uses this format.

So I think this would need to be fixed here in NSwag.

@hauntingEcho
Copy link

using the test YAML below, only /1 and /6 are displayed incorrectly - telling me that SwaggerUI has an issue with enums in oneOf.

It does seem bizarre to have a oneOf with only one entry, but that's needed for defaults as far as I can tell (probably other stuff too).

openapi: 3.0.0
paths:
  /1:
    get:
      parameters:
      - name: order
        in: query
        description: Optional parameter for the method to sort results by.
        schema:
          default: Ascending
          oneOf:
          - $ref: '#/components/schemas/SortOrder'
  /2:
    get:
      parameters:
      - name: order
        in: query
        description: Optional parameter for the method to sort results by.
        schema:
          $ref: '#/components/schemas/SortOrder'
  /3:
    get:
      parameters:
      - name: order
        in: query
        description: Optional parameter for the method to sort results by.
        schema:
          type: string
          enum:
          - ascending
          - descending
  /4:
    get:
      parameters:
      - name: order
        in: query
        description: Optional parameter for the method to sort results by.
        schema:
          $ref: '#/components/schemas/SortOrder'
  /5:
    get:
      parameters:
      - name: order
        in: query
        description: Optional parameter for the method to sort results by.
        schema:
          type: string
          nullable: true
          enum:
          - ascending
          - descending
  /6:
    get:
      parameters:
      - name: order
        in: query
        description: Optional parameter for the method to sort results by.
        schema:
          oneOf:
          - type: string
            enum:
            - ascending
            - descending
components:
  schemas:
    SortOrder:
      type: string
      enum:
      - ascending
      - descending
    NullableSortOrder:
      type: string
      nullable: true
      enum:
      - ascending
      - descending

@hauntingEcho
Copy link

hauntingEcho commented Mar 15, 2022

I've opened an issue with Swagger-UI: swagger-api/swagger-ui#7912.

That said, oneOf/allOf support tends to be pretty poor across the ecosystem IMO.

@hauntingEcho
Copy link

It looks like the Swagger-UI issue also affects anyOf, but not allOf. For this use case, any of the three would be valid - would you be open to generating allOf instead?

This also affects body objects that need endpoint-specific config

@hauntingEcho
Copy link

hauntingEcho commented Mar 15, 2022

If I'm reading back through history correctly, at some point in the past allOf was used but it was switched to oneOf to work around bugs in AutoRest? Meaning this would need to be a configuration flag of which to use, depending on whose bugs you want to work around :)

@hauntingEcho
Copy link

hauntingEcho commented Mar 25, 2022

thinking on this more - this could be exposed as an option e.g. "ModelPropertyOverrideMethod" with options "allOf" and "oneOf" (leaving "anyOf" out until a consumer that only works with that is identified). Because swagger-ui is used for the hosted docs UI, and still mentioned in the docs by AllowReferencesWithProperties, I'd push for allOf to be the default option. I think this needs to be in JsonSchemaGeneratorSettings, though.

@Red1408192
Copy link

i encountered this problem as well, it affect only the openapi3 documentation but not the previus ones

@Red1408192
Copy link

there is any new insight on how to workaroud this problem?

@Red1408192
Copy link

Red1408192 commented Mar 5, 2023

image

this fixed the issue to me for the time being

EDIT: but doing so will remove the default value since $ref must be the only child of a node

@Red1408192
Copy link

an even better solution is to add the following postOperation:

                conf.PostProcess = (x) =>
                {
                    foreach (var parameter in x.Operations
                    .SelectMany(x => x.Operation.Parameters)
                    .Where(x => x.Schema.OneOf.Count == 1 && x.Schema.OneOf.Single().Reference != null && x.Schema.OneOf.Single().Reference.IsEnumeration))
                    {
                        parameter.Schema.AllOf.Add(parameter.Schema.OneOf.Single());
                        parameter.Schema.OneOf.Clear();
                    }
                };

this will mantain the default values in the interface

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants