Skip to content

Commit 5c87a60

Browse files
authored
Extract base functionality into abstract parent (#17)
* [Gradle Release Plugin] - pre tag commit: 'v0.10.0'. * [Gradle Release Plugin] - new version commit: 'v0.10.1'. * Extract common functionality to abstract parent and add simple servlet. * Add license.
1 parent 523d11b commit 5c87a60

File tree

6 files changed

+315
-179
lines changed

6 files changed

+315
-179
lines changed

src/main/java/graphql/servlet/GraphQLOperationListener.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
import java.util.Map;
2121

2222
public interface GraphQLOperationListener {
23-
void onGraphQLOperation(GraphQLContext context, String operationName, String query, Map<String, Object> variables,
24-
Object data);
25-
void onFailedGraphQLOperation(GraphQLContext context, String operationName, String query,
26-
Map<String, Object> variables, List<GraphQLError> errors);
23+
void beforeGraphQLOperation(GraphQLContext context, String operationName, String query, Map<String, Object> variables);
24+
void onSuccessfulGraphQLOperation(GraphQLContext context, String operationName, String query, Map<String, Object> variables, Object data);
25+
void onFailedGraphQLOperation(GraphQLContext context, String operationName, String query, Map<String, Object> variables, List<GraphQLError> errors);
2726
}

src/main/java/graphql/servlet/GraphQLServlet.java

Lines changed: 61 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
14-
*/
15-
package graphql.servlet;
14+
*/ package graphql.servlet;
1615

1716
import com.fasterxml.jackson.core.JsonParser;
1817
import com.fasterxml.jackson.core.type.TypeReference;
@@ -22,11 +21,14 @@
2221
import com.fasterxml.jackson.databind.RuntimeJsonMappingException;
2322
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2423
import com.google.common.io.CharStreams;
25-
import graphql.*;
24+
import graphql.ExceptionWhileDataFetching;
25+
import graphql.ExecutionResult;
26+
import graphql.GraphQL;
27+
import graphql.GraphQLError;
28+
import graphql.InvalidSyntaxError;
29+
import graphql.execution.ExecutionStrategy;
2630
import graphql.schema.GraphQLFieldDefinition;
27-
import graphql.schema.GraphQLObjectType;
2831
import graphql.schema.GraphQLSchema;
29-
import graphql.schema.GraphQLType;
3032
import graphql.validation.ValidationError;
3133
import lombok.Getter;
3234
import lombok.Setter;
@@ -36,10 +38,6 @@
3638
import org.apache.commons.fileupload.FileItemStream;
3739
import org.apache.commons.fileupload.FileUploadException;
3840
import org.apache.commons.fileupload.servlet.ServletFileUpload;
39-
import org.osgi.service.component.annotations.Component;
40-
import org.osgi.service.component.annotations.Reference;
41-
import org.osgi.service.component.annotations.ReferenceCardinality;
42-
import org.osgi.service.component.annotations.ReferencePolicyOption;
4341

4442
import javax.security.auth.Subject;
4543
import javax.servlet.Servlet;
@@ -52,146 +50,48 @@
5250
import java.io.InputStreamReader;
5351
import java.security.AccessController;
5452
import java.security.PrivilegedAction;
55-
import java.util.*;
53+
import java.util.ArrayList;
54+
import java.util.HashMap;
55+
import java.util.List;
56+
import java.util.Map;
57+
import java.util.Optional;
5658
import java.util.stream.Collectors;
5759

58-
import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;
59-
import static graphql.schema.GraphQLObjectType.newObject;
60-
import static graphql.schema.GraphQLSchema.newSchema;
61-
60+
/**
61+
* @author Andrew Potter
62+
*/
6263
@Slf4j
63-
@Component(property = {"alias=/graphql", "jmx.objectname=graphql.servlet:type=graphql"})
64-
public class GraphQLServlet extends HttpServlet implements Servlet, GraphQLMBean, GraphQLSchemaProvider {
65-
66-
private List<GraphQLQueryProvider> queryProviders = new ArrayList<>();
67-
private List<GraphQLMutationProvider> mutationProviders = new ArrayList<>();
68-
private List<GraphQLTypesProvider> typesProviders = new ArrayList<>();
69-
70-
@Getter
71-
GraphQLSchema schema;
72-
@Getter
73-
GraphQLSchema readOnlySchema;
74-
75-
protected void updateSchema() {
76-
GraphQLObjectType.Builder object = newObject().name("query");
77-
78-
for (GraphQLQueryProvider provider : queryProviders) {
79-
GraphQLObjectType query = provider.getQuery();
80-
object.field(newFieldDefinition().
81-
type(query).
82-
staticValue(provider.context()).
83-
name(provider.getName()).
84-
description(query.getDescription()).
85-
build());
86-
}
87-
88-
Set<GraphQLType> types = new HashSet<>();
89-
for (GraphQLTypesProvider typesProvider : typesProviders) {
90-
types.addAll(typesProvider.getTypes());
91-
}
64+
public abstract class GraphQLServlet extends HttpServlet implements Servlet, GraphQLMBean, GraphQLSchemaProvider {
9265

93-
readOnlySchema = newSchema().query(object.build()).build(types);
66+
protected abstract GraphQLContext createContext(Optional<HttpServletRequest> request, Optional<HttpServletResponse> response);
67+
protected abstract ExecutionStrategy getExecutionStrategy();
68+
protected abstract Map<String, Object> transformVariables(GraphQLSchema schema, String query, Map<String, Object> variables);
9469

95-
if (mutationProviders.isEmpty()) {
96-
schema = readOnlySchema;
97-
} else {
98-
GraphQLObjectType.Builder mutationObject = newObject().name("mutation");
99-
100-
for (GraphQLMutationProvider provider : mutationProviders) {
101-
provider.getMutations().forEach(mutationObject::field);
102-
}
103-
104-
GraphQLObjectType mutationType = mutationObject.build();
105-
if (mutationType.getFieldDefinitions().size() == 0) {
106-
schema = readOnlySchema;
107-
} else {
108-
schema = newSchema().query(object.build()).mutation(mutationType).build();
109-
}
110-
}
111-
}
112-
113-
public GraphQLServlet() {
114-
updateSchema();
115-
}
11670

117-
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY)
118-
public void bindQueryProvider(GraphQLQueryProvider queryProvider) {
119-
queryProviders.add(queryProvider);
120-
updateSchema();
121-
}
122-
public void unbindQueryProvider(GraphQLQueryProvider queryProvider) {
123-
queryProviders.remove(queryProvider);
124-
updateSchema();
125-
}
71+
private List<GraphQLOperationListener> operationListeners = new ArrayList<>();
12672

127-
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY)
128-
public void bindMutationProvider(GraphQLMutationProvider mutationProvider) {
129-
mutationProviders.add(mutationProvider);
130-
updateSchema();
131-
}
132-
public void unbindMutationProvider(GraphQLMutationProvider mutationProvider) {
133-
mutationProviders.remove(mutationProvider);
134-
updateSchema();
73+
public void addOperationListener(GraphQLOperationListener operationListener) {
74+
operationListeners.add(operationListener);
13575
}
13676

137-
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY)
138-
public void typesProviders(GraphQLTypesProvider typesProvider) {
139-
typesProviders.add(typesProvider);
140-
updateSchema();
141-
}
142-
public void unbindTypesProvider(GraphQLTypesProvider typesProvider) {
143-
typesProviders.remove(typesProvider);
144-
updateSchema();
77+
public void removeOperationListener(GraphQLOperationListener operationListener) {
78+
operationListeners.remove(operationListener);
14579
}
14680

14781
@Override
14882
public String[] getQueries() {
149-
return schema.getQueryType().getFieldDefinitions().stream().map(GraphQLFieldDefinition::getName).toArray(String[]::new);
83+
return getSchema().getQueryType().getFieldDefinitions().stream().map(GraphQLFieldDefinition::getName).toArray(String[]::new);
15084
}
15185

15286
@Override
15387
public String[] getMutations() {
154-
return schema.getMutationType().getFieldDefinitions().stream().map(GraphQLFieldDefinition::getName).toArray(String[]::new);
155-
}
156-
157-
private GraphQLContextBuilder contextBuilder = new DefaultGraphQLContextBuilder();
158-
private ExecutionStrategyProvider executionStrategyProvider = new EnhancedExecutionStrategyProvider();
159-
160-
@Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
161-
public void setContextProvider(GraphQLContextBuilder contextBuilder) {
162-
this.contextBuilder = contextBuilder;
163-
}
164-
public void unsetContextProvider(GraphQLContextBuilder contextBuilder) {
165-
this.contextBuilder = new DefaultGraphQLContextBuilder();
166-
}
167-
168-
@Reference(cardinality = ReferenceCardinality.OPTIONAL)
169-
public void setExecutionStrategyProvider(ExecutionStrategyProvider provider) {
170-
executionStrategyProvider = provider;
171-
}
172-
public void unsetExecutionStrategyProvider(ExecutionStrategyProvider provider) {
173-
executionStrategyProvider = new EnhancedExecutionStrategyProvider();
174-
}
175-
176-
protected GraphQLContext createContext(Optional<HttpServletRequest> req, Optional<HttpServletResponse> resp) {
177-
return contextBuilder.build(req, resp);
178-
}
179-
180-
private List<GraphQLOperationListener> operationListeners = new ArrayList<>();
181-
182-
@Reference(cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY)
183-
public void bindOperationListener(GraphQLOperationListener listener) {
184-
operationListeners.add(listener);
185-
}
186-
187-
public void unbindOperationListener(GraphQLOperationListener listener) {
188-
operationListeners.remove(listener);
88+
return getSchema().getMutationType().getFieldDefinitions().stream().map(GraphQLFieldDefinition::getName).toArray(String[]::new);
18989
}
19090

19191
@Override @SneakyThrows
19292
public String executeQuery(String query) {
19393
try {
194-
ExecutionResult result = new GraphQL(schema).execute(query, createContext(Optional.empty(), Optional.empty()), new HashMap<>());
94+
ExecutionResult result = new GraphQL(getSchema()).execute(query, createContext(Optional.empty(), Optional.empty()), new HashMap<>());
19595
if (result.getErrors().isEmpty()) {
19696
return new ObjectMapper().writeValueAsString(result.getData());
19797
} else {
@@ -202,31 +102,6 @@ public String executeQuery(String query) {
202102
}
203103
}
204104

205-
private static class VariablesDeserializer extends JsonDeserializer<Map<String, Object>> {
206-
207-
@Override public Map<String, Object> deserialize(JsonParser p, DeserializationContext ctxt)
208-
throws IOException {
209-
Object o = p.readValueAs(Object.class);
210-
if (o instanceof Map) {
211-
return (Map<String, Object>) o;
212-
} else if (o instanceof String) {
213-
return new ObjectMapper().readValue((String) o, new TypeReference<Map<String, Object>>() {});
214-
} else {
215-
throw new RuntimeJsonMappingException("variables should be either an object or a string");
216-
}
217-
}
218-
}
219-
220-
public static class Request {
221-
@Getter @Setter
222-
private String query;
223-
@Getter @Setter @JsonDeserialize(using = VariablesDeserializer.class)
224-
private Map<String, Object> variables = new HashMap<>();
225-
@Getter @Setter
226-
private String operationName;
227-
228-
}
229-
230105
@Override
231106
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
232107
GraphQLContext context = createContext(Optional.of(req), Optional.of(resp));
@@ -235,10 +110,10 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
235110
path = req.getServletPath();
236111
}
237112
if (path.contentEquals("/schema.json")) {
238-
query(CharStreams.toString(new InputStreamReader(GraphQLServlet.class.getResourceAsStream("introspectionQuery"))), null, new HashMap<>(), schema, req, resp, context);
113+
query(CharStreams.toString(new InputStreamReader(GraphQLServlet.class.getResourceAsStream("introspectionQuery"))), null, new HashMap<>(), getSchema(), req, resp, context);
239114
} else {
240115
if (req.getParameter("q") != null) {
241-
query(req.getParameter("q"), null, new HashMap<>(), readOnlySchema, req, resp, context);
116+
query(req.getParameter("q"), null, new HashMap<>(), getReadOnlySchema(), req, resp, context);
242117
} else if (req.getParameter("query") != null) {
243118
Map<String,Object> variables = new HashMap<>();
244119
if (req.getParameter("variables") != null) {
@@ -248,7 +123,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
248123
if (req.getParameter("operationName") != null) {
249124
operationName = req.getParameter("operationName");
250125
}
251-
query(req.getParameter("query"), operationName, variables, readOnlySchema, req, resp, context);
126+
query(req.getParameter("query"), operationName, variables, getReadOnlySchema(), req, resp, context);
252127
}
253128
}
254129
}
@@ -283,7 +158,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S
283158
if (variables == null) {
284159
variables = new HashMap<>();
285160
}
286-
query(request.query, request.operationName, variables, schema, req, resp, context);
161+
query(request.query, request.operationName, variables, getSchema(), req, resp, context);
287162
}
288163

289164
private void query(String query, String operationName, Map<String, Object> variables, GraphQLSchema schema, HttpServletRequest req, HttpServletResponse resp, GraphQLContext context) throws IOException {
@@ -296,14 +171,16 @@ public Void run() {
296171
}
297172
});
298173
} else {
299-
GraphQLVariables vars = new GraphQLVariables(schema, query, variables);
300-
ExecutionResult result = new GraphQL(schema, executionStrategyProvider.getExecutionStrategy()).execute(query, operationName, context, vars);
174+
Map<String, Object> vars = transformVariables(schema, query, variables);
175+
operationListeners.forEach(l -> l.beforeGraphQLOperation(context, operationName, query, vars));
176+
177+
ExecutionResult result = new GraphQL(schema, getExecutionStrategy()).execute(query, operationName, context, vars);
301178
resp.setContentType("application/json;charset=utf-8");
302179
if (result.getErrors().isEmpty()) {
303180
Map<String, Object> dict = new HashMap<>();
304181
dict.put("data", result.getData());
305182
resp.getWriter().write(new ObjectMapper().writeValueAsString(dict));
306-
operationListeners.forEach(l -> l.onGraphQLOperation(context, operationName, query, vars, result.getData()));
183+
operationListeners.forEach(l -> l.onSuccessfulGraphQLOperation(context, operationName, query, vars, result.getData()));
307184
} else {
308185
result.getErrors().stream().
309186
filter(error -> (error instanceof ExceptionWhileDataFetching)).
@@ -315,8 +192,7 @@ public Void run() {
315192
dict.put("errors", errors);
316193

317194
resp.getWriter().write(new ObjectMapper().writeValueAsString(dict));
318-
operationListeners.forEach(l -> l.onFailedGraphQLOperation(context, operationName, query, vars,
319-
result.getErrors()));
195+
operationListeners.forEach(l -> l.onFailedGraphQLOperation(context, operationName, query, vars, result.getErrors()));
320196
}
321197
}
322198
}
@@ -326,4 +202,28 @@ private List<GraphQLError> getGraphQLErrors(ExecutionResult result) {
326202
filter(error -> error instanceof InvalidSyntaxError || error instanceof ValidationError).
327203
collect(Collectors.toList());
328204
}
205+
206+
protected static class VariablesDeserializer extends JsonDeserializer<Map<String, Object>> {
207+
@Override
208+
public Map<String, Object> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
209+
Object o = p.readValueAs(Object.class);
210+
if (o instanceof Map) {
211+
return (Map<String, Object>) o;
212+
} else if (o instanceof String) {
213+
return new ObjectMapper().readValue((String) o, new TypeReference<Map<String, Object>>() {});
214+
} else {
215+
throw new RuntimeJsonMappingException("variables should be either an object or a string");
216+
}
217+
}
218+
}
219+
220+
public static class Request {
221+
@Getter
222+
@Setter
223+
private String query;
224+
@Getter @Setter @JsonDeserialize(using = GraphQLServlet.VariablesDeserializer.class)
225+
private Map<String, Object> variables = new HashMap<>();
226+
@Getter @Setter
227+
private String operationName;
228+
}
329229
}

0 commit comments

Comments
 (0)