OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
20.62k stars 6.29k forks source link

[BUG][SPRING-JAVA] Wrong generation result #18710

Open Shotim opened 1 month ago

Shotim commented 1 month ago

Bug Report Checklist

Description

I identified several common tags for the API in the specification file

openapi-generator version

7.5.0

OpenAPI declaration file content or url
---
openapi: 3.0.0
info:
  title: Catalog API
  version: 1.0.0
tags:
  - name: Material
    description: List of All materials
  - name: Material Type
    description: List of All material types
  - name: Material Group
    description: List of All material groups
  - name: Status
    description: List of All statuses
  - name: Measurement Unit
    description: List of All measurement units
  - name: Measuring Point
    description: List of All measuring points
  - name: Document
    description: List of All documents
  - name: Document Type
    description: List of All document types

paths:
  /catalog/v1/api/material-types/{id}:
    get:
      tags:
        - Material Type
      description: Get Material Type by id
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MaterialTypeEntity'
  /catalog/v1/api/material-types:
    get:
      tags:
        - Material Type
      description: Get Material Types
      parameters:
        - name: pageNumber
          in: query
          description: page number of the list
          required: false
          schema:
            type: integer
        - name: pageSize
          in: query
          description: amount of elements on a page
          required: false
          schema:
            type: integer
        - name: sort
          in: query
          description: sort parameter
          required: false
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      content:
                        type: array
                        items:
                          $ref: '#/components/schemas/MaterialTypeEntity'
  /catalog/v1/api/material-groups/{id}:
    get:
      tags:
        - Material Group
      description: Get Material Group by id
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MaterialGroupEntity'
  /catalog/v1/api/material-groups:
    get:
      tags:
        - Material Group
      description: Get Material Groups
      parameters:
        - name: pageNumber
          in: query
          description: page number of the list
          required: false
          schema:
            type: integer
        - name: pageSize
          in: query
          description: amount of elements on a page
          required: false
          schema:
            type: integer
        - name: sort
          in: query
          description: sort parameter
          required: false
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      content:
                        type: array
                        items:
                          $ref: '#/components/schemas/MaterialGroupEntity'
  /catalog/v1/api/materials/{id}:
    get:
      tags:
        - Material
      description: Get Material by id
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MaterialEntity'
  /catalog/v1/api/materials:
    get:
      x-spring-paginated: true
      tags:
        - Material
      description: Get list of Materials
      parameters:
        - name: id
          in: query
          description: id of the required material
          required: false
          schema:
            type: string
        - name: statusId
          in: query
          description: exception status of the required material
          required: false
          schema:
            type: string
        - name: pageNumber
          in: query
          description: page number of the list
          required: false
          schema:
            type: integer
        - name: pageSize
          in: query
          description: amount of elements on a page
          required: false
          schema:
            type: integer
        - name: sort
          in: query
          description: sort parameter
          required: false
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      content:
                        type: array
                        items:
                          $ref: '#/components/schemas/MaterialEntity'
    post:
      tags:
        - Material
      description: Create temporal material
      requestBody:
        description: A JSON object of a temporal material
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TemporalMaterialRequest'
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TemporalMaterialResponse'
  /catalog/v1/api/statuses/{id}:
    get:
      tags:
        - Status
      description: Get Status by id
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StatusEntity'
  /catalog/v1/api/statuses:
    get:
      x-spring-paginated: true
      tags:
        - Status
      description: Get list of All statuses
      parameters:
        - name: pageNumber
          in: query
          description: page number of the list
          required: false
          schema:
            type: integer
        - name: pageSize
          in: query
          description: amount of elements on a page
          required: false
          schema:
            type: integer
        - name: category
          in: query
          description: category of the status
          required: true
          schema:
            type: string
        - name: sort
          in: query
          description: sort parameter
          required: false
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      content:
                        type: array
                        items:
                          $ref: '#/components/schemas/StatusEntity'
  /catalog/v1/api/measurement-units/{id}:
    get:
      tags:
        - Measurement Unit
      description: Get Measurement Unit by id
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MeasurementUnitEntity'
  /catalog/v1/api/measurement-units:
    get:
      x-spring-paginated: true
      tags:
        - Measurement Unit
      description: Get list of All Measurement Units
      parameters:
        - name: pageNumber
          in: query
          description: page number of the list
          required: false
          schema:
            type: integer
        - name: pageSize
          in: query
          description: amount of elements on a page
          required: false
          schema:
            type: integer
        - name: sort
          in: query
          description: sort parameter
          required: false
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      content:
                        type: array
                        items:
                          $ref: '#/components/schemas/MeasurementUnitEntity'
  /catalog/v1/api/measuting-points/{id}:
    get:
      tags:
        - Measuring Point
      description: Get Measuring Point by id
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MeasuringPointEntity'
  /catalog/v1/api/measuting-points:
    get:
      x-spring-paginated: true
      tags:
        - Measuring Point
      description: Get list of All Measuring Points
      parameters:
        - name: pageNumber
          in: query
          description: page number of the list
          required: false
          schema:
            type: integer
        - name: pageSize
          in: query
          description: amount of elements on a page
          required: false
          schema:
            type: integer
        - name: sort
          in: query
          description: sort parameter
          required: false
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      content:
                        type: array
                        items:
                          $ref: '#/components/schemas/MeasuringPointEntity'
  /catalog/v1/api/document-types/{id}:
    get:
      tags:
        - Document Type
      description: Get Document Type by id
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentTypeEntity'
  /catalog/v1/api/document-types:
    get:
      x-spring-paginated: true
      tags:
        - Document Type
      description: Get list of All Document Types
      parameters:
        - name: pageNumber
          in: query
          description: page number of the list
          required: false
          schema:
            type: integer
        - name: pageSize
          in: query
          description: amount of elements on a page
          required: false
          schema:
            type: integer
        - name: sort
          in: query
          description: sort parameter
          required: false
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      content:
                        type: array
                        items:
                          $ref: '#/components/schemas/DocumentTypeEntity'
  /catalog/v1/api/documents/{id}:
    get:
      tags:
        - Document
      description: Get Document by id
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentEntity'
  /catalog/v1/api/documents:
    get:
      tags:
        - Document
      description: Get list of All Documents
      parameters:
        - name: search
          in: query
          required: false
          schema:
            type: string
        - name: techPlace
          in: query
          required: false
          schema:
            type: string
        - name: documentKind
          in: query
          required: false
          schema:
            type: string
        - name: documentTypeId
          in: query
          required: false
          schema:
            type: string
        - name: pageNumber
          in: query
          description: page number of the list
          required: false
          schema:
            type: integer
        - name: pageSize
          in: query
          description: amount of elements on a page
          required: false
          schema:
            type: integer
        - name: sort
          in: query
          description: sort parameter
          required: false
          schema:
            type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/Page'
                  - type: object
                    properties:
                      content:
                        type: array
                        items:
                          $ref: '#/components/schemas/DocumentEntity'
    post:
      tags:
        - Document
      description: Create document
      requestBody:
        description: An object of a document
        content:
          multipart/form-data:
            schema:
              type: object
              required: [ file,documentKind,documentTypeId, tm/eoCode ]
              properties:
                file:
                  type: array
                  items:
                    type: string
                    format: binary
                documentKind:
                  type: string
                documentTypeId:
                  type: string
                tm/eoCode:
                  type: string
      responses:
        "200":
          description: ""
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentEntity'

components:
  schemas:
    Page:
      type: object
      properties:
        content:
          type: array
          items:
            type: object
        pageable:
          $ref: '#/components/schemas/Pageable'
        last:
          type: boolean
        totalElements:
          type: integer
        totalPages:
          type: integer
        first:
          type: boolean
        numberOfElements:
          type: integer
        empty:
          type: boolean
    Pageable:
      type: object
      properties:
        pageSize:
          type: integer
        pageNumber:
          type: integer
        sort:
          $ref: '#/components/schemas/Sort'
    Sort:
      type: object
      properties:
        sorted:
          type: boolean
        unsorted:
          type: boolean
    MaterialTypeEntity:
      required:
        - id
        - name
      properties:
        id:
          type: string
        name:
          type: string
      type: object
    MaterialGroupEntity:
      required:
        - id
        - name
      properties:
        id:
          type: string
        name:
          type: string
      type: object
    MaterialEntity:
      required:
        - id
        - name
        - price
        - measurementUnit
        - amount
        - materialTypeId
        - materialType
        - materialGroupId
        - materialGroup
      properties:
        id:
          type: string
        name:
          type: string
        price:
          type: number
        measurementUnit:
          $ref: '#/components/schemas/MeasurementUnitEntity'
        amount:
          type: integer
        materialTypeId:
          type: string
        materialType:
          $ref: '#/components/schemas/MaterialTypeEntity'
        materialGroupId:
          type: string
        materialGroup:
          $ref: '#/components/schemas/MaterialGroupEntity'
      type: object
    TemporalMaterialRequest:
      required:
        - name
        - price
        - measurementUnitId
        - amount
      properties:
        name:
          type: string
        price:
          type: number
        measurementUnitId:
          type: string
        amount:
          type: integer
      type: object
    TemporalMaterialResponse:
      required:
        - id
        - name
        - price
        - measurementUnit
        - amount
      properties:
        id:
          type: string
        name:
          type: string
        price:
          type: number
        measurementUnit:
          $ref: '#/components/schemas/MeasurementUnitEntity'
        amount:
          type: integer
      type: object
    StatusEntity:
      required:
        - id
        - name
      properties:
        id:
          type: string
        name:
          type: string
      type: object
    MeasurementUnitEntity:
      required:
        - id
        - name
      properties:
        id:
          type: string
        name:
          type: string
      type: object
    MeasuringPointEntity:
      required:
        - id
        - measuringPointKind
        - name
        - position
        - description
        - codeGroup
        - nodeId
        - type
      properties:
        id:
          type: string
        measuringPointKind:
          type: string
        name:
          type: string
        position:
          type: string
        description:
          type: string
        codeGroup:
          type: string
        nodeId:
          type: string
        type:
          type: string
      type: object
    DocumentTypeEntity:
      required:
        - id
        - name
      properties:
        id:
          type: string
        name:
          type: string
      type: object
    DocumentEntity:
      required:
        - id
        - name
        - status
        - date
        - documentKind
        - documentType
        - tm/eoCode
        - link
      properties:
        id:
          type: string
        name:
          type: string
        status:
          $ref: '#/components/schemas/StatusEntity'
        date:
          type: string
        documentKind:
          type: string
        documentType:
          $ref: '#/components/schemas/DocumentTypeEntity'
        tm/eoCode:
          type: string
        link:
          type: string
      type: object
Generation Details

I use configuration in the mvn pom.xml

<plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <version>7.5.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <skipValidateSpec>false</skipValidateSpec>
                            <inputSpec>./src/main/resources/catalogAPI.yaml</inputSpec>
                            <generatorName>spring</generatorName>
                            <importMappings>
                                <importMapping>Pageable=org.springframework.data.domain.Pageable</importMapping>
                                <importMapping>Page=org.springframework.data.domain.PageImpl</importMapping>
                                <importMapping>Sort=org.springframework.data.domain.Sort</importMapping>
                            </importMappings>
                            <configOptions>
                                <useSpringBoot3>true</useSpringBoot3>
                                <openApiNullable>false</openApiNullable>
                                <interfaceOnly>true</interfaceOnly>
                            </configOptions>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

And I see result of

@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.5.0")
@Validated
@Tag(name = "Document Type", description = "List of All document types")
public interface CatalogApi {

And it makes for every request additional tag of Document Type when only several requests belong to it

Steps to reproduce

execution of mvn clean package

Related issues/PRs

Didn't found any

Suggest a fix

I didn't find workaround for that

jpfinne commented 1 month ago

All your endpoints starts with /catalog. That produces the CatalogApi.

What you probably want is to use some configOptions described here : https://openapi-generator.tech/docs/generators/spring/

for example <useTags>true</useTags> will produce several interfaces: DocumentApi, MaterialTypeApi, MaterialGroupApi...

The other solution is to remove /catalog/v1/api from the endpoints and use only the relevant end (for example /material-types/{id}) -> it will produce a MaterialTypesApi interface

then you have 2 options:

add the following to your contract.

servers:
  - url: /catalog/v1/api

and correctly configure the configOptions requestMappingMode.

either add @RequestMapping("/catalog/v1/api") to your controllers

Shotim commented 1 month ago

@jpfinne I mean, that it adds @Tag annotation of Document type on a controller class and either additional tags on endpoints. So it makes additional tag for every endpoint so that I see that all endpoints belongs to Document Type tag which is wrong behaviour. Thank you for your impact!