1 package eu.ehri.extension;
2
3 import com.fasterxml.jackson.core.JsonGenerator;
4 import eu.ehri.extension.base.AbstractAccessibleResource;
5 import eu.ehri.extension.errors.ExecutionError;
6 import eu.ehri.project.core.Tx;
7 import eu.ehri.project.graphql.GraphQLImpl;
8 import eu.ehri.project.graphql.GraphQLQuery;
9 import eu.ehri.project.graphql.StreamingGraphQL;
10 import eu.ehri.project.models.base.Accessible;
11 import eu.ehri.project.persistence.Bundle;
12 import graphql.ExecutionInput;
13 import graphql.ExecutionResult;
14 import graphql.GraphQL;
15 import graphql.introspection.IntrospectionQuery;
16 import graphql.language.Document;
17 import graphql.schema.GraphQLSchema;
18 import org.neo4j.graphdb.GraphDatabaseService;
19
20 import javax.ws.rs.Consumes;
21 import javax.ws.rs.GET;
22 import javax.ws.rs.POST;
23 import javax.ws.rs.Path;
24 import javax.ws.rs.Produces;
25 import javax.ws.rs.core.Context;
26 import javax.ws.rs.core.MediaType;
27 import javax.ws.rs.core.Response;
28 import javax.ws.rs.core.StreamingOutput;
29
30
31
32
33 @Path(GraphQLResource.ENDPOINT)
34 public class GraphQLResource extends AbstractAccessibleResource<Accessible> {
35
36 public static final String ENDPOINT = "graphql";
37
38 public GraphQLResource(@Context GraphDatabaseService database) {
39 super(database, Accessible.class);
40 }
41
42
43
44
45
46
47
48
49 @GET
50 @Produces(MediaType.APPLICATION_JSON)
51 public ExecutionResult describe() throws Exception {
52 try (final Tx tx = beginTx()) {
53 GraphQLSchema schema = new GraphQLImpl(api()).getSchema();
54 tx.success();
55 return GraphQL.newGraphQL(schema).build()
56 .execute(IntrospectionQuery.INTROSPECTION_QUERY);
57 }
58 }
59
60
61
62
63
64
65
66
67 @POST
68 @Consumes(MediaType.APPLICATION_JSON)
69 @Produces(MediaType.APPLICATION_JSON)
70 public Response query(GraphQLQuery q) throws Exception {
71 try (final Tx tx = beginTx()) {
72 boolean stream = isStreaming();
73 GraphQLSchema schema = new GraphQLImpl(api(), stream).getSchema();
74 Object data = stream ? lazyExecution(schema, q) : strictExecution(schema, q);
75 tx.success();
76 return Response.ok(data).build();
77 }
78 }
79
80
81
82
83
84
85
86 @POST
87 @Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_OCTET_STREAM})
88 @Produces(MediaType.APPLICATION_JSON)
89 public Response query(String q) throws Exception {
90 return query(new GraphQLQuery(q));
91 }
92
93 private ExecutionResult strictExecution(GraphQLSchema schema, GraphQLQuery q) {
94 ExecutionResult executionResult = GraphQL.newGraphQL(schema).build()
95 .execute(ExecutionInput.newExecutionInput()
96 .query(q.getQuery())
97 .operationName(q.getOperationName())
98 .variables(q.getVariables()).build());
99 if (!executionResult.getErrors().isEmpty()) {
100 throw new ExecutionError(executionResult.getErrors());
101 }
102 return executionResult;
103 }
104
105 private StreamingOutput lazyExecution(GraphQLSchema schema, GraphQLQuery q) {
106
107
108 final StreamingGraphQL ql = new StreamingGraphQL(schema);
109 final Document document = ql.parseAndValidate(q.getQuery(), q.getOperationName(), q.getVariables());
110 return outputStream -> {
111 try (final Tx tx = beginTx();
112 final JsonGenerator generator = jsonFactory
113 .createGenerator(outputStream)
114 .useDefaultPrettyPrinter()) {
115 final StreamingGraphQL ql2 = new StreamingGraphQL(
116 new GraphQLImpl(api(), true).getSchema());
117 generator.writeStartObject();
118 generator.writeFieldName(Bundle.DATA_KEY);
119 ql2.execute(generator, q.getQuery(), document, q.getOperationName(),
120 null, q.getVariables());
121 generator.writeEndObject();
122 tx.success();
123 }
124 };
125 }
126 }