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

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

Open hdva2502 opened this issue 1 year ago • 2 comments

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

hdva2502 avatar Jun 19 '24 11:06 hdva2502

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

wsdng avatar Jun 20 '24 09:06 wsdng

@hdva2502 this issue is fixed from master branch

vanduc2514 avatar Jun 22 '24 11:06 vanduc2514

@hdva2502 the same issue here ;/

mcruzdev avatar Sep 10 '24 21:09 mcruzdev

@vanduc2514 but seems this fix from master does not included in latest openapi-generator 7.8.0? how its possible?

vnosach-clgx avatar Sep 24 '24 10:09 vnosach-clgx

Funny enough, the issue I reported is gone with 7.8.0.

Thanks a ton, Andreas

hdva2502 avatar Sep 24 '24 12:09 hdva2502

@hdva2502 maybe you had override api.mustache in your project? because in https://repo1.maven.org/maven2/org/openapitools/openapi-generator/7.8.0/ jar contains *openapi-generator-7.8.0.jar\Java\libraries\restclient* without

{{#useBeanValidation}}
import {{javaxPackage}}.validation.constraints.*;
import {{javaxPackage}}.validation.Valid;

{{/useBeanValidation}}

vnosach-clgx avatar Sep 24 '24 12:09 vnosach-clgx