@@ -29,7 +29,9 @@ defmodule GraphQL.Execution.Executor do
29
29
def execute ( schema , document , root_value \\ % { } , variable_values \\ % { } , operation_name \\ nil ) do
30
30
context = ExecutionContext . new ( schema , document , root_value , variable_values , operation_name )
31
31
case context . errors do
32
- [ ] -> execute_operation ( context , context . operation , root_value )
32
+ [ ] ->
33
+ { _ , result } = execute_operation ( context , context . operation , root_value )
34
+ { :ok , result }
33
35
_ -> { :error , % { errors: Enum . dedup ( context . errors ) } }
34
36
end
35
37
end
@@ -42,10 +44,14 @@ defmodule GraphQL.Execution.Executor do
42
44
@ spec execute_operation ( ExecutionContext . t , operation , map ) :: result_data | { :error , String . t }
43
45
defp execute_operation ( context , operation , root_value ) do
44
46
type = operation_root_type ( context . schema , operation )
45
- % { fields: fields } = collect_fields ( context , type , operation . selectionSet )
47
+ { context , % { fields: fields } } = collect_fields ( context , type , operation . selectionSet )
46
48
case operation . operation do
47
- :query -> { :ok , execute_fields ( context , type , root_value , fields ) }
48
- :mutation -> { :ok , execute_fields_serially ( context , type , root_value , fields ) }
49
+ :query ->
50
+ { _ , result } = execute_fields ( context , type , root_value , fields )
51
+ { :ok , result }
52
+ :mutation ->
53
+ { _ , result } = execute_fields_serially ( context , type , root_value , fields )
54
+ { :ok , result }
49
55
:subscription -> { :error , "Subscriptions not currently supported" }
50
56
_ -> { :error , "Can only execute queries, mutations and subscriptions" }
51
57
end
@@ -57,12 +63,12 @@ defmodule GraphQL.Execution.Executor do
57
63
end
58
64
59
65
defp collect_fields ( context , runtime_type , selection_set , field_fragment_map \\ % { fields: % { } , fragments: % { } } ) do
60
- Enum . reduce selection_set [ :selections ] , field_fragment_map , fn ( selection , field_fragment_map ) ->
66
+ Enum . reduce selection_set [ :selections ] , { context , field_fragment_map } , fn ( selection , { context , field_fragment_map } ) ->
61
67
case selection do
62
68
% { kind: :Field } ->
63
69
field_name = field_entry_key ( selection )
64
70
fields = field_fragment_map . fields [ field_name ] || [ ]
65
- put_in ( field_fragment_map . fields [ field_name ] , [ selection | fields ] )
71
+ { context , put_in ( field_fragment_map . fields [ field_name ] , [ selection | fields ] ) }
66
72
% { kind: :InlineFragment } ->
67
73
collect_fragment ( context , runtime_type , selection , field_fragment_map )
68
74
% { kind: :FragmentSpread } ->
@@ -71,36 +77,37 @@ defmodule GraphQL.Execution.Executor do
71
77
field_fragment_map = put_in ( field_fragment_map . fragments [ fragment_name ] , true )
72
78
collect_fragment ( context , runtime_type , context . fragments [ fragment_name ] , field_fragment_map )
73
79
else
74
- field_fragment_map
80
+ { context , field_fragment_map }
75
81
end
76
- _ -> field_fragment_map
82
+ _ -> { context , field_fragment_map }
77
83
end
78
84
end
79
85
end
80
86
81
- @ spec execute_fields ( ExecutionContext . t , atom | Map , any , any ) :: any
87
+ @ spec execute_fields ( ExecutionContext . t , atom | Map , any , any ) :: { ExecutionContext . t , map }
82
88
defp execute_fields ( context , parent_type , source_value , fields ) when is_atom ( parent_type ) do
83
89
execute_fields ( context , apply ( parent_type , :type , [ ] ) , source_value , fields )
84
90
end
85
91
86
- @ spec execute_fields ( ExecutionContext . t , atom | Map , any , any ) :: any
92
+ @ spec execute_fields ( ExecutionContext . t , atom | Map , any , any ) :: { ExecutionContext . t , map }
87
93
defp execute_fields ( context , parent_type , source_value , fields ) do
88
- Enum . reduce fields , % { } , fn ( { field_name_ast , field_asts } , results ) ->
94
+ Enum . reduce fields , { context , % { } } , fn ( { field_name_ast , field_asts } , { context , results } ) ->
89
95
case resolve_field ( context , parent_type , source_value , field_asts ) do
90
- :undefined -> results
91
- value -> Map . put ( results , field_name_ast . value , value )
96
+ { context , :undefined } -> { context , results }
97
+ { context , value } -> { context , Map . put ( results , field_name_ast . value , value ) }
92
98
end
93
99
end
94
100
end
95
101
96
- @ spec execute_fields_serially ( ExecutionContext . t , atom , map , any ) :: any
102
+ @ spec execute_fields_serially ( ExecutionContext . t , atom , map , any ) :: { ExecutionContext . t , map }
97
103
defp execute_fields_serially ( context , parent_type , source_value , fields ) do
98
104
# call execute_fields because no async operations yet
99
105
execute_fields ( context , parent_type , source_value , fields )
100
106
end
101
107
102
108
defp resolve_field ( context , parent_type , source , field_asts ) do
103
109
field_ast = hd ( field_asts )
110
+ # FIXME: possible memory leak
104
111
field_name = String . to_atom ( field_ast . name . value )
105
112
106
113
if field_def = field_definition ( parent_type , field_name ) do
@@ -137,44 +144,46 @@ defmodule GraphQL.Execution.Executor do
137
144
end
138
145
complete_value_catching_error ( context , return_type , field_asts , info , result )
139
146
else
140
- :undefined
147
+ { context , :undefined }
141
148
end
142
149
end
143
150
151
+ @ spec complete_value_catching_error ( ExecutionContext . t , any , GraphQL.Document . t , any , map ) :: { ExecutionContext . t , map | nil }
144
152
defp complete_value_catching_error ( context , return_type , field_asts , info , result ) do
145
153
# TODO lots of error checking
146
154
complete_value ( context , return_type , field_asts , info , result )
147
155
end
148
156
149
- defp complete_value ( _ , _ , _ , _ , nil ) , do: nil
157
+ @ spec complete_value ( ExecutionContext . t , any , any , any , nil ) :: { ExecutionContext . t , nil }
158
+ defp complete_value ( context , _ , _ , _ , nil ) , do: { context , nil }
150
159
151
- @ spec complete_value ( ExecutionContext . t , % ObjectType { } , GraphQL.Document . t , any , map ) :: map
160
+ @ spec complete_value ( ExecutionContext . t , % ObjectType { } , GraphQL.Document . t , any , map ) :: { ExecutionContext . t , map }
152
161
defp complete_value ( context , % ObjectType { } = return_type , field_asts , _info , result ) do
153
- sub_field_asts = collect_sub_fields ( context , return_type , field_asts )
162
+ { context , sub_field_asts } = collect_sub_fields ( context , return_type , field_asts )
154
163
execute_fields ( context , return_type , result , sub_field_asts . fields )
155
164
end
156
165
157
166
defp complete_value ( context , % NonNull { ofType: inner_type } , field_asts , info , result ) when is_atom ( inner_type ) do
158
167
complete_value ( context , % NonNull { ofType: apply ( inner_type , :type , [ ] ) } , field_asts , info , result )
159
168
end
160
169
161
- @ spec complete_value ( ExecutionContext . t , % NonNull { } , GraphQL.Document . t , any , any ) :: map
170
+ @ spec complete_value ( ExecutionContext . t , % NonNull { } , GraphQL.Document . t , any , any ) :: { ExecutionContext . t , map }
162
171
defp complete_value ( context , % NonNull { ofType: inner_type } , field_asts , info , result ) do
163
172
# TODO: Null Checking
164
173
complete_value ( context , inner_type , field_asts , info , result )
165
174
end
166
175
167
- @ spec complete_value ( ExecutionContext . t , % Interface { } , GraphQL.Document . t , any , any ) :: map
176
+ @ spec complete_value ( ExecutionContext . t , % Interface { } , GraphQL.Document . t , any , any ) :: { ExecutionContext . t , map }
168
177
defp complete_value ( context , % Interface { } = return_type , field_asts , info , result ) do
169
178
runtime_type = AbstractType . get_object_type ( return_type , result , info . schema )
170
- sub_field_asts = collect_sub_fields ( context , runtime_type , field_asts )
179
+ { context , sub_field_asts } = collect_sub_fields ( context , runtime_type , field_asts )
171
180
execute_fields ( context , runtime_type , result , sub_field_asts . fields )
172
181
end
173
182
174
- @ spec complete_value ( ExecutionContext . t , % Union { } , GraphQL.Document . t , any , any ) :: map
183
+ @ spec complete_value ( ExecutionContext . t , % Union { } , GraphQL.Document . t , any , any ) :: { ExecutionContext . t , map }
175
184
defp complete_value ( context , % Union { } = return_type , field_asts , info , result ) do
176
185
runtime_type = AbstractType . get_object_type ( return_type , result , info . schema )
177
- sub_field_asts = collect_sub_fields ( context , runtime_type , field_asts )
186
+ { context , sub_field_asts } = collect_sub_fields ( context , runtime_type , field_asts )
178
187
execute_fields ( context , runtime_type , result , sub_field_asts . fields )
179
188
end
180
189
@@ -184,26 +193,28 @@ defmodule GraphQL.Execution.Executor do
184
193
185
194
@ spec complete_value ( ExecutionContext . t , % List { } , GraphQL.Document . t , any , any ) :: map
186
195
defp complete_value ( context , % List { ofType: list_type } , field_asts , info , result ) do
187
- Enum . map result , fn ( item ) ->
188
- complete_value_catching_error ( context , list_type , field_asts , info , item )
196
+ { context , result } = Enum . reduce result , { context , [ ] } , fn ( item , { context , acc } ) ->
197
+ { context , value } = complete_value_catching_error ( context , list_type , field_asts , info , item )
198
+ { context , [ value ] ++ acc }
189
199
end
200
+ { context , Enum . reverse ( result ) }
190
201
end
191
202
192
203
defp complete_value ( context , return_type , field_asts , info , result ) when is_atom ( return_type ) do
193
204
type = apply ( return_type , :type , [ ] )
194
205
complete_value ( context , type , field_asts , info , result )
195
206
end
196
207
197
- defp complete_value ( _context , return_type , _field_asts , _info , result ) do
198
- GraphQL.Types . serialize ( return_type , result )
208
+ defp complete_value ( context , return_type , _field_asts , _info , result ) do
209
+ { context , GraphQL.Types . serialize ( return_type , result ) }
199
210
end
200
211
201
212
defp collect_sub_fields ( context , return_type , field_asts ) do
202
- Enum . reduce field_asts , % { fields: % { } , fragments: % { } } , fn ( field_ast , field_fragment_map ) ->
213
+ Enum . reduce field_asts , { context , % { fields: % { } , fragments: % { } } } , fn ( field_ast , { context , field_fragment_map } ) ->
203
214
if selection_set = Map . get ( field_ast , :selectionSet ) do
204
215
collect_fields ( context , return_type , selection_set , field_fragment_map )
205
216
else
206
- field_fragment_map
217
+ { context , field_fragment_map }
207
218
end
208
219
end
209
220
end
@@ -293,7 +304,7 @@ defmodule GraphQL.Execution.Executor do
293
304
if condition_matches do
294
305
collect_fields ( context , runtime_type , selection . selectionSet , field_fragment_map )
295
306
else
296
- field_fragment_map
307
+ { context , field_fragment_map }
297
308
end
298
309
end
299
310
0 commit comments