All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prompto.graphql.GraphQLServlet Maven / Gradle / Ivy

The newest version!
package prompto.graphql;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.SchemaPrinter;
import graphql.schema.idl.SchemaPrinter.Options;
import prompto.code.ICodeStore;
import prompto.declaration.IDeclaration;
import prompto.server.CleverServlet;
import prompto.utils.Instance;
import prompto.utils.JsonUtils;
import prompto.utils.StreamUtils;

@SuppressWarnings("serial")
public class GraphQLServlet extends CleverServlet {

	static Instance instance = new Instance<>();
	static boolean FORCE_ENABLED = false;
	
	public static boolean isEnabled() {
		if(FORCE_ENABLED)
			return true;
		Iterable decls = ICodeStore.getInstance().fetchDeclarationsWithAnnotations(new HashSet<>(Arrays.asList("@GraphQLQuery", "@GraphQLMutation")));
		return decls.iterator().hasNext();
	}

	public static void reset() {
		if(instance.get()!=null) {
			instance.get().schema = null;
			instance.get().graphQL = null;
		}
		GraphQLType.TYPE_BY_NAME_MAP.clear();
	}

	static class GraphQLRequest {
		String query;
		Map variables;
	}

	GraphQLSchema schema;
	GraphQL graphQL;

	public GraphQLServlet() {
		instance.set(this);
	}
	
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try {
			unsafeDoPost(request, response);
		} catch (Throwable t) {
			t.printStackTrace(System.err);
			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
		}
	}

	void unsafeDoPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try(var input = request.getInputStream()) {
			GraphQLRequest gql = readRequest(request, input);
			ExecutionResult result = executeRequest(gql);
			String json = JsonUtils.objectToJson(result.toSpecification());
			try(var writer = response.getWriter()) {
				response.setContentType("application/json");
				writer.print(json);
			}
		}
	}

	private ExecutionResult executeRequest(GraphQLRequest request) {
		ExecutionInput input = ExecutionInput.newExecutionInput()
			.query(request.query)
			.variables(request.variables)
			.build();
		GraphQL gql = getGraphQL();
		return gql.execute(input);
	}
	
	GraphQLSchema getSchema() {
		if(schema==null) synchronized(this) {
			schema = new GraphQLSchemaBuilder().build();
		}
		return schema;
	}

	public String getSDL() {
		var options = Options.defaultOptions().includeDirectives(false);
		return new SchemaPrinter(options).print(getSchema());
	}

	GraphQL getGraphQL() {
		if(graphQL==null) synchronized(this) {
			graphQL = GraphQL.newGraphQL(getSchema()).build();
		}
		return graphQL;
	}

	private GraphQLRequest readRequest(HttpServletRequest request, InputStream input) throws IOException, ServletException {
		String[] contentParts = request.getContentType().split(";");
		String contentType = contentParts[0];
		switch (contentType) {
		case "application/graphql":
			return readRequestFromGraphQL(input);
		case "application/json":
			return readRequestFromJSON(input);
		case "multipart/form-data":
			return readRequestFromMultipart(request);
		default:
			throw new UnsupportedOperationException("Unsupported content type: " + contentType);
		}
	}

	private GraphQLRequest readRequestFromMultipart(HttpServletRequest request) throws IOException, ServletException {
		Part part = request.getPart("operations");
		if (part == null)
			throw new UnsupportedOperationException("Missing 'operations' part");
		else {
			GraphQLRequest gql = readRequestFromPart(part);
			Map uploads = readRequestUploads(request);
			linkUploadVariables(gql.variables, uploads);
			return gql;
		}
	}

	private void linkUploadVariables(Map variables, Map uploads) {
		uploads.forEach((path, part)->linkUploadVariable(variables, path, part));
	}

	private void linkUploadVariable(Map variables, String path, Part part) {
		String[] parts =path.split("\\.");
		if(parts.length==1)
			variables.put(parts[0], part);
		else
			throw new UnsupportedOperationException("yet");
	}

	private Map readRequestUploads(HttpServletRequest request) throws IOException, ServletException {
		Part part = request.getPart("map");
		if (part == null)
			throw new UnsupportedOperationException("Missing 'map' part");
		else
			return readRequestUploads(request, part);
	}

	@SuppressWarnings("unchecked")
	private Map readRequestUploads(HttpServletRequest request, Part part) throws IOException, ServletException {
		try (InputStream input = part.getInputStream()) {
			ObjectNode json = (ObjectNode) JsonUtils.parseInput(input);
			Map> map = (Map>) (Object) JsonUtils.toMap(json);
			Map uploads = new HashMap<>();
			for(Map.Entry> entry : map.entrySet()) {
				Part file = request.getPart(entry.getKey());
				entry.getValue().forEach(s->uploads.put(s, file));
			}
			return uploads;
		}
	}

	private GraphQLRequest readRequestFromPart(Part part) throws IOException {
		try (InputStream input = part.getInputStream()) {
			String data = StreamUtils.readString(input);
			return readRequestFromJSON(data);
		}
	}

	private GraphQLRequest readRequestFromJSON(InputStream input) throws IOException {
		String query = StreamUtils.readString(input);
		return readRequestFromJSON(query);
	}

	private GraphQLRequest readRequestFromJSON(String query) throws IOException {
		JsonNode node = JsonUtils.parseString(query);
		return readRequestFromJSON(node);
	}

	private GraphQLRequest readRequestFromJSON(JsonNode json) {
		if (json.has("query") && json.get("query").isTextual()) {
			GraphQLRequest request = new GraphQLRequest();
			request.query = json.get("query").asText();
			if (json.has("variables") && json.get("variables").isObject()) {
				ObjectNode vars = (ObjectNode) json.get("variables");
				request.variables = JsonUtils.toMap(vars);
			} else
				request.variables = Collections.emptyMap();
			return request;
		} else
			throw new UnsupportedOperationException("Missing 'query' field: " + json.toString());
	}

	private GraphQLRequest readRequestFromGraphQL(InputStream input) throws IOException {
		GraphQLRequest request = new GraphQLRequest();
		request.query = StreamUtils.readString(input);
		request.variables = Collections.emptyMap();
		return request;
	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy