Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG][JAVA] While using java 17 and openapi-generator-maven-plugin with version 7.10.0 there is a compilation error while using allOf #20135

Open
6 tasks
oleksandr-ionov-mark43 opened this issue Nov 19, 2024 · 10 comments

Comments

@oleksandr-ionov-mark43
Copy link

Bug Report Checklist

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

While using java 17 and openapi-generator-maven-plugin with version 7.10.0 there is a compilation error while using allOf.
Example:

openapi: '3.0.3'
info:
  version: '2.0'
paths: {}
components:
  schemas:
    ExamplesCreateRequest:
      type: 'object'
      properties:
        examples:
          type: 'array'
          items:
            $ref: './ExampleCreateRequest.yaml#/components/schemas/ExampleCreateRequest'
          description: 'List of examples to create.'
      required:
        - 'examples'

and ExampleCreateRequest is next:

openapi: '3.0.3'
info:
  version: '2.0'
paths: {}
components:
  schemas:
    ExampleCreateRequest:
      type: 'object'
      allOf:
        - $ref: './Example.yaml#/components/schemas/Example'

the next java code was generated

public ExamplesCreateRequest addExamplesItem(Example examplesItem) {
    if (this.examples == null) {
      this.examples = new ArrayList<>();
    }
    this.examples.add(examplesItem); <--- Compilation error 
    return this;
  }
openapi-generator version
OpenAPI declaration file content or url
Generation Details
Steps to reproduce
Related issues/PRs
Suggest a fix
@paul-kraftlauget
Copy link

I am facing the same issue after upgrading to version 7.10.0:

  InternationalAccountLookupError:
    type: object
    description: >-
      Error object structure for InternationalAccountLookupResponseError
    allOf:
      - type: object
        required:
          - id
          - accountIdType
          - accountId
          - backendErrorCode
          - errorDescription
        properties:
          id:
            $ref: '#/definitions/Id'
          accountIdType:
            $ref: '#/definitions/AccountIdType'
          accountId:
            $ref: '#/definitions/AccountId'
          backendErrorCode:
            $ref: '#/definitions/BackendErrorCode'
          errorDescription:
            $ref: '#/definitions/ErrorDescription'

Generated code:


  public InternationalAccountLookupResponse internationalAccountLookupError(@jakarta.annotation.Nullable List<InternationalAccountLookupError> internationalAccountLookupError) {
    
    this.internationalAccountLookupError = internationalAccountLookupError;
    return this;
  }

  public InternationalAccountLookupResponse addInternationalAccountLookupErrorItem(Object internationalAccountLookupErrorItem) {
    if (this.internationalAccountLookupError == null) {
      this.internationalAccountLookupError = new ArrayList<>();
    }
    this.internationalAccountLookupError.add(internationalAccountLookupErrorItem);
    return this;
  }

  /**
   * Get internationalAccountLookupError
   * @return internationalAccountLookupError
   */
  @jakarta.annotation.Nullable

  @JsonProperty(JSON_PROPERTY_INTERNATIONAL_ACCOUNT_LOOKUP_ERROR)
  @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)

  public List<InternationalAccountLookupError> getInternationalAccountLookupError() {
    return internationalAccountLookupError;
  }

Compile error:

error: incompatible types: Object cannot be converted to InternationalAccountLookupError
    this.internationalAccountLookupError.add(internationalAccountLookupErrorItem);
                                             ^

If I change the API to this, it works:

  InternationalAccountLookupError:
    type: object
    description: >-
      Error object structure for InternationalAccountLookupResponseError
    required:
      - id
      - accountIdType
      - accountId
      - backendErrorCode
      - errorDescription
    properties:
      id:
        $ref: '#/definitions/Id'
      accountIdType:
        $ref: '#/definitions/AccountIdType'
      accountId:
        $ref: '#/definitions/AccountId'
      backendErrorCode:
        $ref: '#/definitions/BackendErrorCode'
      errorDescription:
        $ref: '#/definitions/ErrorDescription'

@wing328
Copy link
Member

wing328 commented Nov 19, 2024

thanks for sharing the details.

did you upgrade from v7.9.0 or an older version such as v7.8.0?

@paul-kraftlauget
Copy link

paul-kraftlauget commented Nov 19, 2024 via email

@svboettger
Copy link

svboettger commented Nov 20, 2024

Same issue. Interfaces for Spring Controllers now use the object of the allof-Property instead of the specified. 7.9.0 Works fine, 7.10.0 Uses the wrong object

@wing328
Copy link
Member

wing328 commented Nov 20, 2024

I'll look into the issue this week. Please use v7.9.0 for the time being.

Thanks again for reporting the issue with the details.

@wing328
Copy link
Member

wing328 commented Nov 24, 2024

can one of you please share the full spec? I tried to reproduce the issue based on the partial spec shared above but no luck.

@paul-kraftlauget
Copy link

Strangely, if I remove the description field, for the object, it works.

Here is the plugin config:

val clientApis = listOf(...
    "test-bug-v1"
)

clientApis.forEach{
    val packageName = "com.example.clients.${it}".replace("-", ".")
    tasks.register<GenerateTask>("${it}-generate") {
        group = "openapi tools"
        description = "Generates the code from ${it}.yaml"
        generatorName.set("java")
        library.set("webclient")
        inputSpec.set("$projectDir/src/main/resources/openapi/${it}.yaml")
        outputDir.set(layout.buildDirectory.file("generated/sources/openapi/${it}").get().asFile.absolutePath)
        apiPackage.set("${packageName}.api")
        invokerPackage.set("${packageName}.invoker")
        modelPackage.set("${packageName}.model")
        configOptions.set(mapOf(
            "dateLibrary" to "java8",
            "useBeanValidation" to "true",
            "useJakartaEe" to "true"
        ))
        generateApiTests.set(false)
        generateModelTests.set(false)
    }
    ...
}

Here is a stripped down version that has the bug:

swagger: '2.0'
info:
  version: 1.0.0
  title: test
host: 'localhost'
basePath: /api/test/v1
consumes:
  - application/json
produces:
  - application/json
security:
  - HTTP_BASIC: []
paths:
  /international-accounts:
    post:
      tags:
        - Account
      operationId: getInternationalAccountDetails
      parameters:
        - in: body
          name: body
          required: true
          schema:
            type: array
            items:
              $ref: '#/definitions/InternationalAccountLookupRequest'
            minItems: 1
            maxItems: 100
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/InternationalAccountLookupResponse'
definitions:
  AccountIdentification:
    type: object
    required:
      - accountIdType
      - accountId
    properties:
      accountIdType:
        $ref: '#/definitions/AccountIdType'
      accountId:
        $ref: '#/definitions/AccountId'
  AccountIdType:
    type: string
    maxLength: 30
  AccountId:
    type: string
    maxLength: 50
  CurrencyCode:
    type: string
    maxLength: 3
  Bic:
    type: string
    maxLength: 11
  AccountStatus:
    type: string
    maxLength: 4
  Id:
    type: string
  BackendErrorCode:
    type: string
  ErrorDescription:
    type: string
  Status:
    type: string
    maxLength: 4
  InternationalAccountLookupRequest:
    type: object
    required:
      - accountIdType
      - accountId
    properties:
      id:
        $ref: '#/definitions/Id'
      accountIdType:
        $ref: '#/definitions/AccountIdType'
      accountId:
        $ref: '#/definitions/AccountId'
      bic:
        $ref: '#/definitions/Bic'
  InternationalAccountRequest:
    type: object
    required:
      - accountIdType
      - accountId
      - bic
    properties:
      id:
        $ref: '#/definitions/Id'
      accountIdType:
        $ref: '#/definitions/AccountIdType'
      accountId:
        $ref: '#/definitions/AccountId'
      bic:
        $ref: '#/definitions/Bic'
  InternationalAccountLookupResponse:
    type: object
    properties:
      internationalAccountLookupSuccess:
        type: array
        items:
          $ref: '#/definitions/InternationalAccountLookupSuccess'
      internationalAccountLookupError:
        type: array
        items:
          $ref: '#/definitions/InternationalAccountLookupError'
  InternationalAccountLookupSuccess:
    type: object
    required:
      - id
      - accountIdentifications
      - currencyCode
      - bic
      - status
    properties:
      id:
        $ref: '#/definitions/Id'
      accountIdentifications:
        type: array
        items:
          $ref: '#/definitions/AccountIdentification'
      currencyCode:
        $ref: '#/definitions/CurrencyCode'
      bic:
        $ref: '#/definitions/Bic'
      status:
        $ref: '#/definitions/Status'
  InternationalAccountLookupError:
    type: object
    description: >-
      Error object structure for InternationalAccountLookupResponseError
    allOf:
      - type: object
        required:
          - id
          - accountIdType
          - accountId
          - backendErrorCode
          - errorDescription
        properties:
          id:
            $ref: '#/definitions/Id'
          accountIdType:
            $ref: '#/definitions/AccountIdType'
          accountId:
            $ref: '#/definitions/AccountId'
          backendErrorCode:
            $ref: '#/definitions/BackendErrorCode'
          errorDescription:
            $ref: '#/definitions/ErrorDescription'

@wing328
Copy link
Member

wing328 commented Nov 24, 2024

@paul-kraftlauget thanks. I'll take another look tomorrow.

@svboettger
Copy link

svboettger commented Nov 26, 2024

@wing328
Another exaple with the spring generator in version 7.10.0.

`openapi: 3.0.3
info:
title: OpenAPi Bug
version: 1.0.0

paths:
/objecta:
get:
summary: gets Object a
operationId: ObjectA
responses:
200:
description: Product Model of policy detail level.
content:
application/json:
schema:
$ref: '#/components/schemas/ObjectA'
/objectblist:
get:
description: Returns a list of object b
responses:
200:
description: New response
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ObjectB"
/objectc:
get:
description: Returns object c
responses:
200:
description: New response
content:
application/json:
schema:
$ref: '#/components/schemas/ObjectC'

components:
schemas:
ObjectA:
description: Product model of policy details
type: object
allOf:
- $ref: '#/components/schemas/BaseObject'

ObjectB:
  description: Product model of payment details
  type: object
  allOf:
    - $ref: '#/components/schemas/BaseObject'

ObjectC:
  description: Product model of contract details
  type: object
  allOf:
    - $ref: '#/components/schemas/BaseObject'
  properties:
    Test:
      type: boolean

BaseObject:
  description: Base object
  type: object
  properties:
    objectName:
      description: Name of the object
      type: string
    objectId:
      description: id of the object in policy
      type: integer
      example: 133213`

In this all objects are generated, but
ObjectsQApi returns BaseObject instead of Object A

`
/**

import de.test.dto.BaseObject;
import io.swagger.v3.oas.annotations.ExternalDocumentation;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.ArraySchema;
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.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.multipart.MultipartFile;

import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import jakarta.annotation.Generated;

@generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.10.0")
@validated
@tag(name = "objecta", description = "the objecta API")
public interface ObjectaApi {

/**
 * GET /objecta : gets Object a
 *
 * @return Product Model of policy detail level. (status code 200)
 */
@Operation(
    operationId = "objectA",
    summary = "gets Object a",
    responses = {
        @ApiResponse(responseCode = "200", description = "Product Model of policy detail level.", content = {
            @Content(mediaType = "application/json", schema = @Schema(implementation = BaseObject.class))
        })
    }
)
@RequestMapping(
    method = RequestMethod.GET,
    value = "/objecta",
    produces = { "application/json" }
)

ResponseEntity<BaseObject> objectA(
    
);

}
`

ObjebList misses the import for ObjectB

`
/**

import de.test.dto.BaseObject;
import io.swagger.v3.oas.annotations.ExternalDocumentation;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.ArraySchema;
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.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.multipart.MultipartFile;

import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import jakarta.annotation.Generated;

@generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.10.0")
@validated
@tag(name = "objectblist", description = "the objectblist API")
public interface ObjectblistApi {

/**
 * GET /objectblist
 * Returns a list of object b
 *
 * @return New response (status code 200)
 */
@Operation(
    operationId = "objectblistGet",
    description = "Returns a list of object b",
    responses = {
        @ApiResponse(responseCode = "200", description = "New response", content = {
            @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = BaseObject.class)))
        })
    }
)
@RequestMapping(
    method = RequestMethod.GET,
    value = "/objectblist",
    produces = { "application/json" }
)

ResponseEntity<List<ObjectB>> objectblistGet(
    
);

}
`
Only objectC works fine.

The maven snippet with the generator:
`
org.openapitools
openapi-generator-maven-plugin
7.10.0

                <execution>
                    <id>OpenApiBug</id>

                    <phase>generate-sources</phase>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <configuration>
                        <inputSpec>${project.basedir}/src/main/resources/swagger/OpenApiBug.yaml
                        </inputSpec>
                        <generatorName>spring</generatorName>
                        <generateApiTests>false</generateApiTests>
                        <generateApiDocumentation>false</generateApiDocumentation>
                        <generateModelTests>false</generateModelTests>
                        <apiPackage>de.test.controller</apiPackage>
                        <modelPackage>de.test.dto</modelPackage>
                        <ignoreFileOverride>${project.basedir}/.openapi-generator-ignore</ignoreFileOverride>
                        <skipIfSpecIsUnchanged>true</skipIfSpecIsUnchanged>
                        <configOptions>
                            <interfaceOnly>true</interfaceOnly>
                            <hideGenerationTimestamp>true</hideGenerationTimestamp>
                            <annotationLibrary>swagger2</annotationLibrary>
                            <useSpringBoot3>true</useSpringBoot3>
                            <skipDefaultInterface>true</skipDefaultInterface>
                        </configOptions>
                        <output>${project.basedir}/target/generated-sources</output>
                    </configuration>
                </execution>
            </executions>
            <dependencies>
                <!-- https://mvnrepository.com/artifact/org.codehaus.plexus/plexus-utils -->
                <dependency>
                    <groupId>org.codehaus.plexus</groupId>
                    <artifactId>plexus-utils</artifactId>
                    <version>4.0.2</version>
                </dependency>
            </dependencies>
        </plugin>

`

@svboettger
Copy link

Sorry, the formatting didn't work so well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants