From b51606883bb06a9c4e0c2d0124077a737aef5980 Mon Sep 17 00:00:00 2001 From: Kevin Kaiser Date: Sun, 13 Jun 2021 20:39:12 +0200 Subject: [PATCH] fix: 'Invalid Syntax : offending token ''' when using Apollo persisted queries --- .../execution/ExtensionsDeserializer.java | 23 ++++++++++++++ .../execution/GraphQLObjectMapper.java | 6 ++++ .../kickstart/execution/GraphQLRequest.java | 29 ++++++++++++++++-- .../ObjectMapDeserializationException.java | 12 ++++++++ .../execution/ObjectMapDeserializeHelper.java | 30 +++++++++++++++++++ .../VariablesDeserializationException.java | 12 -------- .../execution/VariablesDeserializer.java | 18 +---------- .../input/GraphQLSingleInvocationInput.java | 1 + .../GraphQLGetInvocationInputParser.java | 11 ++++++- ...GraphQLMultipartInvocationInputParser.java | 9 +++++- 10 files changed, 117 insertions(+), 34 deletions(-) create mode 100644 graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ExtensionsDeserializer.java create mode 100644 graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ObjectMapDeserializationException.java create mode 100644 graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ObjectMapDeserializeHelper.java delete mode 100644 graphql-java-kickstart/src/main/java/graphql/kickstart/execution/VariablesDeserializationException.java diff --git a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ExtensionsDeserializer.java b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ExtensionsDeserializer.java new file mode 100644 index 00000000..abeece2b --- /dev/null +++ b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ExtensionsDeserializer.java @@ -0,0 +1,23 @@ +package graphql.kickstart.execution; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import java.io.IOException; +import java.util.Map; + +public class ExtensionsDeserializer extends JsonDeserializer> { + + public static Map deserializeExtensionsObject( + Object extensions, ObjectCodec codec) { + return ObjectMapDeserializeHelper.deserializeObjectMapObject(extensions, codec, "extensions"); + } + + @Override + public Map deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { + return ExtensionsDeserializer.deserializeExtensionsObject( + p.readValueAs(Object.class), p.getCodec()); + } +} diff --git a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/GraphQLObjectMapper.java b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/GraphQLObjectMapper.java index dbbe2433..9bac536c 100644 --- a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/GraphQLObjectMapper.java +++ b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/GraphQLObjectMapper.java @@ -174,6 +174,12 @@ public Map deserializeVariables(String variables) { getJacksonMapper().readValue(variables, Object.class), getJacksonMapper()); } + @SneakyThrows + public Map deserializeExtensions(String extensions) { + return ExtensionsDeserializer.deserializeExtensionsObject( + getJacksonMapper().readValue(extensions, Object.class), getJacksonMapper()); + } + @SneakyThrows public Map> deserializeMultipartMap(InputStream inputStream) { return getJacksonMapper().readValue(inputStream, MULTIPART_MAP_TYPE_REFERENCE); diff --git a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/GraphQLRequest.java b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/GraphQLRequest.java index 5d4534c2..f5bcd85e 100644 --- a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/GraphQLRequest.java +++ b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/GraphQLRequest.java @@ -17,13 +17,23 @@ public class GraphQLRequest { @JsonDeserialize(using = VariablesDeserializer.class) private Map variables = new HashMap<>(); + @JsonDeserialize(using = ExtensionsDeserializer.class) + private Map extensions = new HashMap<>(); + private String operationName; public GraphQLRequest() {} - public GraphQLRequest(String query, Map variables, String operationName) { + public GraphQLRequest( + String query, + Map variables, + Map extensions, + String operationName) { this.query = query; this.operationName = operationName; + if (extensions != null) { + this.extensions = extensions; + } if (variables != null) { this.variables = variables; } @@ -31,11 +41,14 @@ public GraphQLRequest(String query, Map variables, String operat public static GraphQLRequest createIntrospectionRequest() { return new GraphQLRequest( - IntrospectionQuery.INTROSPECTION_QUERY, new HashMap<>(), "IntrospectionQuery"); + IntrospectionQuery.INTROSPECTION_QUERY, + new HashMap<>(), + new HashMap<>(), + "IntrospectionQuery"); } public static GraphQLRequest createQueryOnlyRequest(String query) { - return new GraphQLRequest(query, new HashMap<>(), null); + return new GraphQLRequest(query, new HashMap<>(), new HashMap<>(), null); } public String getQuery() { @@ -56,6 +69,16 @@ public void setVariables(Map variables) { } } + public Map getExtensions() { + return extensions; + } + + public void setExtensions(Map extensions) { + if (extensions != null) { + this.extensions = extensions; + } + } + public String getOperationName() { return extractOperationName(query, operationName, null); } diff --git a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ObjectMapDeserializationException.java b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ObjectMapDeserializationException.java new file mode 100644 index 00000000..281f9778 --- /dev/null +++ b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ObjectMapDeserializationException.java @@ -0,0 +1,12 @@ +package graphql.kickstart.execution; + +public class ObjectMapDeserializationException extends RuntimeException { + + ObjectMapDeserializationException(String message) { + super(message); + } + + ObjectMapDeserializationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ObjectMapDeserializeHelper.java b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ObjectMapDeserializeHelper.java new file mode 100644 index 00000000..135c88b1 --- /dev/null +++ b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/ObjectMapDeserializeHelper.java @@ -0,0 +1,30 @@ +package graphql.kickstart.execution; + +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.core.type.TypeReference; +import java.io.IOException; +import java.util.Map; + +public class ObjectMapDeserializeHelper { + + public static Map deserializeObjectMapObject( + Object object, ObjectCodec codec, String fieldName) { + if (object instanceof Map) { + @SuppressWarnings("unchecked") + Map genericObjectMap = (Map) object; + return genericObjectMap; + } else if (object instanceof String) { + try { + return codec.readValue( + codec.getFactory().createParser((String) object), + new TypeReference>() {}); + } catch (IOException e) { + throw new ObjectMapDeserializationException( + String.format("Cannot deserialize field '%s'", fieldName), e); + } + } else { + throw new ObjectMapDeserializationException( + String.format("Field '%s' should be either an object or a string", fieldName)); + } + } +} diff --git a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/VariablesDeserializationException.java b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/VariablesDeserializationException.java deleted file mode 100644 index 46b225ab..00000000 --- a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/VariablesDeserializationException.java +++ /dev/null @@ -1,12 +0,0 @@ -package graphql.kickstart.execution; - -public class VariablesDeserializationException extends RuntimeException { - - VariablesDeserializationException(String message) { - super(message); - } - - VariablesDeserializationException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/VariablesDeserializer.java b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/VariablesDeserializer.java index d69c0c72..a347a52f 100644 --- a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/VariablesDeserializer.java +++ b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/VariablesDeserializer.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import java.io.IOException; @@ -13,22 +12,7 @@ public class VariablesDeserializer extends JsonDeserializer> public static Map deserializeVariablesObject( Object variables, ObjectCodec codec) { - if (variables instanceof Map) { - @SuppressWarnings("unchecked") - Map genericVariables = (Map) variables; - return genericVariables; - } else if (variables instanceof String) { - try { - return codec.readValue( - codec.getFactory().createParser((String) variables), - new TypeReference>() {}); - } catch (IOException e) { - throw new VariablesDeserializationException("Cannot deserialize variables", e); - } - } else { - throw new VariablesDeserializationException( - "Variables should be either an object or a string"); - } + return ObjectMapDeserializeHelper.deserializeObjectMapObject(variables, codec, "variables"); } @Override diff --git a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/input/GraphQLSingleInvocationInput.java b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/input/GraphQLSingleInvocationInput.java index 6f629ddb..1acff407 100644 --- a/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/input/GraphQLSingleInvocationInput.java +++ b/graphql-java-kickstart/src/main/java/graphql/kickstart/execution/input/GraphQLSingleInvocationInput.java @@ -45,6 +45,7 @@ private ExecutionInput createExecutionInput( .context(context) .root(root) .variables(graphQLRequest.getVariables()) + .extensions(graphQLRequest.getExtensions()) .dataLoaderRegistry(context.getDataLoaderRegistry()) .executionId(ExecutionId.generate()) .build(); diff --git a/graphql-java-servlet/src/main/java/graphql/kickstart/servlet/GraphQLGetInvocationInputParser.java b/graphql-java-servlet/src/main/java/graphql/kickstart/servlet/GraphQLGetInvocationInputParser.java index 6d45b6a1..6c8c7464 100644 --- a/graphql-java-servlet/src/main/java/graphql/kickstart/servlet/GraphQLGetInvocationInputParser.java +++ b/graphql-java-servlet/src/main/java/graphql/kickstart/servlet/GraphQLGetInvocationInputParser.java @@ -39,8 +39,10 @@ public GraphQLInvocationInput getGraphQLInvocationInput( if (isSingleQuery(query)) { Map variables = getVariables(request); + Map extensions = getExtensions(request); String operationName = request.getParameter("operationName"); - GraphQLRequest graphqlRequest = new GraphQLRequest(query, variables, operationName); + GraphQLRequest graphqlRequest = + new GraphQLRequest(query, variables, extensions, operationName); return invocationInputFactory.createReadOnly(graphqlRequest, request, response); } @@ -61,4 +63,11 @@ private Map getVariables(HttpServletRequest request) { .map(HashMap::new) .orElseGet(HashMap::new); } + + private Map getExtensions(HttpServletRequest request) { + return Optional.ofNullable(request.getParameter("extensions")) + .map(graphQLObjectMapper::deserializeExtensions) + .map(HashMap::new) + .orElseGet(HashMap::new); + } } diff --git a/graphql-java-servlet/src/main/java/graphql/kickstart/servlet/GraphQLMultipartInvocationInputParser.java b/graphql-java-servlet/src/main/java/graphql/kickstart/servlet/GraphQLMultipartInvocationInputParser.java index 4dd9a114..ea59eea1 100644 --- a/graphql-java-servlet/src/main/java/graphql/kickstart/servlet/GraphQLMultipartInvocationInputParser.java +++ b/graphql-java-servlet/src/main/java/graphql/kickstart/servlet/GraphQLMultipartInvocationInputParser.java @@ -150,13 +150,20 @@ private GraphQLRequest buildRequestFromQuery( graphQLObjectMapper.deserializeVariables(read(variablesItem.get().getInputStream())); } + Map extensions = null; + final Optional extensionsItem = getPart(parts, "extensions"); + if (extensionsItem.isPresent()) { + extensions = + graphQLObjectMapper.deserializeExtensions(read(extensionsItem.get().getInputStream())); + } + String operationName = null; final Optional operationNameItem = getPart(parts, "operationName"); if (operationNameItem.isPresent()) { operationName = read(operationNameItem.get().getInputStream()).trim(); } - return new GraphQLRequest(query, variables, operationName); + return new GraphQLRequest(query, variables, extensions, operationName); } private String read(InputStream inputStream) throws IOException {