diff --git a/gradle.properties b/gradle.properties index baa679d7..065725ec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -version = 6.2.1-SNAPSHOT +version = 7.0.0-SNAPSHOT group = com.graphql-java-kickstart LIB_GRAPHQL_JAVA_VER = 11.0 -LIB_JACKSON_VER = 2.8.11 +LIB_JACKSON_VER = 2.9.7 diff --git a/src/main/java/graphql/servlet/AbstractGraphQLHttpServlet.java b/src/main/java/graphql/servlet/AbstractGraphQLHttpServlet.java index ffba1809..7539c17e 100644 --- a/src/main/java/graphql/servlet/AbstractGraphQLHttpServlet.java +++ b/src/main/java/graphql/servlet/AbstractGraphQLHttpServlet.java @@ -12,6 +12,7 @@ import javax.servlet.AsyncContext; import javax.servlet.Servlet; +import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -24,7 +25,6 @@ import java.io.Writer; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -50,31 +50,66 @@ public abstract class AbstractGraphQLHttpServlet extends HttpServlet implements private static final GraphQLRequest INTROSPECTION_REQUEST = new GraphQLRequest(IntrospectionQuery.INTROSPECTION_QUERY, new HashMap<>(), null); private static final String[] MULTIPART_KEYS = new String[]{"operations", "graphql", "query"}; + private GraphQLConfiguration configuration; + + /** + * @deprecated override {@link #getConfiguration()} instead + */ + @Deprecated protected abstract GraphQLQueryInvoker getQueryInvoker(); + /** + * @deprecated override {@link #getConfiguration()} instead + */ + @Deprecated protected abstract GraphQLInvocationInputFactory getInvocationInputFactory(); + /** + * @deprecated override {@link #getConfiguration()} instead + */ + @Deprecated protected abstract GraphQLObjectMapper getGraphQLObjectMapper(); - private final List listeners; + /** + * @deprecated override {@link #getConfiguration()} instead + */ + @Deprecated + protected abstract boolean isAsyncServletMode(); + + protected GraphQLConfiguration getConfiguration() { + return GraphQLConfiguration.with(getInvocationInputFactory()) + .with(getQueryInvoker()) + .with(getGraphQLObjectMapper()) + .with(isAsyncServletMode()) + .with(listeners) + .build(); + } - private final HttpRequestHandler getHandler; - private final HttpRequestHandler postHandler; + /** + * @deprecated use {@link #getConfiguration()} instead + */ + @Deprecated + private final List listeners; - private final boolean asyncServletMode; + private HttpRequestHandler getHandler; + private HttpRequestHandler postHandler; public AbstractGraphQLHttpServlet() { - this(null, false); + this(null); } - public AbstractGraphQLHttpServlet(List listeners, boolean asyncServletMode) { + public AbstractGraphQLHttpServlet(List listeners) { this.listeners = listeners != null ? new ArrayList<>(listeners) : new ArrayList<>(); - this.asyncServletMode = asyncServletMode; + } + + @Override + public void init(ServletConfig servletConfig) { + this.configuration = getConfiguration(); this.getHandler = (request, response) -> { - GraphQLInvocationInputFactory invocationInputFactory = getInvocationInputFactory(); - GraphQLObjectMapper graphQLObjectMapper = getGraphQLObjectMapper(); - GraphQLQueryInvoker queryInvoker = getQueryInvoker(); + GraphQLInvocationInputFactory invocationInputFactory = configuration.getInvocationInputFactory(); + GraphQLObjectMapper graphQLObjectMapper = configuration.getObjectMapper(); + GraphQLQueryInvoker queryInvoker = configuration.getQueryInvoker(); String path = request.getPathInfo(); if (path == null) { @@ -106,9 +141,9 @@ public AbstractGraphQLHttpServlet(List listeners, boolea }; this.postHandler = (request, response) -> { - GraphQLInvocationInputFactory invocationInputFactory = getInvocationInputFactory(); - GraphQLObjectMapper graphQLObjectMapper = getGraphQLObjectMapper(); - GraphQLQueryInvoker queryInvoker = getQueryInvoker(); + GraphQLInvocationInputFactory invocationInputFactory = configuration.getInvocationInputFactory(); + GraphQLObjectMapper graphQLObjectMapper = configuration.getObjectMapper(); + GraphQLQueryInvoker queryInvoker = configuration.getQueryInvoker(); try { if (APPLICATION_GRAPHQL.equals(request.getContentType())) { @@ -116,12 +151,12 @@ public AbstractGraphQLHttpServlet(List listeners, boolea query(queryInvoker, graphQLObjectMapper, invocationInputFactory.create(new GraphQLRequest(query, null, null)), response); } else if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data") && !request.getParts().isEmpty()) { final Map> fileItems = request.getParts() - .stream() - .collect(Collectors.groupingBy(Part::getName)); + .stream() + .collect(Collectors.groupingBy(Part::getName)); for (String key : MULTIPART_KEYS) { // Check to see if there is a part under the key we seek - if(!fileItems.containsKey(key)) { + if (!fileItems.containsKey(key)) { continue; } @@ -134,20 +169,20 @@ public AbstractGraphQLHttpServlet(List listeners, boolea InputStream inputStream = asMarkableInputStream(queryItem.get().getInputStream()); final Optional>> variablesMap = - getFileItem(fileItems, "map").map(graphQLObjectMapper::deserializeMultipartMap); + getFileItem(fileItems, "map").map(graphQLObjectMapper::deserializeMultipartMap); if (isBatchedQuery(inputStream)) { List graphQLRequests = - graphQLObjectMapper.readBatchedGraphQLRequest(inputStream); + graphQLObjectMapper.readBatchedGraphQLRequest(inputStream); variablesMap.ifPresent(map -> graphQLRequests.forEach(r -> mapMultipartVariables(r, map, fileItems))); GraphQLBatchedInvocationInput invocationInput = - invocationInputFactory.create(graphQLRequests, request, response); + invocationInputFactory.create(graphQLRequests, request, response); invocationInput.getContext().setParts(fileItems); queryBatched(queryInvoker, graphQLObjectMapper, invocationInput, response); return; } else { GraphQLRequest graphQLRequest; - if("query".equals(key)) { + if ("query".equals(key)) { graphQLRequest = buildRequestFromQuery(inputStream, graphQLObjectMapper, fileItems); } else { graphQLRequest = graphQLObjectMapper.readGraphQLRequest(inputStream); @@ -155,7 +190,7 @@ public AbstractGraphQLHttpServlet(List listeners, boolea variablesMap.ifPresent(m -> mapMultipartVariables(graphQLRequest, m, fileItems)); GraphQLSingleInvocationInput invocationInput = - invocationInputFactory.create(graphQLRequest, request, response); + invocationInputFactory.create(graphQLRequest, request, response); invocationInput.getContext().setParts(fileItems); query(queryInvoker, graphQLObjectMapper, invocationInput, response); return; @@ -190,8 +225,7 @@ private static InputStream asMarkableInputStream(InputStream inputStream) { private GraphQLRequest buildRequestFromQuery(InputStream inputStream, GraphQLObjectMapper graphQLObjectMapper, - Map> fileItems) throws IOException - { + Map> fileItems) throws IOException { GraphQLRequest graphQLRequest; String query = new String(ByteStreams.toByteArray(inputStream)); @@ -213,49 +247,48 @@ private GraphQLRequest buildRequestFromQuery(InputStream inputStream, private void mapMultipartVariables(GraphQLRequest request, Map> variablesMap, - Map> fileItems) - { + Map> fileItems) { Map variables = request.getVariables(); variablesMap.forEach((partName, objectPaths) -> { Part part = getFileItem(fileItems, partName) - .orElseThrow(() -> new RuntimeException("unable to find part name " + - partName + - " as referenced in the variables map")); + .orElseThrow(() -> new RuntimeException("unable to find part name " + + partName + + " as referenced in the variables map")); objectPaths.forEach(objectPath -> VariableMapper.mapVariable(objectPath, variables, part)); }); } public void addListener(GraphQLServletListener servletListener) { - listeners.add(servletListener); + configuration.add(servletListener); } public void removeListener(GraphQLServletListener servletListener) { - listeners.remove(servletListener); + configuration.remove(servletListener); } @Override public String[] getQueries() { - return getInvocationInputFactory().getSchemaProvider().getSchema().getQueryType().getFieldDefinitions().stream().map(GraphQLFieldDefinition::getName).toArray(String[]::new); + return configuration.getInvocationInputFactory().getSchemaProvider().getSchema().getQueryType().getFieldDefinitions().stream().map(GraphQLFieldDefinition::getName).toArray(String[]::new); } @Override public String[] getMutations() { - return getInvocationInputFactory().getSchemaProvider().getSchema().getMutationType().getFieldDefinitions().stream().map(GraphQLFieldDefinition::getName).toArray(String[]::new); + return configuration.getInvocationInputFactory().getSchemaProvider().getSchema().getMutationType().getFieldDefinitions().stream().map(GraphQLFieldDefinition::getName).toArray(String[]::new); } @Override public String executeQuery(String query) { try { - return getGraphQLObjectMapper().serializeResultAsJson(getQueryInvoker().query(getInvocationInputFactory().create(new GraphQLRequest(query, new HashMap<>(), null)))); + return configuration.getObjectMapper().serializeResultAsJson(configuration.getQueryInvoker().query(configuration.getInvocationInputFactory().create(new GraphQLRequest(query, new HashMap<>(), null)))); } catch (Exception e) { return e.getMessage(); } } private void doRequestAsync(HttpServletRequest request, HttpServletResponse response, HttpRequestHandler handler) { - if (asyncServletMode) { + if (configuration.isAsyncServletModeEnabled()) { AsyncContext asyncContext = request.startAsync(); HttpServletRequest asyncRequest = (HttpServletRequest) asyncContext.getRequest(); HttpServletResponse asyncResponse = (HttpServletResponse) asyncContext.getResponse(); @@ -324,11 +357,7 @@ private void queryBatched(GraphQLQueryInvoker queryInvoker, GraphQLObjectMapper } private List runListeners(Function action) { - if (listeners == null) { - return Collections.emptyList(); - } - - return listeners.stream() + return configuration.getListeners().stream() .map(listener -> { try { return action.apply(listener); diff --git a/src/main/java/graphql/servlet/ConfiguredGraphQLHttpServlet.java b/src/main/java/graphql/servlet/ConfiguredGraphQLHttpServlet.java new file mode 100644 index 00000000..c00fdf1c --- /dev/null +++ b/src/main/java/graphql/servlet/ConfiguredGraphQLHttpServlet.java @@ -0,0 +1,18 @@ +package graphql.servlet; + +import java.util.Objects; + +class ConfiguredGraphQLHttpServlet extends GraphQLHttpServlet { + + private GraphQLConfiguration configuration; + + ConfiguredGraphQLHttpServlet(GraphQLConfiguration configuration) { + this.configuration = Objects.requireNonNull(configuration, "configuration is required"); + } + + @Override + protected GraphQLConfiguration getConfiguration() { + return configuration; + } + +} diff --git a/src/main/java/graphql/servlet/DefaultGraphQLServlet.java b/src/main/java/graphql/servlet/DefaultGraphQLServlet.java new file mode 100644 index 00000000..76b11824 --- /dev/null +++ b/src/main/java/graphql/servlet/DefaultGraphQLServlet.java @@ -0,0 +1,33 @@ +package graphql.servlet; + +import javax.servlet.ServletConfig; + +public class DefaultGraphQLServlet extends AbstractGraphQLHttpServlet { + + @Override + public void init(ServletConfig servletConfig) { + + super.init(servletConfig); + } + + @Override + protected GraphQLInvocationInputFactory getInvocationInputFactory() { + return null; + } + + @Override + protected GraphQLQueryInvoker getQueryInvoker() { + return GraphQLQueryInvoker.newBuilder().build(); + } + + @Override + protected GraphQLObjectMapper getGraphQLObjectMapper() { + return GraphQLObjectMapper.newBuilder().build(); + } + + @Override + protected boolean isAsyncServletMode() { + return false; + } + +} diff --git a/src/main/java/graphql/servlet/GraphQLConfiguration.java b/src/main/java/graphql/servlet/GraphQLConfiguration.java new file mode 100644 index 00000000..6d93f323 --- /dev/null +++ b/src/main/java/graphql/servlet/GraphQLConfiguration.java @@ -0,0 +1,114 @@ +package graphql.servlet; + +import graphql.schema.GraphQLSchema; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class GraphQLConfiguration { + + private GraphQLInvocationInputFactory invocationInputFactory; + private GraphQLQueryInvoker queryInvoker = GraphQLQueryInvoker.newBuilder().build(); + private GraphQLObjectMapper objectMapper = GraphQLObjectMapper.newBuilder().build(); + private List listeners; + private boolean asyncServletModeEnabled; + + public static GraphQLConfiguration.Builder with(GraphQLSchema schema) { + return with(new DefaultGraphQLSchemaProvider(schema)); + } + + public static GraphQLConfiguration.Builder with(GraphQLSchemaProvider schemaProvider) { + return new Builder(GraphQLInvocationInputFactory.newBuilder(schemaProvider)); + } + + static GraphQLConfiguration.Builder with(GraphQLInvocationInputFactory invocationInputFactory) { + return new Builder(invocationInputFactory); + } + + private GraphQLConfiguration(GraphQLInvocationInputFactory invocationInputFactory, GraphQLQueryInvoker queryInvoker, GraphQLObjectMapper objectMapper, List listeners, boolean asyncServletModeEnabled) { + this.invocationInputFactory = invocationInputFactory; + this.queryInvoker = queryInvoker; + this.objectMapper = objectMapper; + this.listeners = listeners; + this.asyncServletModeEnabled = asyncServletModeEnabled; + } + + public GraphQLInvocationInputFactory getInvocationInputFactory() { + return invocationInputFactory; + } + + public GraphQLQueryInvoker getQueryInvoker() { + return queryInvoker; + } + + public GraphQLObjectMapper getObjectMapper() { + return objectMapper; + } + + public List getListeners() { + return new ArrayList<>(listeners); + } + + public boolean isAsyncServletModeEnabled() { + return asyncServletModeEnabled; + } + + public void add(GraphQLServletListener listener) { + listeners.add(listener); + } + + public boolean remove(GraphQLServletListener listener) { + return listeners.remove(listener); + } + + public static class Builder { + + private GraphQLInvocationInputFactory.Builder invocationInputFactoryBuilder; + private GraphQLInvocationInputFactory invocationInputFactory; + private GraphQLQueryInvoker queryInvoker = GraphQLQueryInvoker.newBuilder().build(); + private GraphQLObjectMapper objectMapper = GraphQLObjectMapper.newBuilder().build(); + private List listeners = new ArrayList<>(); + private boolean asyncServletModeEnabled = false; + + private Builder(GraphQLInvocationInputFactory.Builder invocationInputFactoryBuilder) { + this.invocationInputFactoryBuilder = invocationInputFactoryBuilder; + } + + private Builder(GraphQLInvocationInputFactory invocationInputFactory) { + this.invocationInputFactory = invocationInputFactory; + } + + public Builder with(GraphQLQueryInvoker queryInvoker) { + this.queryInvoker = queryInvoker; + return this; + } + + public Builder with(GraphQLObjectMapper objectMapper) { + this.objectMapper = objectMapper; + return this; + } + + public Builder with(List listeners) { + this.listeners = Objects.requireNonNull(listeners, "listeners must not be null"); + return this; + } + + public Builder with(boolean asyncServletModeEnabled) { + this.asyncServletModeEnabled = asyncServletModeEnabled; + return this; + } + + public GraphQLConfiguration build() { + return new GraphQLConfiguration( + this.invocationInputFactory != null ? this.invocationInputFactory : invocationInputFactoryBuilder.build(), + queryInvoker, + objectMapper, + listeners, + asyncServletModeEnabled + ); + } + + } + +} diff --git a/src/main/java/graphql/servlet/GraphQLHttpServlet.java b/src/main/java/graphql/servlet/GraphQLHttpServlet.java new file mode 100644 index 00000000..0106c22e --- /dev/null +++ b/src/main/java/graphql/servlet/GraphQLHttpServlet.java @@ -0,0 +1,45 @@ +package graphql.servlet; + +import graphql.schema.GraphQLSchema; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author Michiel Oliemans + */ +public abstract class GraphQLHttpServlet extends AbstractGraphQLHttpServlet { + + public static GraphQLHttpServlet with(GraphQLSchema schema) { + return new ConfiguredGraphQLHttpServlet(GraphQLConfiguration.with(schema).build()); + } + + public static GraphQLHttpServlet with(GraphQLConfiguration configuration) { + return new ConfiguredGraphQLHttpServlet(configuration); + } + + @Override + protected abstract GraphQLConfiguration getConfiguration(); + + @Override + protected GraphQLQueryInvoker getQueryInvoker() { + throw new UnsupportedOperationException(); + } + + @Override + protected GraphQLInvocationInputFactory getInvocationInputFactory() { + throw new UnsupportedOperationException(); + } + + @Override + protected GraphQLObjectMapper getGraphQLObjectMapper() { + throw new UnsupportedOperationException(); + } + + @Override + protected boolean isAsyncServletMode() { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/main/java/graphql/servlet/OsgiGraphQLHttpServlet.java b/src/main/java/graphql/servlet/OsgiGraphQLHttpServlet.java index 76a6dbc0..2cec68bf 100644 --- a/src/main/java/graphql/servlet/OsgiGraphQLHttpServlet.java +++ b/src/main/java/graphql/servlet/OsgiGraphQLHttpServlet.java @@ -83,6 +83,11 @@ protected GraphQLObjectMapper getGraphQLObjectMapper() { return graphQLObjectMapper; } + @Override + protected boolean isAsyncServletMode() { + return false; + } + public OsgiGraphQLHttpServlet() { updateSchema(); diff --git a/src/main/java/graphql/servlet/SimpleAbstractGraphQLServlet.java b/src/main/java/graphql/servlet/SimpleAbstractGraphQLServlet.java deleted file mode 100644 index b00adf61..00000000 --- a/src/main/java/graphql/servlet/SimpleAbstractGraphQLServlet.java +++ /dev/null @@ -1,20 +0,0 @@ -package graphql.servlet; - -public abstract class SimpleAbstractGraphQLServlet extends AbstractGraphQLHttpServlet { - - @Override - protected GraphQLQueryInvoker getQueryInvoker() { - return null; - } - - @Override - protected GraphQLInvocationInputFactory getInvocationInputFactory() { - return null; - } - - @Override - protected GraphQLObjectMapper getGraphQLObjectMapper() { - return null; - } - -} diff --git a/src/main/java/graphql/servlet/SimpleGraphQLHttpServlet.java b/src/main/java/graphql/servlet/SimpleGraphQLHttpServlet.java index 24341660..7a3cec1f 100644 --- a/src/main/java/graphql/servlet/SimpleGraphQLHttpServlet.java +++ b/src/main/java/graphql/servlet/SimpleGraphQLHttpServlet.java @@ -2,38 +2,66 @@ import graphql.schema.GraphQLSchema; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * @author Andrew Potter */ public class SimpleGraphQLHttpServlet extends AbstractGraphQLHttpServlet { - private final GraphQLInvocationInputFactory invocationInputFactory; - private final GraphQLQueryInvoker queryInvoker; - private final GraphQLObjectMapper graphQLObjectMapper; + private GraphQLConfiguration configuration; - // protected to allow class to be extended - protected SimpleGraphQLHttpServlet(GraphQLInvocationInputFactory invocationInputFactory, GraphQLQueryInvoker queryInvoker, GraphQLObjectMapper graphQLObjectMapper, List listeners, boolean asyncServletMode) { - super(listeners, asyncServletMode); - this.invocationInputFactory = invocationInputFactory; - this.queryInvoker = queryInvoker; - this.graphQLObjectMapper = graphQLObjectMapper; + public SimpleGraphQLHttpServlet() { + } + + /** + * @param invocationInputFactory + * @param queryInvoker + * @param graphQLObjectMapper + * @param listeners + * @param asyncServletMode + * @deprecated + */ + @Deprecated + public SimpleGraphQLHttpServlet(GraphQLInvocationInputFactory invocationInputFactory, GraphQLQueryInvoker queryInvoker, GraphQLObjectMapper graphQLObjectMapper, List listeners, boolean asyncServletMode) { + super(listeners); + this.configuration = GraphQLConfiguration.with(invocationInputFactory) + .with(queryInvoker) + .with(graphQLObjectMapper) + .with(listeners != null ? listeners : new ArrayList<>()) + .with(asyncServletMode) + .build(); + } + + private SimpleGraphQLHttpServlet(GraphQLConfiguration configuration) { + this.configuration = Objects.requireNonNull(configuration, "configuration is required"); + } + + @Override + protected GraphQLConfiguration getConfiguration() { + return configuration; } @Override protected GraphQLQueryInvoker getQueryInvoker() { - return queryInvoker; + return configuration.getQueryInvoker(); } @Override protected GraphQLInvocationInputFactory getInvocationInputFactory() { - return invocationInputFactory; + return configuration.getInvocationInputFactory(); } @Override protected GraphQLObjectMapper getGraphQLObjectMapper() { - return graphQLObjectMapper; + return configuration.getObjectMapper(); + } + + @Override + protected boolean isAsyncServletMode() { + return configuration.isAsyncServletModeEnabled(); } public static Builder newBuilder(GraphQLSchema schema) { @@ -79,8 +107,15 @@ public Builder withListeners(List listeners) { return this; } + @Deprecated public SimpleGraphQLHttpServlet build() { - return new SimpleGraphQLHttpServlet(invocationInputFactory, queryInvoker, graphQLObjectMapper, listeners, asyncServletMode); + GraphQLConfiguration configuration = GraphQLConfiguration.with(invocationInputFactory) + .with(queryInvoker) + .with(graphQLObjectMapper) + .with(listeners != null ? listeners : new ArrayList<>()) + .with(asyncServletMode) + .build(); + return new SimpleGraphQLHttpServlet(configuration); } } } diff --git a/src/test/groovy/graphql/servlet/AbstractGraphQLHttpServletSpec.groovy b/src/test/groovy/graphql/servlet/AbstractGraphQLHttpServletSpec.groovy index 41f81763..b84a4277 100644 --- a/src/test/groovy/graphql/servlet/AbstractGraphQLHttpServletSpec.groovy +++ b/src/test/groovy/graphql/servlet/AbstractGraphQLHttpServletSpec.groovy @@ -893,6 +893,7 @@ class AbstractGraphQLHttpServletSpec extends Specification { servlet = SimpleGraphQLHttpServlet.newBuilder(GraphQLInvocationInputFactory.newBuilder { throw new TestException() }.build()).build() + servlet.init(null) request.setPathInfo('/schema.json') @@ -992,7 +993,7 @@ class AbstractGraphQLHttpServletSpec extends Specification { def "typeInfo is serialized correctly"() { expect: - servlet.getGraphQLObjectMapper().getJacksonMapper().writeValueAsString(ExecutionStepInfo.newExecutionStepInfo().type(new GraphQLNonNull(Scalars.GraphQLString)).build()) != "{}" + servlet.getConfiguration().getObjectMapper().getJacksonMapper().writeValueAsString(ExecutionStepInfo.newExecutionStepInfo().type(new GraphQLNonNull(Scalars.GraphQLString)).build()) != "{}" } @Ignore diff --git a/src/test/groovy/graphql/servlet/TestUtils.groovy b/src/test/groovy/graphql/servlet/TestUtils.groovy index f3a4df5a..3c04e421 100644 --- a/src/test/groovy/graphql/servlet/TestUtils.groovy +++ b/src/test/groovy/graphql/servlet/TestUtils.groovy @@ -8,7 +8,9 @@ class TestUtils { static def createServlet(DataFetcher queryDataFetcher = { env -> env.arguments.arg }, DataFetcher mutationDataFetcher = { env -> env.arguments.arg }) { - return SimpleGraphQLHttpServlet.newBuilder(createGraphQlSchema(queryDataFetcher, mutationDataFetcher)).build() + GraphQLHttpServlet servlet = GraphQLHttpServlet.with(createGraphQlSchema(queryDataFetcher, mutationDataFetcher)) + servlet.init(null) + return servlet } static def createGraphQlSchema(DataFetcher queryDataFetcher = { env -> env.arguments.arg },