Skip to content

Commit

Permalink
Initial commit with dataloaders (not working)
Browse files Browse the repository at this point in the history
  • Loading branch information
siderakis committed Mar 21, 2018
1 parent a309f77 commit 1b15b82
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 137 deletions.
2 changes: 2 additions & 0 deletions examples/build.gradle
Expand Up @@ -28,13 +28,15 @@ repositories {
def grpcVersion = '1.7.0' // CURRENT_GRPC_VERSION def grpcVersion = '1.7.0' // CURRENT_GRPC_VERSION


dependencies { dependencies {
compile 'com.google.inject.extensions:guice-servlet:4.2.0'
compile "com.google.api.grpc:proto-google-common-protos:0.1.9" compile "com.google.api.grpc:proto-google-common-protos:0.1.9"
compile "io.grpc:grpc-netty:${grpcVersion}" compile "io.grpc:grpc-netty:${grpcVersion}"
compile "io.grpc:grpc-protobuf:${grpcVersion}" compile "io.grpc:grpc-protobuf:${grpcVersion}"
compile "io.grpc:grpc-stub:${grpcVersion}" compile "io.grpc:grpc-stub:${grpcVersion}"
compile "com.google.api.graphql:rejoiner:0.0.1-SNAPSHOT" compile "com.google.api.graphql:rejoiner:0.0.1-SNAPSHOT"
compile "com.google.api.graphql:execution:0.0.1-SNAPSHOT" compile "com.google.api.graphql:execution:0.0.1-SNAPSHOT"
compile group: 'org.eclipse.jetty', name: 'jetty-server', version: '9.3.8.v20160314' compile group: 'org.eclipse.jetty', name: 'jetty-server', version: '9.3.8.v20160314'
compile group: 'org.eclipse.jetty', name: 'jetty-servlet', version: '9.3.8.v20160314'
compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.6.2' compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.6.2'


} }
Expand Down
@@ -0,0 +1,56 @@
// Copyright 2017 Google LLC
//
// 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.google.api.graphql.examples.library.graphqlserver;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.example.library.book.v1.Book;
import com.google.example.library.book.v1.BookServiceGrpc;
import com.google.example.library.book.v1.ListBooksRequest;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.servlet.RequestScoped;
import net.javacrumbs.futureconverter.java8guava.FutureConverter;
import org.dataloader.BatchLoader;
import org.dataloader.DataLoader;
import org.dataloader.DataLoaderRegistry;

import java.util.List;

final class DataLoaderModule extends AbstractModule {

@Provides
@RequestScoped
DataLoaderRegistry dataLoaderRegistry(BookServiceGrpc.BookServiceFutureStub bookService) {

// TODO: Use multibinder to modularize this, or automate this somehow
BatchLoader<String, Book> bookBatchLoader =
keys -> {
ListenableFuture<List<Book>> listenableFuture =
Futures.transform(
bookService.listBooks(ListBooksRequest.newBuilder().build()),
resp -> resp.getBooksList(),
MoreExecutors.directExecutor());
// ServletScopes.transferRequest(() -> ... ); ??
return FutureConverter.toCompletableFuture(listenableFuture);
};

DataLoaderRegistry registry = new DataLoaderRegistry();

registry.register("books", new DataLoader<>(bookBatchLoader));
return registry;
}
}
Expand Up @@ -14,120 +14,61 @@


package com.google.api.graphql.examples.library.graphqlserver; package com.google.api.graphql.examples.library.graphqlserver;


import com.google.api.graphql.execution.GuavaListenableFutureSupport; import com.google.api.graphql.rejoiner.SchemaProviderModule;
import com.google.common.base.Strings; import com.google.inject.Guice;
import com.google.common.collect.ImmutableMap; import com.google.inject.Injector;
import com.google.common.io.CharStreams; import com.google.inject.servlet.GuiceFilter;
import com.google.gson.Gson; import com.google.inject.servlet.GuiceServletContextListener;
import com.google.gson.GsonBuilder; import com.google.inject.servlet.ServletModule;
import com.google.gson.reflect.TypeToken;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.execution.instrumentation.ChainedInstrumentation;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.server.handler.ResourceHandler;
import java.util.EnumSet;
import java.util.logging.Logger;


public class GraphQlServer extends AbstractHandler { public class GraphQlServer {


private static final int HTTP_PORT = 8080; private static final int HTTP_PORT = 8080;
private static final Logger logger = Logger.getLogger(GraphQlServer.class.getName()); private static final Logger logger = Logger.getLogger(GraphQlServer.class.getName());
private static final Gson GSON = new GsonBuilder().serializeNulls().create();
private static final TypeToken<Map<String, Object>> MAP_TYPE_TOKEN =
new TypeToken<Map<String, Object>>() {};

private static final Instrumentation instrumentation =
new ChainedInstrumentation(
Arrays.asList(
GuavaListenableFutureSupport.listenableFutureInstrumentation(),
new TracingInstrumentation()));

private static final GraphQL GRAPHQL =
GraphQL.newGraphQL(LibrarySchema.SCHEMA).instrumentation(instrumentation).build();


public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Server server = new Server(HTTP_PORT); Server server = new Server(HTTP_PORT);
ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setWelcomeFiles(new String[] {"index.html"});
resourceHandler.setDirectoriesListed(false);
resourceHandler.setResourceBase("./src/main/resources");
HandlerList handlerList = new HandlerList();
handlerList.setHandlers(new Handler[] {resourceHandler, new GraphQlServer()});
server.setHandler(handlerList);
server.start();
logger.info("Server running on port " + HTTP_PORT);
server.join();
}

@Override
public void handle(
String target,
Request request,
HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws IOException, ServletException {
if ("/graphql".equals(target)) {
request.setHandled(true);
Map<String, Object> json = readJson(httpServletRequest);
String query = (String) json.get("query");
if (query == null) {
httpServletResponse.setStatus(400);
return;
}
String operationName = (String) json.get("operationName");
Map<String, Object> variables = getVariables(json.get("variables"));


ExecutionInput executionInput = ServletContextHandler context =
ExecutionInput.newExecutionInput() new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS);
.query(query)
.operationName(operationName)
.variables(variables)
.context(new Object())
.build();
ExecutionResult executionResult = GRAPHQL.execute(executionInput);
httpServletResponse.setContentType("application/json");
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
GSON.toJson(executionResult.toSpecification(), httpServletResponse.getWriter());
}
}


private static Map<String, Object> getVariables(Object variables) { context.addEventListener(
Map<String, Object> variablesWithStringKey = new HashMap<>(); new GuiceServletContextListener() {
if (variables instanceof Map) { @Override
((Map) variables).forEach((k, v) -> variablesWithStringKey.put(String.valueOf(k), v)); protected Injector getInjector() {
} return Guice.createInjector(
return variablesWithStringKey; new ServletModule() {
} @Override
protected void configureServlets() {
serve("/graphql").with(GraphQlServlet.class);
}
},
new DataLoaderModule(),
new SchemaProviderModule(), // Part of Rejoiner framework (Provides `@Schema GraphQLSchema`)
new BookClientModule(), // Configures the Book gRPC client
new ShelfClientModule(), // Configures the Shelf gRPC client
new BookSchemaModule(), // Creates queries and mutations for the Book service
new ShelfSchemaModule(), // Creates queries and mutations for the Shelf service
new LibrarySchemaModule(), // Joins together Shelf and Book services
new SeedLibrarySchemaModule() // Fills the Shelf and Book services with data
);
}
});


private static Map<String, Object> readJson(HttpServletRequest request) { context.addFilter(
try { GuiceFilter.class,
String json = CharStreams.toString(request.getReader()); "/*",
return jsonToMap(json); EnumSet.of(javax.servlet.DispatcherType.REQUEST, javax.servlet.DispatcherType.ASYNC));
} catch (IOException e) {
throw new RuntimeException(e);
}
}


private static Map<String, Object> jsonToMap(String json) { context.addServlet(DefaultServlet.class, "/");
if (Strings.isNullOrEmpty(json)) { server.start();
return ImmutableMap.of(); logger.info("Server running on port " + HTTP_PORT);
} server.join();
return Optional.<Map<String, Object>>ofNullable(GSON.fromJson(json, MAP_TYPE_TOKEN.getType()))
.orElse(ImmutableMap.of());
} }
} }
@@ -0,0 +1,110 @@
// Copyright 2017 Google LLC
//
// 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.google.api.graphql.examples.library.graphqlserver;

import com.google.api.graphql.execution.GuavaListenableFutureSupport;
import com.google.api.graphql.rejoiner.Schema;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharStreams;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.execution.instrumentation.ChainedInstrumentation;
import graphql.execution.instrumentation.Instrumentation;
import graphql.execution.instrumentation.tracing.TracingInstrumentation;
import graphql.schema.GraphQLSchema;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Singleton
class GraphQlServlet extends HttpServlet {

private static final Gson GSON = new GsonBuilder().serializeNulls().create();
private static final TypeToken<Map<String, Object>> MAP_TYPE_TOKEN =
new TypeToken<Map<String, Object>>() {};

private static final Instrumentation instrumentation =
new ChainedInstrumentation(
Arrays.asList(
GuavaListenableFutureSupport.listenableFutureInstrumentation(),
new TracingInstrumentation()));

@Inject @Schema GraphQLSchema schema;

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

GraphQL graphql = GraphQL.newGraphQL(schema).instrumentation(instrumentation).build();

Map<String, Object> json = readJson(req);
String query = (String) json.get("query");
if (query == null) {
resp.setStatus(400);
return;
}
String operationName = (String) json.get("operationName");
Map<String, Object> variables = getVariables(json.get("variables"));

ExecutionInput executionInput =
ExecutionInput.newExecutionInput()
.query(query)
.operationName(operationName)
.variables(variables)
.context(new Object())
.build();
ExecutionResult executionResult = graphql.execute(executionInput);
resp.setContentType("application/json");
resp.setStatus(HttpServletResponse.SC_OK);
GSON.toJson(executionResult.toSpecification(), resp.getWriter());
}

private static Map<String, Object> getVariables(Object variables) {
Map<String, Object> variablesWithStringKey = new HashMap<>();
if (variables instanceof Map) {
((Map) variables).forEach((k, v) -> variablesWithStringKey.put(String.valueOf(k), v));
}
return variablesWithStringKey;
}

private static Map<String, Object> readJson(HttpServletRequest request) {
try {
String json = CharStreams.toString(request.getReader());
return jsonToMap(json);
} catch (IOException e) {
throw new RuntimeException(e);
}
}

private static Map<String, Object> jsonToMap(String json) {
if (Strings.isNullOrEmpty(json)) {
return ImmutableMap.of();
}
return Optional.<Map<String, Object>>ofNullable(GSON.fromJson(json, MAP_TYPE_TOKEN.getType()))
.orElse(ImmutableMap.of());
}
}

This file was deleted.

0 comments on commit 1b15b82

Please sign in to comment.