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.54k stars 6.27k forks source link

[Bug, Java, Restclient]: array-param / body leads to generated API class with missing import for jakarta.validation.Valid #18974

Open hdva2502 opened 1 week ago

hdva2502 commented 1 week ago

Hi folks,

Description

having on openapi file that contains array-types as "root type" which are then translated into List<>-parameters of methods in java classes, the "line type" of the list is annotated with @Valid, however the import statement is missing and in turn the generated class contains a syntax error.

openapi-generator version

7.6.0

OpenAPI declaration file content or url

openapi: 3.0.1
info:
  title: Replay OpenAPI Java Generator RestTemplate Issue
  description: Using array-parameters lead to generated API Client with missing import statement
  version: 1.0.0
servers:
  - url: /v1/api/internal
tags:
  - name: Issue
    description: Issue
paths:
  /postWithArrayParam:
    post:
      tags:
        - Issue
      summary: Post with array-parameter leads to issue
      operationId: postWithArrayParam
      requestBody:
        $ref: '#/components/requestBodies/ArrayBody'
      responses:
        '204':
          $ref: '#/components/responses/no_content_204'

components:
  requestBodies:
    ArrayBody:
      description: Array Body
      required: true
      content:
        application/json:
          schema:
            type: array
            minItems: 0
            items:
              $ref: '#/components/schemas/ArrayBodyItemType'

  responses:
    no_content_204:
      description: No content

  schemas:
    ArrayBodyItemType:
      type: object
      properties:
        scalarType:
            $ref: '#/components/schemas/ScalarType'
    ScalarType:
      description: Scalar Type
      type: string
Command line used for generation

Generated via maven plugin with following execution settings:

                <execution>
                    <id>code-gen-openapi-issue</id>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <configuration>
                        <addCompileSourceRoot>true</addCompileSourceRoot>
                        <modelPackage>your-root-package.rest.openapiissue.model</modelPackage>
                        <apiPackage>your-root-package.rest.openapiissue.api</apiPackage>
                        <inputSpec>${resource.directory}/api/OpenApiIssue.yaml</inputSpec>
                        <generateApiTests>false</generateApiTests>
                        <generateModelTests>false</generateModelTests>
                        <generatorName>java</generatorName>
                        <library>resttemplate</library>
                        <typeMappings>
                            <typeMapping>float=java.math.BigDecimal</typeMapping>
                            <typeMapping>double=java.math.BigDecimal</typeMapping>
                            <typeMapping>Date=java.time.LocalDate</typeMapping>
                        </typeMappings>
                        <configOptions>
                            <dateLibrary>java8</dateLibrary>
                            <fullJavaUtil>false</fullJavaUtil>
                            <performBeanValidation>true</performBeanValidation>
                            <useBeanValidation>true</useBeanValidation>
                            <useTags>true</useTags>
                            <generatedConstructorWithRequiredArgs>false</generatedConstructorWithRequiredArgs>
                            <useJakartaEe>true</useJakartaEe>
                        </configOptions>
                    </configuration>
                </execution>
Steps to reproduce

The generated code will look like this:

package your-root-package.rest.openapiissue.api;

import your-root-package.rest.openapiissue.ApiClient;
import your-root-package.rest.openapiissue.BaseApi;

import your-root-package.rest.openapiissue.model.ArrayBodyItemType;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2024-06-19T12:52:11.651306900+02:00[Europe/Berlin]", comments = "Generator version: 7.6.0")
public class IssueApi extends BaseApi {

    public IssueApi() {
        super(new ApiClient());
    }

    public IssueApi(ApiClient apiClient) {
        super(apiClient);
    }

    /**
     * Post with array-parameter leads to issue
     * 
     * <p><b>204</b> - No content
     * @param arrayBodyItemType Array Body (required)
     * @throws RestClientException if an error occurs while attempting to invoke the API
     */
    public void postWithArrayParam(List<@Valid ArrayBodyItemType> arrayBodyItemType) throws RestClientException {
        postWithArrayParamWithHttpInfo(arrayBodyItemType);
    }

    /**
     * Post with array-parameter leads to issue
     * 
     * <p><b>204</b> - No content
     * @param arrayBodyItemType Array Body (required)
     * @return ResponseEntity&lt;Void&gt;
     * @throws RestClientException if an error occurs while attempting to invoke the API
     */
    public ResponseEntity<Void> postWithArrayParamWithHttpInfo(List<@Valid ArrayBodyItemType> arrayBodyItemType) throws RestClientException {
        Object localVarPostBody = arrayBodyItemType;

        // verify the required parameter 'arrayBodyItemType' is set
        if (arrayBodyItemType == null) {
            throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'arrayBodyItemType' when calling postWithArrayParam");
        }

        final MultiValueMap<String, String> localVarQueryParams = new LinkedMultiValueMap<String, String>();
        final HttpHeaders localVarHeaderParams = new HttpHeaders();
        final MultiValueMap<String, String> localVarCookieParams = new LinkedMultiValueMap<String, String>();
        final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();

        final String[] localVarAccepts = {  };
        final List<MediaType> localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
        final String[] localVarContentTypes = { 
            "application/json"
         };
        final MediaType localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes);

        String[] localVarAuthNames = new String[] {  };

        ParameterizedTypeReference<Void> localReturnType = new ParameterizedTypeReference<Void>() {};
        return apiClient.invokeAPI("/postWithArrayParam", HttpMethod.POST, Collections.<String, Object>emptyMap(), localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, localReturnType);
    }

    @Override
    public <T> ResponseEntity<T> invokeAPI(String url, HttpMethod method, Object request, ParameterizedTypeReference<T> returnType) throws RestClientException {
        String localVarPath = url.replace(apiClient.getBasePath(), "");
        Object localVarPostBody = request;

        final Map<String, Object> uriVariables = new HashMap<String, Object>();
        final MultiValueMap<String, String> localVarQueryParams = new LinkedMultiValueMap<String, String>();
        final HttpHeaders localVarHeaderParams = new HttpHeaders();
        final MultiValueMap<String, String> localVarCookieParams = new LinkedMultiValueMap<String, String>();
        final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();

        final String[] localVarAccepts = {  };
        final List<MediaType> localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
        final String[] localVarContentTypes = { 
            "application/json"
         };
        final MediaType localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes);

        String[] localVarAuthNames = new String[] {  };

        return apiClient.invokeAPI(localVarPath, method, uriVariables, localVarQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType, localVarAuthNames, returnType);
    }
}

The problem is the @Valid-Annotation in e.g. List<@Valid ArrayBodyItemType> arrayBodyItemType which is ok, but there is neither an import statement nor it's fully qualified like @jakarta.annotation.Generated

Please have a look.

Thanks in advance, Andreas

wsdng commented 1 week ago

I have the same problem, in addition to this one, which is quite similar: https://github.com/OpenAPITools/openapi-generator/issues/17617

vanduc2514 commented 1 week ago

@hdva2502 this issue is fixed from master branch