package io.vertx.ext.web.handler.graphql.impl;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.web.handler.graphql.GraphiQLHandler;
import io.vertx.ext.web.handler.graphql.GraphiQLHandlerOptions;
import io.vertx.ext.web.impl.Utils;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.SECONDS;
public class GraphiQLHandlerImpl implements GraphiQLHandler {
private static final Logger log = LoggerFactory.getLogger(GraphiQLHandlerImpl.class);
private static final String WEBROOT = "io/vertx/ext/web/handler/graphiql";
private static final Function<RoutingContext, MultiMap> DEFAULT_GRAPHIQL_REQUEST_HEADERS_FACTORY = rc -> null;
private final GraphiQLHandlerOptions options;
private final StaticHandler staticHandler;
private Function<RoutingContext, MultiMap> graphiQLRequestHeadersFactory = DEFAULT_GRAPHIQL_REQUEST_HEADERS_FACTORY;
public GraphiQLHandlerImpl(GraphiQLHandlerOptions options) {
Objects.requireNonNull(options, "options");
this.options = options;
staticHandler = options.isEnabled() ? StaticHandler.create(WEBROOT).setCachingEnabled(true).setMaxAgeSeconds(SECONDS.convert(365, DAYS)) : null;
}
@Override
public GraphiQLHandler graphiQLRequestHeaders(Function<RoutingContext, MultiMap> factory) {
graphiQLRequestHeadersFactory = factory != null ? factory : DEFAULT_GRAPHIQL_REQUEST_HEADERS_FACTORY;
return this;
}
@Override
public void handle(RoutingContext rc) {
if (!options.isEnabled()) {
rc.next();
return;
}
String filename = Utils.pathOffset(rc.normalizedPath(), rc);
if (filename.isEmpty()) {
rc.response().setStatusCode(301).putHeader(HttpHeaders.LOCATION, rc.currentRoute().getPath()).end();
return;
}
if ("/".equals(filename) || "/index.html".equals(filename)) {
String resource = rc.vertx().fileSystem()
.readFileBlocking(WEBROOT + "/index.html")
.toString(UTF_8)
.replace("__VERTX_GRAPHIQL_CONFIG__", replacement(rc));
rc.response()
.putHeader(HttpHeaders.CACHE_CONTROL, "no-cache")
.putHeader(HttpHeaders.CONTENT_TYPE, "text/html;charset=utf8")
.end(resource);
} else {
staticHandler.handle(rc);
}
}
private String replacement(RoutingContext rc) {
JsonObject json = new JsonObject();
if (options.getGraphQLUri() != null) {
json.put("graphQLUri", options.getGraphQLUri());
}
MultiMap headers = MultiMap.caseInsensitiveMultiMap();
Map<String, String> fixedHeaders = options.getHeaders();
if (fixedHeaders != null) {
fixedHeaders.forEach(headers::add);
}
Function<RoutingContext, MultiMap> rh;
synchronized (this) {
rh = this.graphiQLRequestHeadersFactory;
}
MultiMap dynamicHeaders = rh.apply(rc);
if (dynamicHeaders != null) {
headers.addAll(dynamicHeaders);
}
if (!headers.isEmpty()) {
JsonObject headersJson = new JsonObject();
headers.forEach(header -> headersJson.put(header.getKey(), header.getValue()));
json.put("headers", headersJson);
}
if (options.getQuery() != null) {
json.put("query", options.getQuery());
}
if (options.getVariables() != null) {
json.put("parameters", options.getVariables());
}
return json.encode();
}
}