graphql-java-kickstart / graphql-java-servlet Goto Github PK
View Code? Open in Web Editor NEWServlet endpoint for GraphQL Java
Home Page: https://www.graphql-java-kickstart.com/servlet/
License: Other
Servlet endpoint for GraphQL Java
Home Page: https://www.graphql-java-kickstart.com/servlet/
License: Other
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).
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.
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?
I need to know if graphql-java-servlet allows for gzip compression (Accept-Encoding: gzip
and Content-Encoding: gzip
) and if so, is it enabled by default or is there a way to enable it? I'm using the graphql-spring-boot starter as well.
I was wondering if there will ever be support for asynchronous processing in the GraphlQLServlet? In the stack, I see a completeable futures.
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?
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)
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
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.
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.
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.
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
.
Adding a few examples or at least some getting started guides would be helpful.
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
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?
Since it appears Lombok was removed from https://github.com/graphql-java/graphql-java, should we do the same in here? I personally find that library very not-intuitative.
@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]
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?
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
}
}
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();
}
}
Multiple CVEs are published against commons-fileupload 1.3.1.
These have both been resolved in commons-fileupload 1.3.3
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?
Per @jplock at graphql-java/graphql-java#268 (comment). Thanks
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
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?
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
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]
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))
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!
}
`
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.
What is the replacement for SimpleGraphQLServlet (along with the required parameters to the constructor) in the latest release?
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();
...
I am trying to use DataLoaders. If I understand it correctly:
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
)DataLoaderRegistry
from this contextDataLoaderDispatcherInstrumentation
must be used so that dispatch
methods are calledbut 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?
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:
QueryRoot
with @GraphQLInvokeDetached
QueryRoot
staticIs 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
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.
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.
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.
Would be nice, I'll leave this issue here to track progress.
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
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?
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?
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
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.
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?
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.
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?
The README points out that GraphQLServletListener has both RequestCallback and OperationCallback. OperationCallback seems to have been removed and I am having trouble finding a replacement for it. Specifically I was using it to track metrics (operation count, operation time, etc). Is there an alternative that should be used?
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).
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)
Is there any plans to upgrade this to use the latest version of graphql-java (v8)
Thanks
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
.
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
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.