graphql-spring-boot icon indicating copy to clipboard operation
graphql-spring-boot copied to clipboard

Using Upload scalar doesn't work with input object

Open ivans opened this issue 4 years ago • 10 comments

File upload works great if Upload is a direct input parameter of a query or nested in an array, but fails if it is nested.

Here are the examples to demonstrate:

with this in schema:

type Query {
    test1(input: Upload): ID!
    test2(input: [Upload]): ID!
    test3(input: FileInput): ID!
    test4(input: [FileInput]): ID!
}

scalar Upload

input FileInput {
    name: String!
    file: Upload
}

FileInput is mapped to this java class:

@lombok.Data
@lombok.NoArgsConstructor
public class FileInputDTO {

  String name;
  
  Part file;
}

The first two queries work great when called like this:

curl --location --request POST 'http://localhost:8080/graphql' \
--form 'operations={ "query": "query test1query($input: Upload) {test1 (input: $input) }", "variables": {"input": null } } }' \
--form 'map={ "file0": ["variables.input"] }' \
--form 'file0=@/home/ivan/Downloads/2020-02-13-raspbian-buster-full.zip'

curl --location --request POST 'http://localhost:8080/graphql' \
--form 'operations={ "query": "query test2query($input: [Upload]) {test2 (input: $input) }", "variables": {"input": [null, null] } } }' \
--form 'map={ "file0": ["variables.input.0"], "file1": ["variables.input.1"] }' \
--form 'file0=@/home/ivan/Slike/GOPR6667.MP4' \
--form 'file1=@/home/ivan/Slike/GOPR6665.MP4'

But the last two (with Upload contained in another input type fail:

# called this way:
curl --location --request POST 'http://localhost:8080/graphql' \
--form 'operations={ "query": "query test3query($input: FileInput) {test3 (input: $input) }", "variables": {"input": {"name": "file0.txt", "file": null } } }' \
--form 'map={ "file0": ["variables.input.file"] }' \
--form 'file0=@/home/ivan/Slike/s gopro/GOPR6667.MP4'

The exception that is thrown:

java.lang.IllegalArgumentException: Cannot construct instance of `javax.servlet.http.Part` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: hr.verox.engeon.model.dto.FileInputDTO["file"])
	at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3922) ~[jackson-databind-2.10.1.jar:2.10.1]
	at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:3863) ~[jackson-databind-2.10.1.jar:2.10.1]
	at graphql.kickstart.tools.MethodFieldResolver$createDataFetcher$$inlined$forEachIndexed$lambda$1.invoke(MethodFieldResolver.kt:100) ~[graphql-java-tools-6.0.2.jar:na]
	at graphql.kickstart.tools.MethodFieldResolver$createDataFetcher$$inlined$forEachIndexed$lambda$1.invoke(MethodFieldResolver.kt:25) ~[graphql-java-tools-6.0.2.jar:na]
	at graphql.kickstart.tools.MethodFieldResolverDataFetcher.get(MethodFieldResolver.kt:201) ~[graphql-java-tools-6.0.2.jar:na]
	at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:272) ~[graphql-java-14.0.jar:na]
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:200) ~[graphql-java-14.0.jar:na]
	at graphql.execution.AsyncExecutionStrategy.execute(AsyncExecutionStrategy.java:74) ~[graphql-java-14.0.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:165) ~[graphql-java-14.0.jar:na]
	at graphql.execution.Execution.execute(Execution.java:106) ~[graphql-java-14.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:623) ~[graphql-java-14.0.jar:na]
	at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:556) ~[graphql-java-14.0.jar:na]
	at graphql.GraphQL.executeAsync(GraphQL.java:520) ~[graphql-java-14.0.jar:na]
	at graphql.kickstart.execution.GraphQLInvoker.executeAsync(GraphQLInvoker.java:26) ~[graphql-java-kickstart-9.1.0.jar:na]
	at graphql.kickstart.execution.GraphQLInvoker.query(GraphQLInvoker.java:38) ~[graphql-java-kickstart-9.1.0.jar:na]
	at graphql.kickstart.execution.GraphQLInvoker.query(GraphQLInvoker.java:31) ~[graphql-java-kickstart-9.1.0.jar:na]
	at graphql.kickstart.servlet.HttpRequestHandlerImpl.invoke(HttpRequestHandlerImpl.java:67) ~[graphql-java-servlet-9.1.0.jar:na]
	at graphql.kickstart.servlet.HttpRequestHandlerImpl.execute(HttpRequestHandlerImpl.java:52) ~[graphql-java-servlet-9.1.0.jar:na]
	at graphql.kickstart.servlet.HttpRequestHandlerImpl.handle(HttpRequestHandlerImpl.java:37) ~[graphql-java-servlet-9.1.0.jar:na]
	at graphql.kickstart.servlet.AbstractGraphQLHttpServlet.doRequest(AbstractGraphQLHttpServlet.java:147) ~[graphql-java-servlet-9.1.0.jar:na]
	at graphql.kickstart.servlet.AbstractGraphQLHttpServlet.doRequestAsync(AbstractGraphQLHttpServlet.java:137) ~[graphql-java-servlet-9.1.0.jar:na]
	at graphql.kickstart.servlet.AbstractGraphQLHttpServlet.doPost(AbstractGraphQLHttpServlet.java:169) ~[graphql-java-servlet-9.1.0.jar:na]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.29.jar:9.0.29]

ivans avatar Apr 27 '20 07:04 ivans