lanjusto
lanjusto

Reputation: 71

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

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

Upvotes: 8

Views: 5880

Answers (3)

Juan Moreno
Juan Moreno

Reputation: 2795

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<CustomEnum>();
customEnumItem.setType("string");
customEnumItem.setEnum(List.of(CustomEnum.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" : {
      "CustomEnumItem" : {
        "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.

Upvotes: 1

sergiopf
sergiopf

Reputation: 61

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)) }),

Upvotes: 0

David Karlsson
David Karlsson

Reputation: 9716

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
    }
}

Upvotes: 6

Related Questions