Skip to content

[BUG][java native] querystring Objects toString not checked if null prior #21329

Closed
@duttonw

Description

@duttonw
Description

Native Java client code generated by org.openapitools:openapi-generator-maven-plugin:7.13.0 with optional query params are not null tested prior to being 'toString()' called.

openapi-generator version

7.13.0

OpenAPI declaration file content or url

https://github.com/qld-gov-au/kiteworks-integration/blob/main/kiteworks-swagger-gen/src/main/resources/kiteworks.28.swagger.json

"/rest/folders/{id}/actions/fileBase64Encoded": {
      "post": {
        "tags": [
          "files"
        ],
        "summary": "upload  base64 encoded content",
        "description": "uploads base64 encoded file content to a folder",
        "responses": {
          "422": {
            "description": "Errors\\ERR_ENTITY_LOCKED<br />Errors\\ERR_ENTITY_RESTRICTED_EXTENSION<br />Errors\\ERR_ENTITY_RESTRICTED_EXTENSION_CUSTOM<br />Errors\\ERR_ENTITY_RESTRICTED_TYPE<br />Errors\\ERR_ENTITY_RESTRICTED_TYPE_CUSTOM<br />Errors\\ERR_ENTITY_RESTRICTED_TYPE_GROUP<br />Errors\\ERR_ENTITY_EXISTS"
          },
          "490": {
            "description": "Request blocked by WAF"
          }
        },
        "deprecated": false,
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "description": "ID of the folder",
            "type": "string",
            "required": true
          },
          {
            "in": "body",
            "name": "Body",
            "description": "File information",
            "schema": {
              "$ref": "#/definitions/Content.Post"
            },
            "required": true
          },
          {
            "in": "query",
            "name": "returnEntity",
            "description": "Return information about newly created entity",
            "type": "boolean"
          },
          {
            "in": "query",
            "name": "mode",
            "description": "Response mode",
            "type": "string"
          }
        ]
      }
    },
Generation Details
<plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <version>7.13.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/src/main/resources/kiteworks.28.swagger.json</inputSpec>
                            <generatorName>java</generatorName>
                            <configOptions>
                                <dateLibrary>java8</dateLibrary> <!--java8 - Java 8 native JSR310 (preferred for jdk 1.8+) -->
                                <useJakartaEe>true</useJakartaEe>
                                <useTags>true</useTags>
                                <serializationLibrary>jackson</serializationLibrary>
                            </configOptions>

                            <library>native</library>
                            <apiPackage>com.kiteworks.client.api</apiPackage>
                            <modelPackage>com.kiteworks.client.model</modelPackage>
                            <invokerPackage>com.kiteworks.client</invokerPackage>
                            <cleanupOutput>true</cleanupOutput>
                            <generateApiDocumentation>false</generateApiDocumentation>
                            <generateApiTests>false</generateApiTests>
                            <generateModelDocumentation>false</generateModelDocumentation>
                            <generateModelTests>false</generateModelTests>
<!--                            <configHelp>true</configHelp>-->
                        </configuration>
                    </execution>
                </executions>
            </plugin>

outputs

private HttpRequest.Builder restFoldersIdActionsFilePostRequestBuilder(@jakarta.annotation.Nonnull String id, @jakarta.annotation.Nonnull File body, @jakarta.annotation.Nullable Boolean returnEntity, @jakarta.annotation.Nullable String mode, @jakarta.annotation.Nullable LocalDate clientCreated, @jakarta.annotation.Nullable LocalDate clientModified, @jakarta.annotation.Nullable Boolean disableAutoVersion, @jakarta.annotation.Nullable Boolean note) throws ApiException {
    // verify the required parameter 'id' is set
    if (id == null) {
      throw new ApiException(400, "Missing the required parameter 'id' when calling restFoldersIdActionsFilePost");
    }
    // verify the required parameter 'body' is set
    if (body == null) {
      throw new ApiException(400, "Missing the required parameter 'body' when calling restFoldersIdActionsFilePost");
    }

    HttpRequest.Builder localVarRequestBuilder = HttpRequest.newBuilder();

    String localVarPath = "/rest/folders/{id}/actions/file"
        .replace("{id}", ApiClient.urlEncode(id.toString()));

    List<Pair> localVarQueryParams = new ArrayList<>();
    StringJoiner localVarQueryStringJoiner = new StringJoiner("&");
    String localVarQueryParameterBaseName;
    localVarQueryParameterBaseName = "returnEntity";
    localVarQueryParams.addAll(ApiClient.parameterToPairs("returnEntity", returnEntity));
    localVarQueryParameterBaseName = "mode";
    localVarQueryParams.addAll(ApiClient.parameterToPairs("mode", mode));

    if (!localVarQueryParams.isEmpty() || localVarQueryStringJoiner.length() != 0) {
      StringJoiner queryJoiner = new StringJoiner("&");
      localVarQueryParams.forEach(p -> queryJoiner.add(p.getName() + '=' + p.getValue()));
      if (localVarQueryStringJoiner.length() != 0) {
        queryJoiner.add(localVarQueryStringJoiner.toString());
      }
      localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath + '?' + queryJoiner.toString()));
    } else {
      localVarRequestBuilder.uri(URI.create(memberVarBaseUri + localVarPath));
    }

    localVarRequestBuilder.header("Accept", "application/json");

    MultipartEntityBuilder multiPartBuilder = MultipartEntityBuilder.create();
    boolean hasFiles = false;
    multiPartBuilder.addBinaryBody("body", body);
    hasFiles = true;
    multiPartBuilder.addTextBody("clientCreated", clientCreated.toString());
    multiPartBuilder.addTextBody("clientModified", clientModified.toString());
    multiPartBuilder.addTextBody("disableAutoVersion", disableAutoVersion.toString());
    multiPartBuilder.addTextBody("note", note.toString());
    HttpEntity entity = multiPartBuilder.build();
    HttpRequest.BodyPublisher formDataPublisher;
    if (hasFiles) {
        Pipe pipe;
        try {
            pipe = Pipe.open();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        new Thread(() -> {
            try (OutputStream outputStream = Channels.newOutputStream(pipe.sink())) {
                entity.writeTo(outputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        formDataPublisher = HttpRequest.BodyPublishers.ofInputStream(() -> Channels.newInputStream(pipe.source()));
    } else {
        ByteArrayOutputStream formOutputStream = new ByteArrayOutputStream();
        try {
            entity.writeTo(formOutputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        formDataPublisher = HttpRequest.BodyPublishers
            .ofInputStream(() -> new ByteArrayInputStream(formOutputStream.toByteArray()));
    }
    localVarRequestBuilder
        .header("Content-Type", entity.getContentType().getValue())
        .method("POST", formDataPublisher);
    if (memberVarReadTimeout != null) {
      localVarRequestBuilder.timeout(memberVarReadTimeout);
    }
    if (memberVarInterceptor != null) {
      memberVarInterceptor.accept(localVarRequestBuilder);
    }
    return localVarRequestBuilder;
  }
Steps to reproduce

Issues in generated code are
multiPartBuilder.addTextBody("clientCreated", clientCreated.toString());
multiPartBuilder.addTextBody("clientModified", clientModified.toString());
multiPartBuilder.addTextBody("disableAutoVersion", disableAutoVersion.toString());
multiPartBuilder.addTextBody("note", note.toString());

As inputs are
@jakarta.annotation.Nullable Boolean returnEntity
@jakarta.annotation.Nullable String mode
@jakarta.annotation.Nullable LocalDate clientCreated
@jakarta.annotation.Nullable LocalDate clientModified
@jakarta.annotation.Nullable Boolean disableAutoVersion
@jakarta.annotation.Nullable Boolean note

Related issues/PRs
Suggest a fix

https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/Java/libraries/native/api.mustache#L487C65-L487C84

Generated code when not having a String input should wrap with a
if (Objects.isNonNull(clientCreated)) {
multiPartBuilder.addTextBody("clientCreated", clientCreated.toString());
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions