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][jaxrs-spec] Codegen creates invalid code for multipart/form-data for quarkus #7994

Open
M-Landwehr opened this issue Nov 21, 2020 · 7 comments

Comments

@M-Landwehr
Copy link

Description

When creating Java code from a openapi.yml for quakus, the resulting code does not work:

RESTEASY003875: Unable to find a constructor that takes a String param or a valueOf() or fromString() method for javax.ws.rs.FormParam("file") on public abstract void org.openapitools.api.UploadApi.uploadPost(java.io.InputStream,java.lang.String) for basetype: java.io.InputStream

Quarkus cannot handle the geneated method signature of the Rest-resource-class:

@POST
@Consumes({ "multipart/form-data" })
@Produces({ "application/json" })
@ApiOperation(value = "", notes = "", tags={  })
@ApiResponses(value = { 
@ApiResponse(code = 200, message = "", response = Void.class) })
void uploadPost( @FormParam(value = "file") InputStream fileInputStream,@FormParam(value = "name")  String name);

(Additionally, no validation annotations are created!)

openapi-generator version
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>4.3.1</version>
OpenAPI declaration file content or url
openapi: 3.0.1
info:
  title: ''
  description: ''
  version: "1.0"

paths:
  /upload:
    post:
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/MultipartData'
      responses:
        200:
          description: ''
          content:
            application/json: {}
components:
  schemas:
    MultipartData:
      type: object
      properties:
        file:
          format: binary
          type: string
        name:
          pattern: .*
          type: string
Command line used for generation

Quarkus-Version: 1.9.2.Final

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>4.3.1</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${project.basedir}/src/main/resources/META-INF/openapi.yml</inputSpec>
                <generatorName>jaxrs-spec</generatorName>
                <configOptions>
                    <library>quarkus</library>
                    <sourceFolder>src/gen/java/main</sourceFolder>
                    <interfaceOnly>true</interfaceOnly>
                </configOptions>
            </configuration>
        </execution>
    </executions>
</plugin>
Steps to reproduce

Alternatively simply generate java code from the openapi.yml above using the jaxrs-spec generator and the quarkus library

Related issues/PRs
Suggest a fix/enhancement

The model-class is created but is not used in this case. (althogh is has @JsonProperty annotations insteadod the @FormParam annotations

A possible fix would be to

  • For a multipart/form-data request:
@mzellho
Copy link
Contributor

mzellho commented May 5, 2021

hey @M-Landwehr, did you find a way to work around this? we're facing the exact same issue, also tried a couple of other generators but didn't get lucky so far...btw still present in 5.1.0 :-/

@AlbanSeurat
Copy link

Any news on the issue, I am facing the same issue. Any work-around ?

@gigaga
Copy link

gigaga commented Dec 29, 2021

Same for me :(

@fsiegrist
Copy link

Same for me. Tried to use a custom ParamConverter (see https://resteasy.dev/2021/04/05/blog-ParamConverter-with-Quarkus/), without success.

@fsiegrist
Copy link

I "solved" it by modifying the mustache template files.

In api.mustache I added the following two imports:

import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;

In apiInterface.mustache I replaced the last line with
{{#supportAsync}}{{>returnAsyncTypeInterface}}{{/supportAsync}}{{^supportAsync}}{{#returnResponse}}Response{{/returnResponse}}{{^returnResponse}}{{>returnTypeInterface}}{{/returnResponse}}{{/supportAsync}} {{nickname}}({{#isMultipart}}@MultipartForm MultipartFormDataInput multipartFormDataInput{{#allParams}}{{^isFormParam}}, {{/isFormParam}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{/allParams}}{{/isMultipart}}{{^isMultipart}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}{{/isMultipart}});

And in apiMethod.mustache I replaced the third line from the bottom with
public {{#supportAsync}}CompletionStage<{{/supportAsync}}Response{{#supportAsync}}>{{/supportAsync}} {{nickname}}({{#isMultipart}}@MultipartForm MultipartFormDataInput multipartFormDataInput{{#allParams}}{{^isFormParam}}, {{/isFormParam}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{/allParams}}{{/isMultipart}}{{^isMultipart}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}{{/isMultipart}}) {

@Ragin-LundF
Copy link

@fsiegrist
It works with the link you provided. But you need to implement both classes:

package example

import java.io.ByteArrayInputStream
import java.io.InputStream
import java.nio.charset.StandardCharsets
import javax.ws.rs.ext.ParamConverter
import javax.ws.rs.ext.Provider

@Provider
class FileConverterProvider: ParamConverter<InputStream> {
    override fun toString(value: InputStream): String {
        return String(value.readAllBytes(), StandardCharsets.UTF_8)
    }

    override fun fromString(value: String): InputStream {
        return ByteArrayInputStream(value.toByteArray())
    }
}
package example

import java.io.InputStream
import java.lang.reflect.Type
import javax.ws.rs.ext.ParamConverter
import javax.ws.rs.ext.ParamConverterProvider
import javax.ws.rs.ext.Provider


@Provider
class FileParamConverterProvider: ParamConverterProvider {
    override fun <T : Any?> getConverter(
        rawType: Class<T>?,
        genericType: Type?,
        annotations: Array<out Annotation>?
    ): ParamConverter<T>? {
        return if (rawType!!.isAssignableFrom(InputStream::class.java)) {
            (FileConverterProvider() as ParamConverter<T>?)!!
        } else null
    }

}

@vikalpsareen
Copy link

vikalpsareen commented Oct 4, 2022

@fsiegrist thanks for suggesting custom converter, it worked for me below is the java code


@Provider
public class InstantParamConverter implements ParamConverter<InputStream> {

   public InputStream fromString(String value){
      try {
         return  IOUtils.toInputStream(value);
      } catch (Exception e) {

      }
      return null;
   }

   public String toString(InputStream value){
      return value.toString();
   }
}

@Provider
public class InstantParamConverterProvider implements ParamConverterProvider
{


   @Override
   public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations)
   {
      if (rawType.isAssignableFrom(InputStream.class)) {
         return (ParamConverter<T>) new InstantParamConverter();
      }
      return null;
   }
}

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

No branches or pull requests

7 participants