javaspring-bootswaggeropenapispringdoc

Is there a way to have a ComposedSchema with a discriminator part in a contract generated with springdoc-openapi-maven-plugin?


I have a sample SpringBoot API with the following features:

I try to generate an OpenApi contract from this API with springdoc-openapi-maven-plugin.

In my pom.xml, I have the following elements:

Here are my classes I generate schema from.

import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;

public class ContainerClass {

    @ArraySchema(
        arraySchema = @Schema(discriminatorProperty = "classType"), 
        schema = @Schema(implementation = ParentClass.class)
    )
    public List<ParentClass> elements;
    
    // + Getter/Setter
}
@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXISTING_PROPERTY,
        property = "classType",
        defaultImpl = ParentClass.class,
        visible = true)
@JsonSubTypes({
        @JsonSubTypes.Type(value = ChildA.class, name = "CHILD_A"),
        @JsonSubTypes.Type(value = ChildB.class, name = "CHILD_B")})
@Schema(
        description = "Parent description",
        discriminatorProperty = "classType",
        discriminatorMapping = {
                @DiscriminatorMapping(value = "CHILD_A", schema = ChildA.class),
                @DiscriminatorMapping(value = "CHILD_B", schema = ChildB.class)
        }
)
public abstract class ParentClass {

    public String classType;

    // + Getter/Setter
}
@io.swagger.v3.oas.annotations.media.Schema(description = " Child A", allOf = ParentClass.class)
public class ChildA extends ParentClass{
}
@io.swagger.v3.oas.annotations.media.Schema(description = " Child B", allOf = ParentClass.class)
public class ChildB extends ParentClass{
}

When I run springdoc-openapi-maven-plugin, I get the following contract file.

openapi: 3.0.1
info:
  title: OpenAPI definition
  version: v0
servers:
- url: http://localhost:8080
  description: Generated server url
paths:
  /container:
    get:
      tags:
      - hello-controller
      operationId: listElements
      responses:
        "200":
          description: OK
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/ContainerClass'
components:
  schemas:
    ChildA:
      type: object
      description: ' Child A'
      allOf:
      - $ref: '#/components/schemas/ParentClass'
    ChildB:
      type: object
      description: ' Child B'
      allOf:
      - $ref: '#/components/schemas/ParentClass'
    ContainerClass:
      type: object
      properties:
        elements:
          type: array
          description: array schema description
          items:
            oneOf:
            - $ref: '#/components/schemas/ChildA'
            - $ref: '#/components/schemas/ChildB'
    ParentClass:
      type: object
      properties:
        classType:
          type: string
      description: Parent description
      discriminator:
        propertyName: classType
        mapping:
          CHILD_A: '#/components/schemas/ChildA'
          CHILD_B: '#/components/schemas/ChildB'

Actually, in my context, in order to have not any breaking change with existing consumers, I need items property in ContainerClass schema to contain the discriminator part that is contained in ParentClass schema, like this:

ContainerClass:
  type: object
  properties:
    elements:
      type: array
      description: array schema description
      items:
        discriminator:
          propertyName: classType
          mapping:
            CHILD_A: '#/components/schemas/ChildA'
            CHILD_B: '#/components/schemas/ChildB'
        oneOf:
        - $ref: '#/components/schemas/ChildA'
        - $ref: '#/components/schemas/ChildB'

When I try to set properties in annotation, I don't manage to do that. And when I debug code of io.swagger.v3.core.jackson.ModelResolver, I don't manage to find a way to do that. And so far I have not found an example of code that help me.

Is there a way so that a ComposedSchema (array contained in ContainerClass in my case) has a disciminator part generated by springdoc-openapi-maven-plugin execution?


Solution

  • This the default generation structure. Handled directly by swagger-api (and not springdoc-openapi.

    The generated OpenAPI description looks coorect.

    With springdoc-openapi, you can define an OpenApiCustomiser Bean, where you can change the elements of the components element defined on the OpenAPI level: