openapi-generator icon indicating copy to clipboard operation
openapi-generator copied to clipboard

[BUG] [Kotlin] DTOs for components of different components are conflated if they have the same members

Open HermannGruber opened this issue 1 year ago • 0 comments

Bug Report Checklist

  • [ X ] Have you provided a full/minimal spec to reproduce the issue?
  • [ X ] Have you validated the input using an OpenAPI validator (example)?
  • [ ] Have you tested with the latest master to confirm the issue still exists?
  • [ X ] Have you searched for related issues/PRs?
  • [ X ] What's the actual output vs expected output?
  • [ ] [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

In my minimal repro, I have two types of data. The issue component has a user with fields id and name. The item component also has a user with fields id and name. The generated Kotlin code consists of an ItemDto, an IssueDto, and an ItemUserDto.

The problem is the following: the IssueDto references the ItemUserDto. I do not expect that:

  • Although these user objects by coincidence have the same members at the moment, these are entirely different , and are expected to have different members in the future, as the API evolves. If I had wanted to have a UserDto as a concept which is shared by the two components, I would have declared the user object as a global component.
  • also, the name ItemUserDto is misleading, since the DTO is used in the context of an issue.
openapi-generator version

I tried it out with versions 6.5.0 and 7.5.0. I was not able yet to verify that it still occurs with 7.6.0, since I had trouble updating to 7.6.0.

OpenAPI declaration file content or url
openapi: 3.0.0
info:
  title: Test API
  version: 1.0.0

components:
  schemas:
    Item:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for the item
          example: "123e4567-e89b-12d3-a456-426614174001"
        user:
          type: object
          properties:
            id:
              type: string
              format: uuid
              description: Unique identifier for the user
              example: "123e4567-e89b-12d3-a456-426614174000"
            name:
              type: string
              description: Name of the user
              example: "John Doe"
          required:
            - id
            - name
      required:
        - id
        - user

    Issue:
      type: object
      properties:
        id:
          type: string
          format: uuid
          description: Unique identifier for the issue
          example: "987e6543-b21a-43c2-b789-876543210000"
        user:
          type: object
          properties:
            id:
              type: string
              format: uuid
              description: Unique identifier for the user
              example: "123e4567-e89b-12d3-a456-426614174000"
            name:
              type: string
              description: Name of the user
              example: "John Doe"
          required:
            - id
            - name
      required:
        - id
        - user

paths:
  /items:
    get:
      summary: Get items
      responses:
        '200':
          description: A list of items
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Item'

  /issues:
    get:
      summary: Get issues
      responses:
        '200':
          description: A list of issues
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Issue'
Generation Details

The following is the relevant part of our build.gradle.kts:

    inputSpec.set("$rootDir/src/main/resources/static/openapi.yaml")
    outputDir.set(openApiOutputDirPath.get().toString())
    packageName.set(rootProject.name)
    invokerPackage.set("${rootProject.name}.security")
    apiPackage.set("${rootProject.name}.api")
    modelPackage.set("${rootProject.name}.model")
    modelNameSuffix.set("Dto")
    generatorName.set("kotlin-spring")
    configOptions.set(
        mapOf(
            "useSpringBoot3" to "true",
            "delegatePattern" to "true",
            "interfaceOnly" to "true",
            "dateLibrary" to "java8",
            "useTags" to "true",
            "enumPropertyNaming" to "UPPERCASE"
        )
    )
}
Steps to reproduce

Invoke the gradle build task described above

Related issues/PRs

None found.

Suggest a fix

I will refer to the above example for clarity. I suggest to generate not only an ItemDto, an IssueDto, and an ItemUserDto, but also an IssueUserDto. The IssueUserDto should be referenced by the IssueDto.

I understand that changing the generated types as a default behavior would incur a breaking change; we can introduce a new configuration option dontReuseDtoTypes = true or similar to enable the new behavior suggested above.

HermannGruber avatar Jun 18 '24 08:06 HermannGruber