1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package eu.ehri.project.graphql;
21
22 import com.fasterxml.jackson.core.JsonGenerator;
23 import graphql.ExecutionResult;
24 import graphql.ExecutionResultImpl;
25 import graphql.execution.AsyncExecutionStrategy;
26 import graphql.execution.DataFetcherExceptionHandlerParameters;
27 import graphql.execution.ExecutionContext;
28 import graphql.execution.ExecutionStrategy;
29 import graphql.execution.ExecutionStrategyParameters;
30 import graphql.execution.ExecutionTypeInfo;
31 import graphql.execution.FieldCollectorParameters;
32 import graphql.execution.TypeResolutionParameters;
33 import graphql.execution.instrumentation.Instrumentation;
34 import graphql.execution.instrumentation.InstrumentationContext;
35 import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
36 import graphql.execution.instrumentation.parameters.InstrumentationFieldParameters;
37 import graphql.language.Field;
38 import graphql.schema.DataFetchingEnvironment;
39 import graphql.schema.DataFetchingFieldSelectionSet;
40 import graphql.schema.DataFetchingFieldSelectionSetImpl;
41 import graphql.schema.GraphQLEnumType;
42 import graphql.schema.GraphQLFieldDefinition;
43 import graphql.schema.GraphQLInterfaceType;
44 import graphql.schema.GraphQLList;
45 import graphql.schema.GraphQLObjectType;
46 import graphql.schema.GraphQLOutputType;
47 import graphql.schema.GraphQLScalarType;
48 import graphql.schema.GraphQLType;
49 import graphql.schema.GraphQLUnionType;
50
51 import java.io.IOException;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.concurrent.CompletableFuture;
57
58 import static graphql.execution.ExecutionTypeInfo.newTypeInfo;
59 import static graphql.execution.FieldCollectorParameters.newParameters;
60 import static graphql.schema.DataFetchingEnvironmentBuilder.newDataFetchingEnvironment;
61
62
63
64
65
66
67
68
69 public class StreamingExecutionStrategy extends ExecutionStrategy {
70
71 public void execute(JsonGenerator generator, ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws IOException {
72 generator.writeStartObject();
73 for (String fieldName : parameters.fields().keySet()) {
74 generator.writeFieldName(fieldName);
75 resolveField(generator, executionContext, parameters, parameters.fields().get(fieldName));
76 }
77 generator.writeEndObject();
78 }
79
80 private void handleFetchingException(ExecutionContext executionContext,
81 ExecutionStrategyParameters parameters,
82 Field field,
83 GraphQLFieldDefinition fieldDef,
84 Map<String, Object> argumentValues,
85 DataFetchingEnvironment environment,
86 Throwable e) {
87 DataFetcherExceptionHandlerParameters handlerParameters = DataFetcherExceptionHandlerParameters.newExceptionParameters()
88 .executionContext(executionContext)
89 .dataFetchingEnvironment(environment)
90 .argumentValues(argumentValues)
91 .field(field)
92 .fieldDefinition(fieldDef)
93 .path(parameters.path())
94 .exception(e)
95 .build();
96
97 dataFetcherExceptionHandler.accept(handlerParameters);
98 }
99
100 private void resolveField(JsonGenerator generator, ExecutionContext executionContext, ExecutionStrategyParameters parameters, List<Field> fields) throws IOException {
101 Field field = fields.get(0);
102 GraphQLObjectType parentType = parameters.typeInfo().castType(GraphQLObjectType.class);
103 GraphQLFieldDefinition fieldDef = getFieldDef(executionContext.getGraphQLSchema(), parentType, field);
104
105 Map<String, Object> argumentValues = valuesResolver.getArgumentValues(fieldDef.getArguments(), field.getArguments(), executionContext.getVariables());
106
107 GraphQLOutputType fieldType = fieldDef.getType();
108 DataFetchingFieldSelectionSet fieldCollector = DataFetchingFieldSelectionSetImpl.newCollector(executionContext, fieldType, fields);
109
110 DataFetchingEnvironment environment = newDataFetchingEnvironment(executionContext)
111 .source(parameters.source())
112 .arguments(argumentValues)
113 .fieldDefinition(fieldDef)
114 .fields(fields)
115 .fieldType(fieldType)
116 .parentType(parentType)
117 .selectionSet(fieldCollector)
118 .build();
119
120 ExecutionTypeInfo fieldTypeInfo = newTypeInfo()
121 .type(fieldType)
122 .parentInfo(parameters.typeInfo())
123 .build();
124
125 Instrumentation instrumentation = executionContext.getInstrumentation();
126
127 InstrumentationContext<ExecutionResult> fieldCtx = instrumentation.beginField(new InstrumentationFieldParameters(executionContext, fieldDef, fieldTypeInfo));
128
129 InstrumentationContext<Object> fetchCtx = instrumentation.beginFieldFetch(new InstrumentationFieldFetchParameters(executionContext, fieldDef, environment));
130 Object resolvedValue = null;
131 try {
132 resolvedValue = fieldDef.getDataFetcher().get(environment);
133 fetchCtx.onEnd(resolvedValue, null);
134 } catch (Exception e) {
135 handleFetchingException(executionContext, parameters, field, fieldDef, argumentValues, environment, e);
136 fetchCtx.onEnd(null, e);
137 }
138
139 ExecutionStrategyParameters newParameters = ExecutionStrategyParameters.newParameters()
140 .typeInfo(fieldTypeInfo)
141 .fields(parameters.fields())
142 .arguments(argumentValues)
143 .source(resolvedValue).build();
144
145 completeValue(generator, executionContext, newParameters, fields);
146
147 fieldCtx.onEnd(new ExecutionResultImpl(resolvedValue, Collections.emptyList()), null);
148 }
149
150 private void completeValue(JsonGenerator generator, ExecutionContext executionContext, ExecutionStrategyParameters parameters, List<Field> fields) throws IOException {
151
152 ExecutionTypeInfo typeInfo = parameters.typeInfo();
153 Object result = parameters.source();
154 GraphQLType fieldType = parameters.typeInfo().getType();
155
156 if (result == null) {
157 generator.writeNull();
158 } else if (fieldType instanceof GraphQLList) {
159 completeValueForList(generator, executionContext, parameters, fields, result);
160 } else if (fieldType instanceof GraphQLScalarType) {
161 completeValueForScalar(generator, (GraphQLScalarType) fieldType, result);
162 } else if (fieldType instanceof GraphQLEnumType) {
163 completeValueForEnum(generator, (GraphQLEnumType) fieldType, result);
164 } else {
165 GraphQLObjectType resolvedType;
166 if (fieldType instanceof GraphQLInterfaceType) {
167 TypeResolutionParameters resolutionParams = TypeResolutionParameters.newParameters()
168 .graphQLInterfaceType((GraphQLInterfaceType) fieldType)
169 .field(fields.get(0))
170 .value(parameters.source())
171 .argumentValues(parameters.arguments())
172 .schema(executionContext.getGraphQLSchema()).build();
173 resolvedType = resolveTypeForInterface(resolutionParams);
174
175 } else if (fieldType instanceof GraphQLUnionType) {
176 TypeResolutionParameters resolutionParams = TypeResolutionParameters.newParameters()
177 .graphQLUnionType((GraphQLUnionType) fieldType)
178 .field(fields.get(0))
179 .value(parameters.source())
180 .argumentValues(parameters.arguments())
181 .schema(executionContext.getGraphQLSchema()).build();
182 resolvedType = resolveTypeForUnion(resolutionParams);
183 } else {
184 resolvedType = (GraphQLObjectType) fieldType;
185 }
186
187 FieldCollectorParameters collectorParameters = newParameters()
188 .schema(executionContext.getGraphQLSchema())
189 .objectType(resolvedType)
190 .fragments(executionContext.getFragmentsByName())
191 .variables(executionContext.getVariables())
192 .build();
193
194 Map<String, List<Field>> subFields = fieldCollector.collectFields(collectorParameters, fields);
195
196 ExecutionStrategyParameters newParameters = ExecutionStrategyParameters.newParameters()
197 .typeInfo(typeInfo.treatAs(resolvedType))
198 .fields(subFields)
199 .source(result).build();
200
201
202 execute(generator, executionContext, newParameters);
203 }
204 }
205
206 private void completeValueForEnum(JsonGenerator generator, GraphQLEnumType enumType, Object result) throws IOException {
207 generator.writeObject(enumType.getCoercing().serialize(result));
208 }
209
210 private void completeValueForScalar(JsonGenerator generator, GraphQLScalarType scalarType, Object result) throws IOException {
211 Object serialized = scalarType.getCoercing().serialize(result);
212
213 if (serialized instanceof Double && ((Double) serialized).isNaN()) {
214 serialized = null;
215 }
216 generator.writeObject(serialized);
217 }
218
219 private void completeValueForList(JsonGenerator generator, ExecutionContext executionContext, ExecutionStrategyParameters parameters, List<Field> fields, Object result) throws IOException {
220 if (result.getClass().isArray()) {
221 result = Arrays.asList((Object[]) result);
222 }
223
224 completeValueForList(generator, executionContext, parameters, fields, (Iterable<Object>) result);
225 }
226
227 private void completeValueForList(JsonGenerator generator, ExecutionContext executionContext, ExecutionStrategyParameters parameters, List<Field> fields, Iterable<Object> result) throws IOException {
228 ExecutionTypeInfo typeInfo = parameters.typeInfo();
229 GraphQLList fieldType = typeInfo.castType(GraphQLList.class);
230
231 generator.writeStartArray();
232 for (Object item : result) {
233 ExecutionStrategyParameters newParameters = ExecutionStrategyParameters.newParameters()
234 .typeInfo(typeInfo.treatAs(fieldType.getWrappedType()))
235 .fields(parameters.fields())
236 .source(item).build();
237
238 completeValue(generator, executionContext, newParameters, fields);
239 }
240 generator.writeEndArray();
241 }
242
243
244 @Override
245 public CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) {
246 return new AsyncExecutionStrategy().execute(executionContext, parameters);
247 }
248 }