Giter Club home page Giter Club logo

graphql-java-servlet's Introduction

GraphQL Java Servlet

Maven Central Build Status Quality Gate Status GitHub contributors Discuss on GitHub

We are looking for contributors!

Are you interested in improving our documentation, working on the codebase, reviewing PRs?

Reach out to us on Discussions and join the team!

We hope you'll get involved! Read our Contributors' Guide for more details.

Overview

Implementation of GraphQL Java Servlet including support for Relay.js, Apollo and OSGi out of the box. This project wraps the Java implementation of GraphQL provided by GraphQL Java. See GraphQL Java documentation for more in depth details regarding GraphQL Java itself.

We try to stay up to date with GraphQL Java as much as possible maintaining the retro-compatibility with javax and Springframework 5.

On each release we publish three flavours of this project:

All of them also supports legacy projects that can compile with older JDK versions: the minimum JDK version supported is the 11.

Jakarta and Springframework 6.*

This is the main flavour using the latest version of Jakarta (currently the 6.*) and the latest version of Springframework (currently the 6.*). All the codebase can be found in the branch: master

<dependency>
    <groupId>com.graphql-java-kickstart</groupId>
    <artifactId>graphql-java-servlet</artifactId>
    <version>${graphql-java-servlet.version}</version>
</dependency>

Jakarta5

This flavour use the jakarta version 5.* and it is meant to be used for all the projects that are already migrated to jakarta, but they are waiting that jakarta6 will become more broadly used. All the codebase can be found in the branch: jakarta5

<dependency>
    <groupId>com.graphql-java-kickstart</groupId>
    <artifactId>graphql-java-servlet-jakarta5</artifactId>
    <version>${graphql-java-servlet-jakarta5.version}</version>
</dependency>

Javax and Springframework 5.*

This is the legacy flavour using the javax dependency and the version 5.* of Springframework (since it is still broadly used by a lot of projects). All the codebase can be found in the branch: master

<dependency>
    <groupId>com.graphql-java-kickstart</groupId>
    <artifactId>graphql-java-servlet-javax</artifactId>
    <version>${graphql-java-servlet.version}</version>
</dependency>

Installation and getting started

See Getting started for more detailed instructions.

graphql-java-servlet's People

Contributors

actions-user avatar apottere avatar artem-ag avatar benfortuna avatar bsara avatar denvned avatar dependabot[bot] avatar donbeave avatar eduarddrenth avatar federicorispo avatar fliptaboada avatar guilhermeblanco avatar heowc avatar jacobmountain avatar jmccaull avatar jplock avatar ma2gedev avatar mlvandijk avatar oliemansm avatar olim7t avatar osi avatar renovate-bot avatar renovate[bot] avatar ronald-d-rogers avatar sammyhk avatar sergehuber avatar setchy avatar tomqwpl avatar vojtapol avatar yrashk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-java-servlet's Issues

GraphQLServlet doesn't play nice with custom DataFetchers

The GraphQLServlet does not allow proper use of the 'context' item that is needed for custom DataFetchers. This (executable) example illustrates the problem:

import java.util.Arrays;
import java.util.List;

import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.Scalars;
import graphql.schema.GraphQLList;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;

public class GraphQLTest {
    public static void main(String[] args) {
        try {
            // build schema
            GraphQLObjectType personType = GraphQLObjectType.newObject()
                    .name("Person")
                    .field(f -> f.name("name")
                                 .type(Scalars.GraphQLString)
                                 .dataFetcher(e -> ((Person) e.getSource()).name))
                    .field(f -> f.name("age")
                                 .type(Scalars.GraphQLInt)
                                 .dataFetcher(e -> ((Person) e.getSource()).age))
                    .build();

            GraphQLObjectType teamType = GraphQLObjectType.newObject()
                    .name("Team")
                    .field(f -> f.name("people")
                                 .type(new GraphQLList(personType))
                                 .dataFetcher(e -> ((Team) e.getSource()).people))
                    .build();

            GraphQLSchema schema = GraphQLSchema.newSchema()
                    .query(teamType).build();

            // create our 'database' that implements the schema
            Team team = new Team(Arrays.asList(
                    new Person("Alice", 24), new Person("Bob", 45), 
                    new Person("Carol", 33), new Person("David", 28)));

            // run the query
            ExecutionResult result = new GraphQL(schema)
                    .execute("{ people { name, age } }", team);
            System.out.println(result.getData().toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static class Person {
        public final String name;
        public final int age;

        public Person(String name, int age) { this.name = name; this.age = age; }
    }

    private static class Team {
        public final List<Person> people;

        public Team(List<Person> people) { this.people = people; }
    }
}

This is an idiomatic way to use data fetchers.

The problem is that the graphql-java-servlet implementation provides no way to provide the 'context' item to the execute method ('team' in this case). Indeed looking at the source code it seems that the 'context' parameter to execute is being used to pass something else (a GraphQLContext). This will not work with custom DataFetchers that make use of getSource (as most do).

In the above example, the GraphQLServlet implementation effectively does this:

ExecutionResult result = new GraphQL(schema).execute("{ people { name, age } }", new GraphQLContext(...));

Which will crash:

java.lang.ClassCastException: graphql.servlet.GraphQLContext cannot be cast to GraphQLTest$Team

on this line:

.dataFetcher(e -> ((Team) e.getSource()).people))

Servlet API 3.0 multipart support does not work for content-type `application/json`

@nrouge, @ieugen I have been trying to test the changes for multipart in the last commit - 387ac86. I am testing with Tomcat and a request with content-type as application/json. I cant get the request.getParts() to work, I get InvalidContentTypeException.

javax.servlet.ServletException: org.apache.tomcat.util.http.fileupload.FileUploadBase$InvalidContentTypeException: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is application/json;charset=UTF-8
	at org.apache.catalina.connector.Request.parseParts(Request.java:2909) ~[tomcat-embed-core-8.5.28.jar:8.5.28]
	at org.apache.catalina.connector.Request.getParts(Request.java:2777) ~[tomcat-embed-core-8.5.28.jar:8.5.28]
	at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1084) ~[tomcat-embed-core-8.5.28.jar:8.5.28]
	at graphql.servlet.GraphQLServlet.lambda$new$2(GraphQLServlet.java:129) ~[graphql-java-servlet-5.0.2.jar:na]
	at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.java:261) ~[graphql-java-servlet-5.0.2.jar:na]
	at graphql.servlet.GraphQLServlet.doPost(GraphQLServlet.java:279) ~[graphql-java-servlet-5.0.2.jar:na]

This is after the allowCasualMultipartParsing in Catalina context is enabled. Without that setting the exception is:

java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided
	at org.apache.catalina.connector.Request.parseParts(Request.java:2811) ~[tomcat-embed-core-8.5.28.jar:8.5.28]
	at org.apache.catalina.connector.Request.getParts(Request.java:2777) ~[tomcat-embed-core-8.5.28.jar:8.5.28]
	at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1084) ~[tomcat-embed-core-8.5.28.jar:8.5.28]
	at graphql.servlet.GraphQLServlet.lambda$new$2(GraphQLServlet.java:129) ~[graphql-java-servlet-5.0.2.jar:na]
	at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.java:261) ~[graphql-java-servlet-5.0.2.jar:na]
	at graphql.servlet.GraphQLServlet.doPost(GraphQLServlet.java:279) ~[graphql-java-servlet-5.0.2.jar:na]

Request-scoped DataLoader

I am trying to use DataLoaders. If I understand it correctly:

  • it is recommended to create new DataLoaderRegistry for each user request (to prevent mixing caches for different users)
  • DataLoaderRegistry can be stored in custom context class which is instantiated in GraphQLServlet.createContext (in override or using GraphQLContextBuilder)
  • in resolvers it is possible to get DataLoaderRegistry from this context
  • DataLoaderDispatcherInstrumentation must be used so that dispatch methods are called

but here is the problem: DataLoaderDispatcherInstrumentation has DataLoaderRegistry and it is not request-scoped.

I haven't find a way to have request-scoped DataLoaderRegistry so I would like to improve the servlet to allow this use case. Or am I missing something?

Methods createContext and getInstrumentation need to somehow share new DataLoaderRegistry (per-request) instance. One solution would be to add methods getInstrumentation with GraphQLContext parameter, delegate these methods to their parameter-less versions and call this new method from newGraphQL method.

If you think this would be useful I can create PR (I already tried it). Or is there a better way?

Exceptions while fetching are double-logged

If a data fetcher throws an exception, it is logged both by the ExecutionStrategy and the GraphQLServlet. The first (in graphql-java) is INFO, while the latter is ERROR.

I think the servlet should rely on the back end to do the logging instead of doing it again.

Unable to set status code

Happy to make a pull request with the below solution, let me know your thoughts...

I'm using this with the spring-boot starter and would like to make use of @ResponseStatus on my exceptions to set the status code.

The only thing from preventing this to work nicely is being unable to set the status code programatically.

TL;DR - I suggest changing GraphQLServlet#query as such:

GraphQLResponse graphQLResponse = new GraphQLResponse();
graphQLResponse.setStatus(STATUS_OK);
graphQLResponse.setResponse(response);

if(getGraphQLErrorHandler().errorsPresent(errors)) {
    // note graphQLResponse is passed to the callback
    runCallbacks(operationCallbacks, c -> c.onError(context, operationName, query, variables, data, errors, graphQLResponse));
} else {
    // note graphQLResponse is passed to the callback
    runCallbacks(operationCallbacks, c -> c.onSuccess(context, operationName, query, variables, data, graphQLResponse));
}

responseHandler.handle(graphQLResponse);

This would allow GraphQLServletListener.OperationCallback instances to be registered to change the status code before it's written to the underlying response.


What I've already tried

The response is hardcoded to STATUS_OK and the only other place you can get access to both the response and throwable objects is in the GraphQLServletListener.RequestCallback.onError(...) method, however I can't see a way of throwing an exception up to GraphQLServlet.doRequest(...). I tried implementing an GraphQLServletListener.OperationCallback but these are wrapped in a silenced try/catch.

The only place to throw would be in the GraphQLErrorHandler.processErrors(...), but I want to keep the graphql error JSON format and throwing an exception here would prevent the response body being written.

I suggest the GraphQLServletListener.OperationCallback methods should get access to the GraphQLResponse object before being handled by the GraphQLServlet.GraphQLResponseHandler parameter.

Query Batching

Using GraphQL-Servlet 4.4

I am getting the following exception when using batching with the Apollo client:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Can not deserialize instance of graphql.servlet.GraphQLServlet$GraphQLRequest out of START_ARRAY token
at [Source: (org.apache.catalina.connector.CoyoteInputStream); line: 1, column: 1]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:62) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1307) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1116) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1070) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromArray(BeanDeserializerBase.java:1440) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:185) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1613) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1183) ~[jackson-databind-2.9.0.pr4.jar:2.9.0.pr4]
at graphql.servlet.GraphQLServlet.lambda$new$1(GraphQLServlet.java:163) ~[graphql-java-servlet-4.4.0.jar:na]
at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.java:229) ~[graphql-java-servlet-4.4.0.jar:na]
at graphql.servlet.GraphQLServlet.doPost(GraphQLServlet.java:247) ~[graphql-java-servlet-4.4.0.jar:na]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.16.jar:8.5.16]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.16.jar:8.5.16]

..snip..

Is query batching supported?

Support for Preparsed queries caching

Hi,
This may be rather a question then an issue. But just in case you guys decide to push it forward to implementation.

I am looking forward to the Preparsed queries issue from graphql-java project been released.
Are there any plans to provide support to that feature by graphql-java-servlet? Or could you please throw some light at how I may implement preparsed query caching myself without any specific support?

Split project into different modules (to re-use common classes but don't depend on javax.servlet)

While working on a configuration module for Micronaut I found many patterns and supporting classes in the graphql-java-servlet project useful.

It would be nice if these classes could me moved to a "common http"-module supporting the "servlet"-module itself but could be used by other projects as well, like the Micronaut modue.

This common http module shoud not have any dependency to javax.servlet.

I think basically everything except the *Servlet classes could be useful in the common module.
And maybe even some stuff inside the *Servlet classes could be extracted from. I think the job for the servlet classes would to extract the query, operation name, variables, body etc. and pass that to a query invoker.

What do you think?

RequestCallback and OperationCallback no before method

Quote README.md

Servlet Listeners
You can also add servlet listeners to an existing servlet. These listeners provide hooks into query execution (before, success, failure, and finally) and servlet execution (before, success, error, and finally):

but RequestCallback and OperationCallback no before method

Maven dependency scope

I'm using graphql-java-servlet in a Maven project, and it breaks compilation since its dependencies are runtime scoped instead of compile scoped. Is this intended?

Weblogic 12.2.1 and GraphQL Java Servlet

Hello I'm trying to deploy a GraphQL servlet version 4.7.0 within a Spring MVC war that will be deployed onto Weblogic 12.2.1 and whenever I try and do a request with a content type of application-json I get the following error.

Bad POST request: parsing failed com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input
at [Source: weblogic.servlet.internal.ServletInputStreamImpl@14b82135; line: 1, column: 0]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:255)
at com.fasterxml.jackson.databind.ObjectReader._initForReading(ObjectReader.java:361)
at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1561)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:1166)
at graphql.servlet.GraphQLServlet.lambda$new$1(GraphQLServlet.java:197)
at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.java:255)

I think I have tracked the problem down to the post handler, specifically the part that deals with requests that are not multipart requests (starts on line 188 in the version I have). This code checks that marking is supported by the input stream and if it isn't wraps it in a BufferedInputStream so that the isBatchedQuery method can "peek" ahead at the request body and then reset back to the start of the input for the parser.

The input stream class at this stage is an implementation of ServletInputStream which is implemented different for different application containers. Most containers, like Tomcat, do not implement marking on their implementation so it will be wrapped by a BufferedInputStream. Unfortunately Weblogic does implement these methods on it's version and so will not be wrapped.

This would not cause a problem apart from the line of code where the input stream is marked, as shown below, in the isBatchedQuery method (line 449).

inputStream.mark(0);

According to the Java API the parameter (in this case zero) indicates the maximum number of bytes that can be read before the mark becomes invalid. So technically this is saying that the mark becomes invalid the moment any data is read from it. This doesn't cause an issue with BufferedInputStreams as this parameter is ignored (in fact it seems to be ignored by a lot of implementations I have seen) but the weblogic implementation does use the value passed to it. This means that when reset is called at the end of the isBatchedQuery method the input stream is not actually reset and the parsing then fails. In my case I get a no content error because there is less than 128 characters in the body so the parser gets no input.

The quick solution as far as I can see is to rewrite the method as shown in the snippet below. The difference being that we set the maximum number of characters for the mark to equal the size of the buffer we are about to read in. In that way the reset method will be able to do it's job and the parser will find the expected input.

        ByteArrayOutputStream result = new ByteArrayOutputStream();
        byte[] buffer = new byte[128];
        int length;

        inputStream.mark(128);
        while ((length = inputStream.read(buffer)) != -1) {
            result.write(buffer, 0, length);
            String chunk = result.toString();

Does this seem like a reasonable solution to the problem I'm having or have I missed something? I can create a pull request for this (although I'm away for the next week) but I thought it best to raise an issue first and ensure that this is the best approach.

error handling in custom scalar type deserialization

I'm reissuing this from @rolandkozma as I'm facing the very same problem and it seems more related to this project (I've also posted it on graphql-java):

https://github.com/graphql-java/graphql-java-tools/issues/81

If an exception happens to be thrown while handling deserialization of a scalar type in:
I parseValue(Object input); or I parseLiteral(Object input);
methods from the Coercing<I, O> interface,
it is not handled and the output of the query will be an empty String, as "".

Going on the flow I found that in graphql.GraphQL.executeAsync(ExecutionInput) there is a catch that catches AbortExecutionException so I made the exception that I throw at scalar deserialization to extend AbortExecutionException in order to be caught. With this fix, the exception will be passed in my custom GraphQLErrorHandler and something meaningful will be displayed.
However there is no way to collect the path/name of the field that failed to be deserialized and it would be great to have one.
I'm not sure, but I think that exceptions like CoercingParseValueException may be considered clientErrors and automatically handled by the framework. What do you think?

Thanks,
Roland

graphql-java 4.1 compatibility

Servlet project is completely broken for graphql-java 4.1.

Caused by: java.lang.NoClassDefFoundError: graphql/execution/TypeInfo
	at graphql.servlet.GraphQLServlet.<clinit>(GraphQLServlet.java:78) ~[graphql-java-servlet-4.2.1.jar:na]

And the graphql.servlet.DefaultExecutionStrategyProvider cannot find:

Caused by: java.lang.ClassNotFoundException: graphql.execution.SimpleExecutionStrategy

For me it is working again with the following changes: soudmaijer@767e318

But there is a failing test, I have no clue why that one is failing (yet).

A way to send an 401 - Unauthorized HTTP response

I want to use the GraphQL for authentication mechanism also, using mutations

I was following the How to GraphQL - Backend Java tutorial and in the createContext method:

@Override
protected GraphQLContext createContext(Optional<HttpServletRequest> request, Optional<HttpServletResponse> response) {
// ...
}
  • I need to be able to check the token (it may be expired or a wrong token provided) and return a custom status code, like 401 - Unauthorized so we can redirect the user in the web to a login page eventually. At the moment, if I throw an Exception in that method, I will get a 500 - Server Error and this is not explicit enough. Is there any way to do that?

  • How can I check the mutation name in order to exclude signin calls or some public mutations (mutations calls that does not require an Authorization header)

Failed to instantiate SimpleGraphQservlet with arguments in WebLogic server

Hi,

When running this servlet in WebLogic server, the servlet container is trying to create new servlet instance by invoking the no-arg constructor and throws exception:

<Oct 2, 2017 1:51:28 PM SGT> <[ServletContext@933807824[app:CPRES module:CPRES path:null spec-version:3.1]] Error occurred while instantiating servlet: "simpleGraphQLServlet".
java.lang.InstantiationException: graphql.servlet.SimpleGraphQLServlet
at java.lang.Class.newInstance(Class.java:427)
at weblogic.servlet.internal.WebComponentContributor.getNewInstance(WebComponentContributor.java:252)
at weblogic.servlet.internal.WebComponentContributor.getNewInstance(WebComponentContributor.java:245)
at weblogic.servlet.internal.WebComponentContributor.createServletInstance(WebComponentContributor.java:274)
at weblogic.servlet.internal.StubSecurityHelper$ServletInitAction.newServletInstanceIfNecessary(StubSecurityHelper.java:365)
Truncated. see log file for complete stacktrace
Caused By: java.lang.NoSuchMethodException: graphql.servlet.SimpleGraphQLServlet.()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
at weblogic.servlet.internal.WebComponentContributor.getNewInstance(WebComponentContributor.java:252)
at weblogic.servlet.internal.WebComponentContributor.getNewInstance(WebComponentContributor.java:245)
at weblogic.servlet.internal.WebComponentContributor.createServletInstance(WebComponentContributor.java:274)
Truncated. see log file for complete stacktrace

<Oct 2, 2017 1:51:28 PM SGT> <Servlet: "simpleGraphQLServlet" failed to preload on startup in Web application: "CPRES".
javax.servlet.ServletException: Servlet class: 'graphql.servlet.SimpleGraphQLServlet' couldn't be instantiated
at weblogic.servlet.internal.StubSecurityHelper$ServletInitAction.run(StubSecurityHelper.java:326)
at weblogic.servlet.internal.StubSecurityHelper$ServletInitAction.run(StubSecurityHelper.java:294)
at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:326)
at weblogic.security.service.SecurityManager.runAsForUserCode(SecurityManager.java:196)
at weblogic.servlet.provider.WlsSecurityProvider.runAsForUserCode(WlsSecurityProvider.java:203)
Truncated. see log file for complete stacktrace

<Oct 2, 2017 1:51:29 PM SGT> <Failure occurred in the execution of deployment request with ID "19094957742917053" for task "130" on [partition-name: DOMAIN]. Error is: "weblogic.application.ModuleException: javax.servlet.ServletException: Servlet class: 'graphql.servlet.SimpleGraphQLServlet' couldn't be instantiated"
weblogic.application.ModuleException: javax.servlet.ServletException: Servlet class: 'graphql.servlet.SimpleGraphQLServlet' couldn't be instantiated

Enhance ObjectMapper with modules (JavaTimeModule)

Can you please add a possibility to customize the ObjectMapper used by GraphQL Servlet. We need to register JavaTimeModule.

public abstract class GraphQLServlet extends HttpServlet implements Servlet, GraphQLMBean {
...
    private static final ObjectMapper mapper = new ObjectMapper();
...

Apollo keepalive incorrect implementation

Hello!
The new subscription feature implementation is really good, unfortunately realisation of apollo protocol is not aligned with the specification.

In the specification said, that if server sends GQL_CONNECTION_KEEP_ALIVE once, then it should send it periodically.
https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md#gql_connection_keep_alive

Otherwise, client will close current connection and create new one each 30s(according to this logic https://github.com/apollographql/subscriptions-transport-ws/blob/f95a363fb63df014d16e1333e607fe92520ebfd1/src/client.ts#L515)
image

So my proposal is to make it configurable. If keepalive is enabled, then it should be sent to client periodically(should be configurable in properties). If keepalive is disabled, then it shouldn't be send

Thanks

Error serializing requests with extensions (Apollo client 2.0)

The 2.0 version of apollo client's GraphQL requests can contain the property extensions which causes it to fail to deserialize to GraphQLServlet$GraphQLRequest because it is not also defined there.

I guess the best thing to do would be to simply ignore any unknown JSON properties.

I will go ahead and create a PR for it.

GraphQLJavaServlet

What is the replacement for SimpleGraphQLServlet (along with the required parameters to the constructor) in the latest release?

GraphiQL JsonMappingException

Querying with GraphiQL I get this exception:

Error executing GraphQL request!: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.util.LinkedHashMap: no String-argument constructor/factory method to deserialize from String value ('{}')
 at [Source: "{}"; line: 1, column: 1]
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
	at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1456)
	at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1012)
	at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:370)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:315)
	at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:355)
	at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:27)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2861)
	at graphql.servlet.GraphQLServlet.lambda$new$0(GraphQLServlet.java:108)
	at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.java:224)
	at graphql.servlet.GraphQLServlet.doGet(GraphQLServlet.java:237)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
        ...

It seems like variables are an empty JSON object {} and it fails parsing, isn't it?

Cannot Resolve Symbol SimpleGraphQLServlet

I must be missing something quite simple. I am attempting to follow the tutorial here which would like me to extend SimpleGraphQLServlet. When I try to import this class I get a cannot resolve symbol error though.

This is strange as some of the examples in the readme make use of this class.

My mvn dependency looks like this...

<dependency>
      <groupId>com.graphql-java</groupId>
      <artifactId>graphql-java-servlet</artifactId>
      <version>6.0.0</version>
</dependency>

and I have no trouble importing classes like SimpleGraphQLHttpServlet.

Include extensions in response.

I'd like to include Apollo Tracing information in my reply, as documented here:
http://graphql-java.readthedocs.io/en/v6/instrumentation.html#apollo-tracing-instrumentation

It all works and I can see the data being available in the ExecutionResult, but the servlet only takes data and errors from the ExecutionResult when it builds the reply in GraphQLServlet.createResultFromDataAndErrors, and as such, the extension information is lost.

Can the extensions element be added to the reply, when it's available?

graphql-java-servlet is incompatible with graphql-java-annotations?

I've registered GraphQL java servlet as defined in a workaround to:

graphql-java-kickstart/graphql-spring-boot#20

and registered the following query root using graphql-java-annotations:

@GraphQLName("QueryRoot")
class QueryRoot {
   @GraphQLField
    List<Item> items(DataFetchingEnvironment environment) {
       ...
    }

    @GraphQLField
    Item item(DataFetchingEnvironment environment, @GraphQLName("id") String id) {
        ...
    }
}

But when I perform a query I receive the following:

java.lang.IllegalArgumentException: object is not an instance of declaring class
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_131]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_131]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_131]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_131]
	at graphql.annotations.MethodDataFetcher.get(MethodDataFetcher.java:63) ~[graphql-java-annotations-3.0.3.jar:na]
	at graphql.execution.ExecutionStrategy.resolveField(ExecutionStrategy.java:99) ~[graphql-java-annotations-3.0.3.jar:na]
	at graphql.execution.SimpleExecutionStrategy.execute(SimpleExecutionStrategy.java:19) [graphql-java-annotations-3.0.3.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:106) [graphql-java-annotations-3.0.3.jar:na]
	at graphql.execution.Execution.execute(Execution.java:49) [graphql-java-annotations-3.0.3.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:222) [graphql-java-3.0.0.jar:na]
	at graphql.servlet.GraphQLServlet.query(GraphQLServlet.java:277) [graphql-java-servlet-4.1.0.jar:na]
	at graphql.servlet.GraphQLServlet.lambda$new$1(GraphQLServlet.java:185) [graphql-java-servlet-4.1.0.jar:na]
	at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.java:222) [graphql-java-servlet-4.1.0.jar:na]
	at graphql.servlet.GraphQLServlet.doPost(GraphQLServlet.java:240) [graphql-java-servlet-4.1.0.jar:na]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.15.jar:8.5.15]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.15.jar:8.5.15]

This boils down to the following code in the graphql-java-annotations:

class MethodDataFetcher implements DataFetcher {
  ...

    @Override
    public Object get(DataFetchingEnvironment environment) {
        try {
            Object obj;

            if (Modifier.isStatic(method.getModifiers())) {
                obj = null;
            } else if (method.getAnnotation(GraphQLInvokeDetached.class) == null) {
                obj = environment.getSource(); // <- THIS BRANCH IS EXECUTED
                if (obj == null) {
                    return null;
                }
            } else {
                obj = newInstance(method.getDeclaringClass());
            }
            //
            // FAILS HERE!
            // Expects "obj" to be of the "QueryRoot" type, but it is of the type
            // "GraphQLContext"
            return method.invoke(obj, invocationArgs(environment));
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}

There are two workarounds for this:

  • Annotate methods in QueryRoot with @GraphQLInvokeDetached
  • Make methods in QueryRoot static

Is this type mismatch expected or is it a bug? It seems like it should would work without these workarounds, according to graphql-java-annotations documentation

No HandshakeRequest in websocket subscription DataFetchingEnvironment

https://github.com/graphql-java/graphql-java-servlet/blob/e94d00e749dcc20a2ae474bb39072f3080800f06/src/main/java/graphql/servlet/internal/ApolloSubscriptionProtocolHandler.java#L60-L62

I found out that there is no HandshakeRequest in websocket subscription DataFetchingEnvironment GraphqlContext.
Why there is no (HandshakeRequest) session.getUserProperties().get(HANDSHAKE_REQUEST_KEY) builder argument that should be passed in GraphqlContext constructor?

Graphql servlet mapping not loading during springboot test.

I am using a graphqlqueryresolver and When I run my application i can see on the console all the servlet mapping loading as shown below:

2018-02-28 10:22:35.091 INFO 18072 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2018-02-28 10:22:35.097 INFO 18072 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'simpleGraphQLServlet' to [/graphql/]
2018-02-28 10:22:35.097 INFO 18072 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/]
2018-02-28 10:22:35.097 INFO 18072 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/]
2018-02-28 10:22:35.097 INFO 18072 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/]
2018-02-28 10:22:35.097 INFO 18072 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/]
2018-02-28 10:22:35.097 INFO 18072 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'corsConfigurer' to: [/]

But when I run a springboot test none of these mapping are loaded. This is my test class:

@RunWith(SpringRunner.class)
@SpringBootTest(classes= {ActionListApiApplication.class})
@autoconfiguremockmvc
public class ApiApplicationTests {

@test
public void contextLoads() {
}
}

I am not able to test my application as I get a 404 error for any request sent on /graphql.

can anyone please help me out with this.

NonNullableFieldWasNullError renders null locations field

Upstream graphql-java NonNullableFieldWasNullError.java returns null for locations & extensions.

According to GraphQL spec, locations must be non null if rendered:

If an error can be associated to a particular point in the requested GraphQL document, it should contain an entry with the key locations with a list of locations, where each location is a map with the keys line and column, both positive numbers starting from 1 which describe the beginning of an associated syntax element.

The simplest fix for this would be to apply @JsonInclude.NON_NULL to the getLocations() getter on the NonNullableFieldWasNullError upstream but that project has no dependency upon Jackson.

In lieu of doing that a decorator for NonNullableFieldWasNullError which can be swapped in by the DefaultGraphQLErrorHandler would solve the isue.

I had opened the issue in the upstream project but closed as it is a rendering issue. See graphql-java/graphql-java#940

Suggested decorator:

public class RenderableNonNullableFieldWasNullError implements GraphQLError {

    private final NonNullableFieldWasNullError delegate;

    public RenderableNonNullableFieldWasNullError(NonNullableFieldWasNullError nonNullableFieldWasNullError) {
        this.delegate = nonNullableFieldWasNullError;
    }

    @Override
    public String getMessage() {
        return delegate.getMessage();
    }

    @Override
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public List<SourceLocation> getLocations() {
        return delegate.getLocations();
    }

    @Override
    public ErrorType getErrorType() {
        return delegate.getErrorType();
    }

    @Override
    public List<Object> getPath() {
        return delegate.getPath();
    }

    @Override
    public Map<String, Object> toSpecification() {
        return delegate.toSpecification();
    }

    @Override
    @JsonInclude(JsonInclude.Include.NON_NULL)
    public Map<String, Object> getExtensions() {
        return delegate.getExtensions();
    }
}

Multipart usage

Hello,

I saw in GraphQLServletSpec.groovy (line 216) a java example of query by multipart.
But I tried with curl, and I get a 500 Error.

curl -v \
--request POST --url http://localhost:8080/graphql \
--header 'content-type: multipart/graphql' \
--form 'graphql={query: {test { test } }}'

What is wrong ?
Thx

Schema is not configured for mutations

When I try to execute the mutation I get the error on the tomcat console of "Schema is not configured for mutations". When I go to the /graphql/schema.json I am able to see the mutationType: Mutation (as in the attached image). Somewhere there is a disconnect or a configuration issue. I have not been able find the disconnect. I have yet to debug the code to find the issue but wanted to see if there is something specific that I am missing before I did that.

Snippet of schema.graphqls file.

`schema {
mutation: Mutation
}
type Mutation {
createComicBox( comicBoxInput: ComicBoxInput! ) : ComicBox
removeComicBox ( comicBoxInput: ComicBoxInput! ) : ComicBox
}

type ComicBox {
boxName: String!
}
input ComicBoxInput {
boxName: String!
}
`

image

I attempted to execute the mutation with the following URL snippet: /graphql?query=mutation {createComicBox(comicBoxInput:{boxName:"Song"}){boxName}}

Any help would be greatly appreciated.

Add examples

Adding a few examples or at least some getting started guides would be helpful.

Instrumentation / Extensions: blacklist and strip to prevent/send extensions to clients

Could a mechanism be implemented like in Apollo (cfr https://www.apollographql.com/docs/engine/setup-standalone.html#api-apollo-engine-launcher): frontends.extensions.blacklist and frontends.extensions.strip allow you to determine which extension keys are sent to the client.

More specifically, it provides 2 mechanisms: one to prevent certain extension information to ever be send to the client (blacklist) and another one to send only when the client requests it (strip).
Typically used for information that we want available on the server to process/analyse, but just bloats the responses for the client (like tracing and caching).

I tried to implement this with the listeners (removing the keys from the extensions map in onError and onSuccess), but the response is written before those are invoked in GraphQLServlet.java, so that didn't work.

More generally, I'm looking for a mechanism to prevent certain extension keys to be send to the client (or only when explicitly requested, but that's not really necessary for me at the moment).

Authorization/error handling

I'm looking for a way to authorize requests, specifically mutations. For example, if a user without the required permission attempts to execute mutateFoo, they should be rejected with an error. Or maybe they do not have the permission to mutate this particular instance of Foo (so it depends on payload, not just mutation type). Offhand I would expect HTTP 403, unless the graphql-land wants to reinvent HTTP and put that in the payload.

Anyway, I don't really see a way to do this with GraphQLServlet at all. There doesn't seem to be a pre-execution hook that could veto execution and let me return an error. When an exception is thrown, it's always caught and translated to the generic:

{"data":{"mutateFoo":null},"errors":[{"message":"Internal Server Error(s) while executing query"}]}

Ideally, I should be able to throw an exception from anywhere in my business logic, and have it translated to a meaningful GraphQL response. That's even more flexible than the abovementioned execution hook.

I don't want to use something like servlet filters for this, since that would mean interpreting the GraphQL request myself, and it's on the wrong level.

JsonMappingException: No serializer found for class graphql.execution.TypeInfo

The following exception is thrown writing GraphQL execution result with errors to response:

com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class graphql.execution.TypeInfo and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.HashMap["errors"]->java.util.ArrayList[0]->graphql.execution.NonNullableFieldWasNullException["typeInfo"])
	at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:284) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.SerializerProvider.mappingException(SerializerProvider.java:1110) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.SerializerProvider.reportMappingProblem(SerializerProvider.java:1135) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:32) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:704) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:18) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeFields(MapSerializer.java:633) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:536) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:30) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3681) ~[jackson-databind-2.8.8.jar:2.8.8]
	at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3057) ~[jackson-databind-2.8.8.jar:2.8.8]
	at graphql.servlet.GraphQLServlet.query(GraphQLServlet.java:281) [graphql-java-servlet-4.1.0.jar:na]
	at graphql.servlet.GraphQLServlet.query(GraphQLServlet.java:264) [graphql-java-servlet-4.1.0.jar:na]
	at graphql.servlet.GraphQLServlet.lambda$new$1(GraphQLServlet.java:185) [graphql-java-servlet-4.1.0.jar:na]
	at graphql.servlet.GraphQLServlet.doRequest(GraphQLServlet.java:222) [graphql-java-servlet-4.1.0.jar:na]
	at graphql.servlet.GraphQLServlet.doPost(GraphQLServlet.java:240) [graphql-java-servlet-4.1.0.jar:na]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:707) [javax.servlet-api-3.1.0.jar:3.1.0]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790) [javax.servlet-api-3.1.0.jar:3.1.0]
`


Here a NonNullableFieldWasNullException containing a TypeInfo field is going to be serialized without matching Jackson Serializer.

How to show dataloader statistics in graphql response

i want to show dataloader statistics in GraphQL response like below.

"extensions": {
    "dataloader": {
      "overall-statistics": {
        "loadCount": 8,
        "loadErrorCount": 0,
        ....
      },
      "individual-statistics": {
        "replies": {
          "loadCount": 8,
          "loadErrorCount": 0,
          "loadErrorRatio": 0,
          ....
        }
      }

In order to show the statistics, DataLoaderDispatcherInstrumentationOptions is needed to be set to DataLoaderDispatcherInstrumentation constructor.
However, in the graphql-java-servlet library, DataLoaderDispatcherInstrumentationOptions isn't set to the constructor.
https://github.com/graphql-java/graphql-java-servlet/blob/e94d00e749dcc20a2ae474bb39072f3080800f06/src/main/java/graphql/servlet/GraphQLQueryInvoker.java#L68

I've tried to extend GraphQLQueryInvoker and override ExtendedGraphQLInvoker#getInstrumentation method.

override fun getInstrumentation(context: Any?): Instrumentation {
    return if (context is GraphQLContext) {
      context.dataLoaderRegistry
          .map { registry ->
            val instrumentations = ArrayList<Instrumentation>()
            instrumentations.add(getInstrumentation.get())
            // ๐Ÿ‘‡ add DataLoaderDispatcherInstrumentationOptions.newOptions().includeStatistics(true)
            instrumentations.add(DataLoaderDispatcherInstrumentation(registry, DataLoaderDispatcherInstrumentationOptions.newOptions().includeStatistics(true)))
            ChainedInstrumentation(instrumentations)
          }
          .map { it as Instrumentation }
          .orElse(getInstrumentation.get())
    } else getInstrumentation.get()
  }

I've be able to show dataloader statistics, but I don't want to take this approach.

Is there any way to set DataLoaderDispatcherInstrumentationOptions to the constructor, or to show dataloader statistics?

[Question] Is "application/graphql" Content Type supported by graphql-java-servlet?

GraphQL doc mentions application/graphql as one of the options to send POST requests to the server - http://graphql.org/learn/serving-over-http/#post-request

If the "application/graphql" Content-Type header is present, treat the HTTP POST body contents as the GraphQL query string.

This helps the client to send the query exactly like the GraphIQL tool.

I was wondering if this is something graphql-java-servlet supports right now or planning to support in the future?

I did try to send the request with this ContentTyope header and the Request raw body in the query format, however it did nor work.

Got the following error:

com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'query': was expecting ('true', 'false' or 'null')
 at [Source: (BufferedInputStream); line: 1, column: 7]

Provide an alternative way of initializing the SimpleGraphQLServlet

I often get asked what to do with the servlet instance once it's created, as the servlet spec provides no programmatic way to register it. Some servlet containers do, when started programmatically (e.g. Jetty), but there's nothing portable. Spring also allows this, but if you already have Spring, creating a custom servlet is already a strange requirement, in the face of Spring MVC controllers etc.

So what people end up doing is what I did in the HowToGraphQL tutorial: subclass SimpleGraphQLServlet, handle the entire setup in the constructor, and register that subclass with the container (using @WebServlet or web.xml) so the container can initialize it as usual. The problem with this is that all the setup steps need to be static and weirdly inlined as the call to super needs to be the very first thing in the constructor. This is very unintuitive and cumbersome. Worse yet, until recently, there was no accessible non-deprecated constructor to call from the subclass, making the only obvious strategy obsolete.

So what I propose is to allow SimpleGraphQLServlet to initialize its state in the usual Servlet#init(ServletConfig) method instead of the constructor. This method exists in the spec exactly because the constructor is often a very inconvenient place to setup servlets.

E.g.

class SimpleGraphQLServlet {
    
    @Override
    public void init(ServletConfig config) {
        Builder builder = getConfiguration();
        if (builder != null) {
            this.schemaProvider = builder.schemaProvider;
            ... // the same as the current constructor
        }
    }
   
    //this is for the subclasses to optionally override
    protected Builder getConfiguration() {
        return null;
    }
}

This would allow a very vanilla use of the servlet, compatible with any container with no surprises or workarounds needed:

@WebServlet(urlPatterns = "/graphql") //or via web.xml
public class GraphQLEndpoint extends SimpleGraphQLServlet {

    @Override
    protected Builder getConfiguration() {
       return SimpleGraphQLServlet.builder(schema)
           .withInstrumentation(..) //etc
    }
}

This is just a quick example of what I mean. Instead of a Builder, getConfiguration could return some Configuration object you could get e.g. by calling Builder#toConfiguration instead of Builder#build or whatever.

I could, of course, implement this if you'd want me to.

Empty String as operationName results in incorrect GraphQL response

If an incorrect query is sent by using an empty String as value for the operationName instead of null, the GraphQL servlet responds with a 500 Internal Server Error with an empty String as the response body. Simple example which generates this error:

operationName: ""
query:"{ viewer { username } }"
variables: null

I would expect either a correct GraphQL error response to indicate a problem with the query, or treat the empty String the same as a null value. See deserialization in GraphQLServlet and the check in ExecutionContextBuilder:55.

How to provide custom error messages to the client?

Hi, I hope this isn't a too silly question.

I'm using graphql-java-tools, so I have a resolver that I would like to provide a nice error to the client from.

Example:

@Component
public class SomeMutationResolver implements GraphQLMutationResolver {

  @Autowired
  DataService DataService;

  public SomeObject addSomething(InputObject something) throws ValidationError {
    ...
    if (something.getSomeValue() < 0) {
      throw new ValidationError("I'd like this message to be passed up to the client");
    }
    ...
  }

I've been trying a few things, the signature of ValidationError (not to be confused with graphql.validation.ValidationError), and the signature looks liket his:

public class ValidationError extends RuntimeException implements GraphQLError {
    ...
}

The "problem" is that when this error ends up in graphql.servlet.DefaultGraphQLErrorHandler it has already been wrapped in an ExceptionWhileDataFetching, meaning that isClientError is always false.

I'm having a hard time finding the place that wraps my ValidationError with ExceptionWhileDataFetching, but my guess is that I should probably implement my own GraphQLErrorHandler.

Not sure if this issue fits best here or in graphql-java-utils.

Access to servlet request in resolver?

I'm actually using the graphql-spring-boot-starter where it auto-maps the query/mutation to a GraphQLQueryResolver/GraphQLMutationResolver. What I need is the ability to access the request object in order to extract certain pieces of data, such as the IP address of the requestor.

Is this possible? If so, how would one accomplish it?

Example GraphQL schema:

type Login {
    token: String
}
type Mutation {
    login(username: String!, password: String!): Login
}

Example resolver:

@Component
public class LoginResolver implements GraphQLMutationResolver {
    public LoginDto login(String username, String password) {
        // Get IP address of requestor
    }
}

GraphQLServlet not returning GraphQL response object on GraphQL errors

Getting HTTP 400 error response from servlet when passing in invalid graphql query but structurally correct. No logging happening either.

Request:
{"query":"{ __schema{types{name}} }","ariables":"{}"}

Expected response to be something like:
{errors=[InvalidSyntaxError{sourceLocations=[SourceLocation{line=2, column=3}]}]}

HTTP 500 errors do not log stacktraces either.

If this is intended behavior for the GraphQLServlet could you expose the ExecutionResult and the GraphQLRequest objects?

Thanks,
Barb Craig

Customise ObjectMapper capabilities

Is there a way to customise the object mapper used in the servlet.
I'm having trouble serialising LocalDateTime objects, which can be solved by registering extra Jackson modules in the ObjectMapper.

Is there a way to achieve this?

Ability to pass custom context while executing a query

While implementing dataloaders with graphql-java, there is a usecase to pass loaders as a context to executionInput:

DataLoaderRegistry loaders = ...; //Initializing dataloaders
GraphQL runtime = GraphQL.newGraphQL(schemaFromSPQR)
          .instrumentation(new DataLoaderDispatcherInstrumentation(loaders))
          .build();
graphQL.execute(ExecutionInput.newExecutionInput()
         .query(operation)
         .context(loaders) // <-- Need to pass loaders as a context.
         .build());

This is required so that loaders can be accessed from resolver like:

@GraphQLQuery
public CompletableFuture<Item> item(@GraphQLArgument(name = "id") Long id, 
@GraphQLRootContext DataLoaderRegistry loaders){ 
      return loaders.getDataLoader("items").load(id); //load using the injected DataLoader
}

But, I didn't find a way to pass custom context to graphql-java-servlet while resolving a query.

Can we extend GraphQLContext to accept custom context object?

Simple change to support alternative error handling strategies

I see many requests to handle errors differently. Some simple, some quite involved.

However by simply changing the method GraphQLServlet.createResultFromDataAndErrors from private to protected access, it would be possible for subclasses to override and implement alternative error reporting/handling strategies.

My case is one of the simple ones. I would like to pass the executionId back to the client regardless of whether it is a "client error" or not. This allows me to tie an error in my browser console back to a server log.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.