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

com.fleetpin.graphql.aws.lambda.LambdaGraphQL Maven / Gradle / Ivy

/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.fleetpin.graphql.aws.lambda;

import static com.google.common.net.HttpHeaders.AUTHORIZATION;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2ProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2ProxyResponseEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fleetpin.graphql.builder.SchemaBuilder;

import graphql.ExecutionResult;
import graphql.GraphQL;

public abstract class LambdaGraphQL implements RequestHandler{
	
	private static final Logger LOGGER = LoggerFactory.getLogger(LambdaGraphQL.class);

	private final GraphQL build;
	private ObjectMapper mapper;

	public LambdaGraphQL() throws Exception {
		this.build = buildGraphQL();
		this.mapper = builderObjectMapper();
	}
	
	

	@Override
	public APIGatewayV2ProxyResponseEvent handleRequest(APIGatewayV2ProxyRequestEvent input, com.amazonaws.services.lambda.runtime.Context context) {
		try {
			GraphQLQuery query = mapper.readValue(input.getBody(), GraphQLQuery.class);
			
			var toReturn = new APIGatewayV2ProxyResponseEvent();
			CompletableFuture result =	validate(input.getHeaders().get(AUTHORIZATION)).handle((user, userError) -> {
				if(userError != null) {
					LOGGER.error("Failed to validate user", userError);
					toReturn.setStatusCode(401);
					return CompletableFuture.completedFuture((ExecutionResult) null);
				}

				C graphContext = buildContext(user, query);
				var target = build.executeAsync(builder -> builder.query(query.getQuery()).operationName(query.getOperationName()).variables(query.getVariables()).context(graphContext));
				graphContext.start(target);
				return target;
			}).thenCompose(t -> t);
			toReturn.setStatusCode(200);
			var headers = new HashMap();
			headers.put("Access-Control-Allow-Origin", "*");
			headers.put("content-type", "application/json; charset=utf-8");
			toReturn.setHeaders(headers);
			var r = result.get();
			if(r != null) {
				//remove empty array to match apollo
				ObjectNode tree = mapper.valueToTree(r);
				if(tree.get("errors").isEmpty()) {
					tree.remove("errors");
				}
				toReturn.setBody(mapper.writeValueAsString(tree));
			}else {
				toReturn.setBody("");
			}
			return toReturn;
		}catch (Throwable e) {
			LOGGER.error("Failed to invoke graph", e);
			e.printStackTrace();
			throw new RuntimeException(e);
		}finally {
			LambdaCache.evict();
		}
	}

	protected ObjectMapper builderObjectMapper() {
		return SchemaBuilder.MAPPER;
	}
	
	protected abstract GraphQL buildGraphQL() throws Exception;

	protected abstract CompletableFuture validate(String authHeader);

	protected abstract C buildContext(U user, GraphQLQuery query);

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy