How do I create reusable enums in Swagger specification generated from Java code?
Asked Answered
P

3

8

I am trying to generate OpenAPI (version 3.0.1) specification for my Java code. In order to achieve this I use Swagger Annotations (version 2.0.8) and Swagger Maven Plugin.

I have a problem with Enums. Say, I have two methods returning the very same Enum. In OpenAPI specification, I would like to have the single schema definition for this Enum and $ref link in both API operations. But instead I have duplicated Enum definitions in each API operations. How do I avoid this duplication without editing specification file manually?

Here is Java code with two methods returning the same Enum:

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/properties")
@Produces("application/json")
public class TestService {
    @GET
    @Path("/enum1")
    @Operation
    @ApiResponses({
        @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Color.class)))
    })
    public Color getColor1() {
        throw new UnsupportedOperationException();
    }

    @GET
    @Path("/enum2")
    @Operation
    @ApiResponses({
        @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Color.class)))
    })
    public Color getColor2() {
        throw new UnsupportedOperationException();
    }

    public enum Color {
        RED,

        GREEN,

        BLUE
    }
}

Here is specification I would like to get:

openapi: 3.0.1
components:
    schemas:
        Color:
            type: string
            enum:
                - RED
                - GREEN
                - BLUE
paths:
  /properties/enum2:
    get:
      operationId: getColor2
      responses:
        200:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Color'
  /properties/enum1:
    get:
      operationId: getColor1
      responses:
        200:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Color'

And here is specification I do get:

openapi: 3.0.1
paths:
  /properties/enum2:
    get:
      operationId: getColor2
      responses:
        200:
          content:
            application/json:
              schema:
                type: string
                enum:
                - RED
                - GREEN
                - BLUE
  /properties/enum1:
    get:
      operationId: getColor1
      responses:
        200:
          content:
            application/json:
              schema:
                type: string
                enum:
                - RED
                - GREEN
                - BLUE

Pigtail answered 17/7, 2019 at 15:48 Comment(5)
Q: Have you considered using a ref? swagger.io/docs/specification/using-refGanges
The thing is I prefer not to write Swagger file manually. I would like to have it autogenerated from my code. Actually $ref is what I need but I would like to express it in via Swagger Java annotations.Pigtail
If a $ref is what you need, then a $ref is what you should use :) Look here and here for annotations examples you should be able to adapt pretty easily. Please post back what you learn.Ganges
Sorry, I was not accurate enough. Yes, $ref is what I need. But there is one more thing I need — a schema definition this $ref will refer to. Thank you for your links, but there is a little different kind of issue. It is about how to refer (with $ref) to existing schema. As for me, I need this schema to be generated (not inside operation definition, but in reusable way).Pigtail
Did you manage to do it?Thunderstorm
C
6

I am in kotlin but I added the (relatively new) @Schema(enumAsRef = true) to the enum class with success.

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/properties")
@Produces("application/json")
public class TestService {
    @GET
    @Path("/enum1")
    @Operation
    @ApiResponses({
        @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Color.class)))
    })
    public Color getColor1() {
        throw new UnsupportedOperationException();
    }

    @GET
    @Path("/enum2")
    @Operation
    @ApiResponses({
        @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Color.class)))
    })
    public Color getColor2() {
        throw new UnsupportedOperationException();
    }

    @Schema(enumAsRef = true) // THIS MAKES ENUM REF
    public enum Color {
        RED,

        GREEN,

        BLUE
    }
}
Charmer answered 4/2, 2021 at 10:36 Comment(0)
D
0

I was facing the same issue with a Dropwizard application.

My model was something like this:

public class Model {

  @Schema(name = "fields")
  @Default
  @NotNull
  private Set<CustomEnum> fields = Collections.emptySet();

  @Schema(name = "patches")
  @Default
  @NotNull
  private Set<CustomEnum> patches = Collections.emptySet();
}

and my enum:

public enum CustomEnum {
  COMPANY,
  PERSON,
  EMAIL
}

My approach was the following:

First, add the @Schema annotation to the CustomEnum enum:

@Schema(ref = "#/components/schemas/CustomEnumItem")
public enum CustomEnum {}

Note that I change the reference name to CustomEnumItem.

Second, I registered the schema and swagger configuration programmatically:

OpenAPI oas =
    new OpenAPI()
        .info(getInfo())
        .addServersItem(new Server().url(baseUrl));

var customEnumItem = new Schema<CustomEnumItem>();
patchKeysItem.setType("string");
patchKeysItem.setEnum(List.of(CustomEnumItem.values()));
oas.getComponents().addSchemas("CustomEnumItem", customEnumItem);

return new SwaggerConfiguration()
    .openAPI(oas)
    .readAllResources(true)
    .prettyPrint(true);

So, my swagger.json file was generated like this:

  • Schema section:
  "components" : {
    "schemas" : {
      "PatchKeyItem" : {
        "type" : "string",
        "enum" : [ "COMPANY", "PERSON", "EMAIL"]
      },
  • Model section:
"Model" : {
        "required" : [ "fields", "patches"],
        "type" : "object",
        "properties" : {
          "fields" : {
            "uniqueItems" : true,
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/CustomEnumItem"
            }
          },
          "patches" : {
            "uniqueItems" : true,
            "type" : "array",
            "items" : {
              "$ref" : "#/components/schemas/CustomEnumItem"
            }
          },

And my generated client code like this:

public class Model  {

    @JsonProperty("fields")
    private Set<CustomEnumItem> fields;

    @JsonProperty("patches")
    private Set<CustomEnumItem> patches;

Before this change I got something like this (two enums instead of one):

public class Model  {

    @JsonProperty("fields")
    private Set<FieldsEnum> fields;

    @JsonProperty("patches")
    private Set<PatchesEnum> patches;
}

We can find the reference to reusable enums here.

Also as @david-karlsson mentioned enumAsRef also works, but you need swagger annotations 2.1.0.

Devonadevondra answered 20/7, 2021 at 14:52 Comment(0)
S
0

Write a toString in the enum class (in my example ErrorClass). Swagger takes it automatically.

@ApiResponse(responseCode = "404",description = "Errors example",content = {
                    @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorClass.class)) }),
Swamp answered 19/7, 2024 at 7:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.