Code Monkey home page Code Monkey logo

graphql-spqr-spring-boot-starter's People

Contributors

baldylocks avatar csueiras avatar ertugrulsener avatar esc-sbarden avatar icarus8050 avatar kaqqao avatar kicktipp avatar michael-simons avatar saschapeukert avatar vince250598 avatar vjroby avatar vkachan 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

graphql-spqr-spring-boot-starter's Issues

Enable easy customization of GraphQL execution

In order to customize any of the following:

  • Building ExecutionInput
  • Executing the operation
  • Handling the response

it is currently necessary to override the whole controller and/or WebSocket handler.

To facilitate easier customization, all of these concerns are now delegated to GraphQLExecutor implementation that can easily be swapped by the user.

Right now, 3 built-in implementations exist (with at least 1 more to come):

  • servlet.DefaultGraphQLExecutor - used by the GraphQL controller in a servlet-based app
  • reactive.DefaultGraphQLExecutor - used by the GraphQL controller in a reactive app
  • servlet.websocket.DefaultGraphQLExecutor - used by the WebSocket handler in a servlet-based app

Once WebSockets are supported on the reactive stack, an additional executor will be added.

Custom Exception Handling with Spring Method Level Security

Hi,

thanks for this library. I'm using method level security to restrict the graphql endpoints. So far this works great. However, if a user lacks authentication the application returns an AccessDeniedException (as expected) which I can't seem to catch.

I'd greatly appreciate any advice.

Example code:

@GraphQLApi
@Component
public class UserGraphQLService {

    @PreAuthorize("hasRole('ROLE_USER')")
    @GraphQLQuery
    public String someMethod() {
        //...
    }

}

Results in:

org.springframework.security.access.AccessDeniedException: Access Denied
	at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84) ~[spring-security-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:233) ~[spring-security-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:65) ~[spring-security-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at my.application.UserGraphQLService$$EnhancerBySpringCGLIB$$a4cbe9c.someMethod(<generated>) ~[main/:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at io.leangen.graphql.metadata.execution.SingletonMethodInvoker.execute(SingletonMethodInvoker.java:21) ~[spqr-0.9.9.jar:na]
	at io.leangen.graphql.metadata.Resolver.resolve(Resolver.java:100) ~[spqr-0.9.9.jar:na]
	at io.leangen.graphql.execution.OperationExecutor.lambda$execute$1(OperationExecutor.java:93) ~[spqr-0.9.9.jar:na]
	at io.leangen.graphql.execution.OperationExecutor.execute(OperationExecutor.java:98) ~[spqr-0.9.9.jar:na]
	at io.leangen.graphql.execution.OperationExecutor.execute(OperationExecutor.java:94) ~[spqr-0.9.9.jar:na]
	at io.leangen.graphql.execution.OperationExecutor.execute(OperationExecutor.java:59) ~[spqr-0.9.9.jar:na]
	at graphql.execution.instrumentation.dataloader.DataLoaderDispatcherInstrumentation.lambda$instrumentDataFetcher$0(DataLoaderDispatcherInstrumentation.java:86) ~[graphql-java-11.0.jar:na]
	at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:261) ~[graphql-java-11.0.jar:na]
	at graphql.execution.ExecutionStrategy.resolveFieldWithInfo(ExecutionStrategy.java:202) ~[graphql-java-11.0.jar:na]
	at graphql.execution.ExecutionStrategy.resolveField(ExecutionStrategy.java:175) ~[graphql-java-11.0.jar:na]
	at graphql.execution.AsyncSerialExecutionStrategy.lambda$execute$1(AsyncSerialExecutionStrategy.java:43) ~[graphql-java-11.0.jar:na]
	at graphql.execution.Async.eachSequentiallyImpl(Async.java:75) ~[graphql-java-11.0.jar:na]
	at graphql.execution.Async.eachSequentially(Async.java:64) ~[graphql-java-11.0.jar:na]
	at graphql.execution.AsyncSerialExecutionStrategy.execute(AsyncSerialExecutionStrategy.java:38) ~[graphql-java-11.0.jar:na]
	at graphql.execution.Execution.executeOperation(Execution.java:159) ~[graphql-java-11.0.jar:na]
	at graphql.execution.Execution.execute(Execution.java:101) ~[graphql-java-11.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:573) ~[graphql-java-11.0.jar:na]
	at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:515) ~[graphql-java-11.0.jar:na]
	at graphql.GraphQL.executeAsync(GraphQL.java:489) ~[graphql-java-11.0.jar:na]
	at graphql.GraphQL.execute(GraphQL.java:420) ~[graphql-java-11.0.jar:na]
	at io.leangen.graphql.spqr.spring.web.servlet.DefaultGraphQLExecutor.execute(DefaultGraphQLExecutor.java:23) ~[graphql-spqr-spring-boot-autoconfigure-0.0.4.jar:na]
	at io.leangen.graphql.spqr.spring.web.servlet.DefaultGraphQLExecutor.execute(DefaultGraphQLExecutor.java:11) ~[graphql-spqr-spring-boot-autoconfigure-0.0.4.jar:na]
	at io.leangen.graphql.spqr.spring.web.GraphQLController.executeJsonPost(GraphQLController.java:42) ~[graphql-spqr-spring-boot-autoconfigure-0.0.4.jar:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
	at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.19.jar:9.0.19]
	at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

variables passed to the ExecutionInput cannot be null

When the variables parameter is not being passed as part of the request, graphql-java throws the following exception. This, in particular, is a nightly version of graphql-java which makes the issue explicit (graphql-java/graphql-java#1596). Earlier versions of graphql-java (from 11 on) fail with more obscure errors.

Would it make sense to ensure the propagated variables instance is never null and rather an empty map?

graphql.AssertException: variables map can't be null
	at graphql.Assert.assertNotNull(Assert.java:15)
	at graphql.ExecutionInput$Builder.variables(ExecutionInput.java:215)
	at io.leangen.graphql.spqr.spring.web.GraphQLExecutor.buildInput(GraphQLExecutor.java:20)
	at io.leangen.graphql.spqr.spring.web.servlet.DefaultGraphQLExecutor.execute(DefaultGraphQLExecutor.java:23)
	at io.leangen.graphql.spqr.spring.web.servlet.DefaultGraphQLExecutor.execute(DefaultGraphQLExecutor.java:11)
	at io.leangen.graphql.spqr.spring.web.GraphQLController.executeJsonPost(GraphQLController.java:42)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)

The above issue, in particular, is triggered simply loading a GraphiQL page which attempts an introspection query that doesn't include a variables field in the request.

Deal with generated proxies

Currently, if a bean is wrapped into a proxy for any reason (security interceptors, async support, transactional control etc), SPQR will reject the bean as it's package name can no be reliably determined.

The starter should explicitly specify the real types when registering the discovered beans.

mapper field in ApolloMessage Class

Hello,

In ApolloMessage the object mapper does not include null.

private static final ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);

When null value are not included apollo-client is warning about missing fields in queries causing some weird behaviours when updatingQueries.

Is there a good reason to configure the mapper like that?

GraphQLSubscription "InvalidDefinitionException" error

Hi,

I'm newbie to spring reactive so this question can be silly, sorry for that.
I'm trying to use graphqlsubscription from looking to the example.
Here is my code:

`private final ConcurrentMultiMap<String, FluxSink> subscribers = new ConcurrentMultiMap<>();

@GraphQLSubscription
public Publisher expUpdated(String code) {
return Flux.create(subscriber -> subscribers.add(code, subscriber.onDispose(() -> subscribers.remove(code, subscriber))), FluxSink.OverflowStrategy.LATEST);
}`

And here is the error:

org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class graphql.execution.reactive.CompletionStageMappingPublisher]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class graphql.execution.reactive.CompletionStageMappingPublisher and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.LinkedHashMap["data"])
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:293) ~[spring-web-5.0.7.RELEASE.jar:5.0.7.RELEASE]

Support Spring Data paging out of the box

Firstly, we must make sure Spring Data paging types (Page, Pageable) map nicely out of the box.

Additionally, we should have custom mappers, and whatever else necessary, to optionally automatically map Spring paging style to Relay Connection style.

A similar issue was raised in SPQR (leangen/graphql-spqr#127).

Edit prefix Input object

Dear bro,
I have issue with auto generator input type such as:
I have class CreateCompanyInput
And SPQR auto generator CreateCompanyInputInput. How can i fix it?

Websockets with GraphQL Playground

I am using 0.0.2 version. If I enable websocket support via graphql.spqr.ws.enabled=true then websockets doesn't work with GraphQL Playground. It appears that SockJsHttpRequestHandler is being used by the app instead of PerConnectionWebSocketHandler. See below line from application startup logs -

Mapped URL path [/graphql/**] onto handler of type [class org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler]

Can anyone help with this? I have been using SPQR to build an app from last 3 months but because of Websocket support issues my development work is not crippled. :( I don't want to write a separate NodeJs app with Apollo server.

Apollo websocket authentication with Spring Security

I can't authenticate over websocket using Apollo aproach:

const wsLink = new WebSocketLink({
uri: 'ws://localhost:9090/graphql',
  options: {
    reconnect: true,
    connectionParams: {
        authToken: user.authToken,
    },
});

The response is "HTTP Authentication failed; no valid credentials available"

How I can manage the connection params 'authToken' in Spring?

get Authentication with WebFlux

I have a need that get authentication inside @GraphQLMutation method, this might be a common need, as was mentioned in #11, However when using with WebFlux, the solution mentioned there is out of style.

SecurityContextHolder.getContext().getAuthentication() will return null in WebFlux, as explained in this StackOverflow post

Because there is no way to use ThreadLocal objects anymore. The only way to get Authentication for you, is to ask for it in controller's method signature, or...
Return a reactive-chain from method, that is making a ReactiveSecurityContextHolder.getContext() call.

In the case of this project, however, I cannot modify the Controller method, or return the reactive-chain that is making ReactiveSecurityContextHolder.getContext() call.

I found a workaround to return the reactive-chain, by providing a custom GraphQLReactiveExecutor, like this:

@Component
public class DefaultGraphQLExecutor implements GraphQLReactiveExecutor {

    @Autowired(required = false)
    @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
    private DataLoaderRegistryFactory dataLoaderRegistryFactory;


    @Override
    public Mono<Map<String, Object>> execute(GraphQL graphQL, GraphQLRequest graphQLRequest, ServerWebExchange request) {
        return ReactiveSecurityContextHolder.getContext()
                .flatMap(securityContext -> Mono.fromFuture(
                        graphQL.executeAsync(
                                buildInput(
                                        graphQLRequest,
                                        request,
                                        (params -> {
                                            DefaultGlobalContext<ServerWebExchange> context = new DefaultGlobalContext<>(params.getNativeRequest());
                                            context.setExtension("authentication", securityContext);
                                            return context;
                                        }),
                                        dataLoaderRegistryFactory)
                        ).thenApply(ExecutionResult::toSpecification)));
    }
}

Then I will be able to get authentication information inside @GraphQLMutation method by injecting:@GraphQLRootContext DefaultGlobalContext<SecurityContextServerWebExchange> context and calling context.getExtension("authentication").getAuthentication(),

    @Transactional
    @GraphQLMutation
    public Authentication getAuthentication(
            @GraphQLRootContext DefaultGlobalContext<SecurityContextServerWebExchange> context) {
        SecurityContext securityContext = context.getExtension("authentication");
        return securityContext.getAuthentication();
    }

Is there any better way to achieve that?

JSON parse error: Cannot construct instance of `java.util.LinkedHashMap`

Hello, I am trying to use spring boot auto starter in my project. I am not sure what I am missing.
I am getting below error :

JSON parse error: Cannot construct instance of java.util.LinkedHashMap (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value (''); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of java.util.LinkedHashMap (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('')\n at [Source: (PushbackInputStream); line: 1, column: 70] (through reference chain: io.leangen.graphql.spqr.spring.web.dto.GraphQLRequest["variables"])",
"path": "/graphql"

Below is my service class in spring boot application.

@GraphQLApi
@Service
public class Serviceq {

	@Autowired
	PRepository pRepo;

	@Transactional
	@GraphQLQuery(name = "party")
	public Test getPByNum(@GraphQLArgument(name = "num") String num)
					throws Exception, ExecutionException {

		Future<Test> test= partyRepo.findByNum(num);
		return test.get();
	}

}

I am requesting with GraphiQL.

{
  party (num: "1234"){
    id
    name
   number
  }
}

Using Spring data Pageable and Sort

Hi there,

I am struggling to get my API to work with org.springframework.data.domain.PageRequest as input to a graphql query resolver.

public Page<Discussion> getPagedDiscussions(@GraphQLArgument(name = "pagingInfo") PageRequest pageRequest) {}

When I load graphiql I get a message saying "SortInput fields must be an object with field names as keys or a function which returns such an object."

I tried custom InputFieldBuilders but it doesn't work :(

        inputFieldBuilders.add(new InputFieldBuilder() {
            @Override
            public Set<InputField> getInputFields(InputFieldBuilderParams params) {
                AnnotatedType type = params.getType();

                Set<InputField> fields = new HashSet<>();
                fields.add(new InputField("pageNumber", "Page number", GenericTypeReflector.annotate(Integer.class), GenericTypeReflector.annotate(Integer.class), 0));
                fields.add(new InputField("pageSize", "Page size", GenericTypeReflector.annotate(Integer.class), GenericTypeReflector.annotate(Integer.class), 20));
                fields.add(new InputField("sort", "Page sorting", GenericTypeReflector.annotate(Sort.class), GenericTypeReflector.annotate(Sort.class), null));
                return fields;
            }

            @Override
            public boolean supports(AnnotatedType type) {
                return PageRequest.class.equals(type.getType());
            }
        });


        inputFieldBuilders.add(new InputFieldBuilder() {
            @Override
            public Set<InputField> getInputFields(InputFieldBuilderParams params) {
                AnnotatedType type = params.getType();

                Set<InputField> fields = new HashSet<>();
                fields.add(new InputField("orders", "Orders for sorting", GenericTypeReflector.annotate(Sort.Order[].class), GenericTypeReflector.annotate(Sort.Order[].class), 20));
                return fields;
            }

            @Override
            public boolean supports(AnnotatedType type) {
                return Sort.class.equals(type.getType());
            }
        });
        inputFieldBuilders.add(new InputFieldBuilder() {
            @Override
            public Set<InputField> getInputFields(InputFieldBuilderParams params) {
                AnnotatedType type = params.getType();

                Set<InputField> fields = new HashSet<>();
                fields.add(new InputField("direction", "Sorting order direction", GenericTypeReflector.annotate(String.class), GenericTypeReflector.annotate(String.class), 20));
                fields.add(new InputField("property", "Sorting order property", GenericTypeReflector.annotate(String.class), GenericTypeReflector.annotate(String.class), 20));
                return fields;
            }

            @Override
            public boolean supports(AnnotatedType type) {
                return Sort.Order.class.equals(type.getType());
            }
        });

Using starter on application having @EnableScheduling cause an exception

In an application with @EnableScheduling annotation, I'm facing the following error:

org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'defaultSockJsTaskScheduler' is expected to be of type 'org.springframework.scheduling.TaskScheduler' but was actually of type 'org.springframework.beans.factory.support.NullBean'
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:392)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:224)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1115)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveNamedBean(DefaultListableBeanFactory.java:1082)
	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.resolveSchedulerBean(ScheduledAnnotationBeanPostProcessor.java:313)
	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.finishRegistration(ScheduledAnnotationBeanPostProcessor.java:254)
	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:231)
	at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.onApplicationEvent(ScheduledAnnotationBeanPostProcessor.java:103)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:402)
	at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:359)
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:896)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:163)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:552)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)

fail to GET request with variables

reported at #52 (comment)

Currently the transmission of variables in the request via puery param does not work either. When I send a request (as defined in the docu https://graphql.org/learn/serving-over-http/), the following error occurs

Field error in object 'graphQLRequest' on field 'variables': rejected value [{
"$content": "123"
}]; codes [typeMismatch.graphQLRequest.variables,typeMismatch.variables,typeMismatch.java.util.Map,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [graphQLRequest.variables,variables]; arguments []; default message [variables]]; default message [Failed to convert value of type 'java.lang.String[]' to required type 'java.util.Map'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'java.util.Map': no matching editors or conversion strategy found]]

	@Test
	public void defaultControllerTest_GET_with_variables() throws Exception {
		mockMvc.perform(
				get("/"+apiContext)
						.param("query","query echo($content: String) {echo(content: $content)}")
						.param("variables", "{\n" +
                                "  \"$content\": \"123\"\n" +
                                "}")
						)
				.andExpect(status().isOk())
				.andExpect(content().string(containsString("Hello world")));
	}

reason: Jackson databiner doesn't know how to convert String to Map.

Subscriptions not working with withOperationsFromSingleton

Perhaps not an issue - I'm a bit of a newbie so forgive my ignorance :)

I'm using the new built in subscriptions with Apollo (@GraphQLSubscription). It all works beautifully, unless I add .withOperationsFromSingleton() to the GraphQLSchemaGenerator. I had originally thought the subscriptions stuff was not working at all but since realised it works without that config

Any pointers? Thanks

Throw exception on type clash

I just created two @Services with clashing method names:

@Service
@GraphQLApi
public class MyService1 {
  @GraphQLMutation
  public String foo() {
    return "1";
  }
}

@Service
@GraphQLApi
public class MyService2 {
  @GraphQLMutation
  public String foo() {
    return "2";
  }
}

Now it's completely random (changes every time I restart the application) which of those 2 methods gets called.

I think it would be best, if the auto configuration throws an exception, when adding a GraphQL Type that already exists.

Apollo subscriptions on the reactive stack

We already support Spring WebFlux for queries and mutations (#9), but Apollo graphql-ws only works with Spring MVC.
We need a reactive WebSocket implementation to complete the WebFlux support.

ClassCastException when class implements multiple Union-Interfaces and is used as return type

I have a UserDto.class which implements two interfaces which both are used as union-interfaces (to handle errors in the response instead of throwing an exception).

public class UserDto implements IUserResult, IFindUserResult { // fields/getter/setter }
@GraphQLUnion(possibleTypes = {UserDto.class, UserRegistrationErrorDto.class})
public interface IUserResult {}
@GraphQLUnion(possibleTypes = {UserDto.class, UserNotFoundDto.class})
public interface IFindUserResult {}

In the service class i try to return the UserDto object but get the following error:
java.util.concurrent.CompletionException: java.lang.ClassCastException: graphql.schema.GraphQLTypeReference cannot be cast to graphql.schema.GraphQLObjectType

Method in service class:
Returns interface type. Returning a UserRegistrationError object works, but returning the UserDto object throws the above mentioned error. :

    @GraphQLMutation
    public IUserResult registerUserAccount(
            @GraphQLArgument(name = "user") UserRegistrationInputDto user) {

        // validation omitted

        List<ErrorDto> errors = new ArrayList<>();
        //check duplicate email addresses
        if(userRepository.findByEmail(user.getEmail()).isPresent()){
            errors.add(new ErrorDto("email", "user already registered with this email address"));
            return new UserRegistrationErrorDto(errors);
        }

        User toRegister = modelMapper.map(user, User.class);
        User registered = userRepository.save(toRegister);
        return modelMapper.map(registered, UserDto.class);
    }

This error does not occur when UserDto.class only implements one interface, but this would break the ability to return error types/messages without throwing exceptions.
See example project at https://github.com/ssiemens-hm/spgr-graphql-test

Used GraphQL-Mutation:

mutation {
  registerUserAccount(
    user: {
      firstName: "firstname"
      lastName: "lastname"
      email: "[email protected]"
      password: "password"
      matchingPassword: "password"
    }
  ) {
    __typename
    ... on User {
      id
      firstName
      lastName
    }
    ... on UserRegistrationError {
      errors {
        field
        reason
      }
    }
  }
}

Data loader in subscriptions

Hi!

I'm trying to use subscriptions with this library, but unfortunately it seems that data loaders are not working with subscriptions. The problem seems to be that the code in ApolloProtocolHandler doesn't pass the dataLoaderRegistry to the ExecutionInput.Builder.

ApolloProtocolHandler line 91:

ExecutionResult result = graphQL.execute(ExecutionInput.newExecutionInput()
                            .query(request.getQuery())
                            .operationName(request.getOperationName())
                            .variables(request.getVariables()));

The missing lines are (copied from GraphQLController:

        if (dataLoaderRegistryFactory != null) {
            inputBuilder.dataLoaderRegistry(dataLoaderRegistryFactory.createDataLoaderRegistry());
        }

@kaqqao can you take a look at it?

Thanks,
Gabor

Spring Security Context + Subfields

Hey @kaqqao I've pretty successfully integrated with spring security and SPQR but I just noticed a bug in my code that manifests itself only with subfields that are making use of @GraphQLContext, and more specifically so far I've discovered this in a Subscription. For this example my subscription is returning the Stuff objects.

Basically I have something like this:

@PreAuthorize("isAuthenticated()")
@GraphQLQuery
public Stuff getStuff() {
  return repository.getStuff();
}

@PreAuthorize("isAuthenticated()")
@GraphQLQuery
public MoreStuff getMoreStuff(@GraphQLContext Stuff stuff) {
   final MyAuthentication auth = (MyAuthentication)SecurityContextHolder.getContext().getAuthentication();
   return repository.getMoreStuff(stuff.getStuffId(), auth.getUserId());
}

This results in a couple of issues, first the PreAuthorize on the getMoreStuff query field cannot find an authenticated user in its context. As you may know SecurityContextHolder uses a ThreadLocal variable to keep track of the current security context, and I know that getMoreStuff and getStuff are executed in different threads, so this explains that issue. I tried to enable inheritable thread local strategy on the security context holder but that wouldnt work either because my getStuff thread is not the one initiating this other thread.

I assume you or someone else in the community has probably solved this issue before and I was wondering if I could get some insight on how its been solved for GraphQL. I prototyped something that while would work its also a lot of work...

Basically any parent field would have to do:

@PreAuthorize("isAuthenticated()")
@GraphQLQuery
public Stuff getStuff(@GraphQLRootContext DefaultGlobalContext<HttpServletRequest> context) {
  // Store the authenticated user in the GraphQL DefaultGlobalContext so that it can be retrieved by sub fields
  ContextUtils.saveAuthenticatedUser(context, authenticatedUser);
  return repository.getStuff();
}

Then in my subfield I can do:

@GraphQLQuery
public MoreStuff getMoreStuff(@GraphQLRootContext DefaultGlobalContext<HttpServletRequest> context, @GraphQLContext Stuff stuff) {
   final MyAuthentication auth = ContextUtils.getAuthenticatedUser(context);
   return repository.getMoreStuff(stuff.getStuffId(), auth.getUserId());
}

As you can imagine this is very error prone and most likely I'm bruteforcing something in a way that can probably be done a lot easier. I was thinking of maybe an argument injector could be the way, basically create an argument injector that propagates the security context to subfields? I haven't found an example of something like this just yet but perhaps you have some pointers.

Thanks

Support for Spring Security (needs CSRF Token for graphiql)

When adding spring-boot-starter-security you have to disable CSRF protection, because the graphiql user interface doesn't send csrf token when doing requests to POST /graphql.

Don't know if that's possible at all. Else just close this issue.

how the directive of the client works?

@GraphQLDirective(name = "timeout", locations = Introspection.DirectiveLocation.FIELD)
public static class Interrupt {
    @GraphQLInputField(name = "afterMillis") //Customize the fields as usual
    public int after;
}

{
    books(searchString: "Monkey") @timeout(afterMillis: 500) {
        title
    }
}

throw err:
Validation error of type UnknownDirective: Unknown directive timeout

Connection error after receiving GQL_CONNECTION_INIT

When the client sends a GQL_CONNECTION_INIT (connection_init) message to the server, the server now expects (Jackson) an id field to be present in the message. However, the apollo client doesn't seem to send the id field in this message, so the server responds with a GQL_CONNECTION_ERROR (connection_error) response due to Jackson being unable to parse the message.

The problem seems to be in ApolloProtocolHandler:79 :

 try {
            ApolloMessage apolloMessage;
            try {
                apolloMessage = ApolloMessage.from(message);
            } catch (IOException e) {
                session.sendMessage(ApolloMessage.connectionError());
                return;
            }

Now the question is, is Apollo not following the GraphQL WS specification, or the server is to blame here?

Support for configuring GraphQL object

Currently the DefaultGraphQLController autowires a GraphQLSchema, and builds a default GraphQL object. This doesn't leave room to configure an ExecutionStrategy or Instrumentation. The DefaultGraphQLController could instead autowire a GraphQL, which can be overridden for custom configuration.

how to set basePackages

i want to use GraphQLInterface, but throw error 'not implement interface...'.
i search graphql-spqr, set basePackages will resolve this problem.
but i do not know how to set basePackages in this case.

Support to add custom url than the static /graphql

So I look around and played around a bit and I think letting users customize/have their own url endpoints is also a great addition to this awsome library.

I can push to this repo but if you can just take out the /graphql in the GrpahQLController class and then specify the property in your application.properties file. Sample below

change below

@PostMapping(
            value = "${graphql.spqr.http.endpoint:/graphql}",
            consumes = {"application/graphql", "application/graphql;charset=UTF-8"},
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE
    )

to

@PostMapping(
            value = "${graphql.spqr.http.endpoint}",
            consumes = {"application/graphql", "application/graphql;charset=UTF-8"},
            produces = MediaType.APPLICATION_JSON_UTF8_VALUE
    )

and then place this graphql.spqr.http.endpoint=/customUrl in your projects application.properties file

Annotation to enable SPQR and set packages

Usually, in Spring Boot starters there's a @EnableXyz annotation you use to enable the autoconfiguration and set the packages to scan. It would be useful for this starter to conform to this convention! For example:

@SpringApplication
@EnableSpqr("my.package")
public class MyApplication {}

This also let one enable SPQR in its own starters, without the need to specify the packages in a configuration file.

How to set current user in the context?

I couldn't figure out how to store currently authenticated user in the context so that I can use it for authorisation for example. And does this library provide any easy way to do authorisation?

Use String instead of Enum names in GraphQL queries

Hi, this is kind of the same issue raised here in graphql-spqr, but the solution provided by kaqqao in that issue seems not available with graphql-spqr-spring-boot-starter. Basically kaqqao suggested to implement a custom mapper and register it when generating schema. However while using graphql-spqr-spring-boot-starter, schema is automatically generated and configured. So can we have this feature supported in this spring-boot-starter in the future?

To make the issue clear, suppose I have a Pet class with PetType enum:

public class Pet implements Serializable {
    private String name;
    private PetType type;
}

public enum PetType implements Serializable {
    PUPPY("Dog"),
    KITTY("Cat");

    private final String value;

    PetType(String value) {
        this.value = value;
    }
}

and I have a PetStoreService:

@GraphqlApi
public class PetStoreService {
    @AutoWired
    private PetService petService;

    @GraphQLQuery(name = "getPetsByType")
    public List<Pet> getPetsByType(@GraphqlQLArgument(name = "petType") PetType type) {
        return petService.getPetsByType(type);
    }
}

Now I want my customers to fire GraphQL queries like this:

query {
    getPetsByType(petType: "Dog") {
        name
        type
    }
}

that is, use "Dog" or "Cat" rather than PUPPY or KITTY in queries. Kaqqao mentioned another solution in the previous issue, which is to use @GraphQLEnumValue(name = "Dog") PUPPY. However this annotation just gives each enum type an alias, not a String value. Customers still have to fire queries like this:

query {
    getPetsByType(petType: Dog) {
        name
        type
    }
}

Which is not what I expected. How can I achieve this with graphql-spqr-spring-boot-starter? Thanks a lot!

Customize ObjectMapper in ApolloMessage

Hi, awesome library, thanks for it!

Is it planned to be able to customize ObjectMapper in the ApolloMessage class?

My use case is that I'd like to serialize LocalDateTime as a string (by default it is serialized as an Object, it's really weird).

TypeMappingException: Type io.leangen.graphql.spqr.spring.autoconfigure.DefaultGlobalContext is unbounded or missing generic type parameters

Hi I believe I found a bug, whenever the DefaultGlobalContext is injected without the type being specified for the request object a TypeMappingException is raised:

Caused by: io.leangen.graphql.metadata.exceptions.TypeMappingException: Type io.leangen.graphql.spqr.spring.autoconfigure.DefaultGlobalContext is unbounded or missing generic type parameters
	at io.leangen.graphql.util.ClassUtils.completeGenerics(ClassUtils.java:509) ~[spqr-0.9.9.jar:na]
	at io.leangen.graphql.metadata.strategy.type.DefaultTypeTransformer.transform(DefaultTypeTransformer.java:28) ~[spqr-0.9.9.jar:na]
	at io.leangen.graphql.metadata.strategy.query.AnnotatedArgumentBuilder.buildResolverArguments(AnnotatedArgumentBuilder.java:40) ~[spqr-0.9.9.jar:na]
	... 65 common frames omitted

Here's an example to reproduce:

@GraphQLApi
@Service
public class SpqrBugService {
    @GraphQLNonNull
    @GraphQLQuery(name = "coolPeople")
    public List<String> findCoolPeople(@GraphQLArgument(name = "coolnessFactor") final int factor,
                                       @GraphQLRootContext final DefaultGlobalContext ctx) {

        if (factor > 5) {
            return ImmutableList.of("Me");
        }

        return ImmutableList.of();
    }
}

A workaround is to do:

@GraphQLRootContext final DefaultGlobalContext<Object> ctx

graphql-spqr version 0.9.9
graphql-spqr-spring-boot-starter 0.0.4

[Autoconfigure] First impressions

Hi there!

Congratulations on this amazing GraphQL spring boot extension, it was really easy to setup our current spring boot project to use this graphql-spqr-spring-boot-autoconfigure dependency.

To help this project get even better, I would like to suggest a few improvements:

  • Remove the SPQR banner. This is a dependency, so it should not add a banner to the project.
  • Document the spring-boot-starter-websocket dependency. Without this the application crashes after booting up.

Best regards,
Rafael Renan Pacheco.

Start Project using Spring boot

When I import dependencies, my project not finding annotations, like @GraphQLAp, how I setup it?

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <!--Will be optional as of 0.0.2-->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
  </dependency>
  <dependency>
    <groupId>io.leangen.graphql</groupId>
    <artifactId>graphiql-spring-boot-starter</artifactId>
    <version>0.0.1</version>
  </dependency>
  <!--Will be implicit as of 0.0.2-->
  <dependency>
    <groupId>io.leangen.graphql</groupId>
    <artifactId>spqr</artifactId>
    <version>0.9.7</version>
  </dependency>
</dependencies>

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.