graphql.servlet.AbstractGraphQLHttpServlet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of graphql-java-servlet Show documentation
Show all versions of graphql-java-servlet Show documentation
relay.js-compatible GraphQL servlet
package graphql.servlet;
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import graphql.ExecutionResult;
import graphql.introspection.IntrospectionQuery;
import graphql.schema.GraphQLFieldDefinition;
import graphql.servlet.internal.GraphQLRequest;
import graphql.servlet.internal.VariableMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author Andrew Potter
*/
public abstract class AbstractGraphQLHttpServlet extends HttpServlet implements Servlet, GraphQLMBean {
public static final Logger log = LoggerFactory.getLogger(AbstractGraphQLHttpServlet.class);
public static final String APPLICATION_JSON_UTF8 = "application/json;charset=UTF-8";
public static final String APPLICATION_GRAPHQL = "application/graphql";
public static final int STATUS_OK = 200;
public static final int STATUS_BAD_REQUEST = 400;
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();
/**
* @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();
}
/**
* @deprecated use {@link #getConfiguration()} instead
*/
@Deprecated
private final List listeners;
private HttpRequestHandler getHandler;
private HttpRequestHandler postHandler;
public AbstractGraphQLHttpServlet() {
this(null);
}
public AbstractGraphQLHttpServlet(List listeners) {
this.listeners = listeners != null ? new ArrayList<>(listeners) : new ArrayList<>();
}
@Override
public void init(ServletConfig servletConfig) {
this.configuration = getConfiguration();
this.getHandler = (request, response) -> {
GraphQLInvocationInputFactory invocationInputFactory = configuration.getInvocationInputFactory();
GraphQLObjectMapper graphQLObjectMapper = configuration.getObjectMapper();
GraphQLQueryInvoker queryInvoker = configuration.getQueryInvoker();
String path = request.getPathInfo();
if (path == null) {
path = request.getServletPath();
}
if (path.contentEquals("/schema.json")) {
query(queryInvoker, graphQLObjectMapper, invocationInputFactory.create(INTROSPECTION_REQUEST, request, response), response);
} else {
String query = request.getParameter("query");
if (query != null) {
if (isBatchedQuery(query)) {
queryBatched(queryInvoker, graphQLObjectMapper, invocationInputFactory.createReadOnly(graphQLObjectMapper.readBatchedGraphQLRequest(query), request, response), response);
} else {
final Map variables = new HashMap<>();
if (request.getParameter("variables") != null) {
variables.putAll(graphQLObjectMapper.deserializeVariables(request.getParameter("variables")));
}
String operationName = request.getParameter("operationName");
query(queryInvoker, graphQLObjectMapper, invocationInputFactory.createReadOnly(new GraphQLRequest(query, variables, operationName), request, response), response);
}
} else {
response.setStatus(STATUS_BAD_REQUEST);
log.info("Bad GET request: path was not \"/schema.json\" or no query variable named \"query\" given");
}
}
};
this.postHandler = (request, response) -> {
GraphQLInvocationInputFactory invocationInputFactory = configuration.getInvocationInputFactory();
GraphQLObjectMapper graphQLObjectMapper = configuration.getObjectMapper();
GraphQLQueryInvoker queryInvoker = configuration.getQueryInvoker();
try {
if (APPLICATION_GRAPHQL.equals(request.getContentType())) {
String query = CharStreams.toString(request.getReader());
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));
for (String key : MULTIPART_KEYS) {
// Check to see if there is a part under the key we seek
if (!fileItems.containsKey(key)) {
continue;
}
final Optional queryItem = getFileItem(fileItems, key);
if (!queryItem.isPresent()) {
// If there is a part, but we don't see an item, then break and return BAD_REQUEST
break;
}
InputStream inputStream = asMarkableInputStream(queryItem.get().getInputStream());
final Optional