Skip to content
This repository was archived by the owner on Jul 25, 2024. It is now read-only.

Commit f6027f9

Browse files
committed
Refactor: pass and return ExecutionContext everywhere
This will allow us to report errors from anywhere in the flow and accumulate them in the ExecutionContext.
1 parent fb7cae3 commit f6027f9

File tree

1 file changed

+38
-29
lines changed

1 file changed

+38
-29
lines changed

lib/graphql/execution/executor.ex

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,14 @@ defmodule GraphQL.Execution.Executor do
4242
@spec execute_operation(ExecutionContext.t, operation, map) :: result_data | {:error, String.t}
4343
defp execute_operation(context, operation, root_value) do
4444
type = operation_root_type(context.schema, operation)
45-
%{fields: fields} = collect_fields(context, type, operation.selectionSet)
45+
{context, %{fields: fields}} = collect_fields(context, type, operation.selectionSet)
4646
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)}
47+
:query ->
48+
{_, result} = execute_fields(context, type, root_value, fields)
49+
{:ok, result}
50+
:mutation ->
51+
{_, result} = execute_fields_serially(context, type, root_value, fields)
52+
{:ok, result}
4953
:subscription -> {:error, "Subscriptions not currently supported"}
5054
_ -> {:error, "Can only execute queries, mutations and subscriptions"}
5155
end
@@ -57,12 +61,12 @@ defmodule GraphQL.Execution.Executor do
5761
end
5862

5963
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) ->
64+
Enum.reduce selection_set[:selections], {context, field_fragment_map}, fn(selection, {context, field_fragment_map}) ->
6165
case selection do
6266
%{kind: :Field} ->
6367
field_name = field_entry_key(selection)
6468
fields = field_fragment_map.fields[field_name] || []
65-
put_in(field_fragment_map.fields[field_name], [selection | fields])
69+
{context, put_in(field_fragment_map.fields[field_name], [selection | fields])}
6670
%{kind: :InlineFragment} ->
6771
collect_fragment(context, runtime_type, selection, field_fragment_map)
6872
%{kind: :FragmentSpread} ->
@@ -71,36 +75,37 @@ defmodule GraphQL.Execution.Executor do
7175
field_fragment_map = put_in(field_fragment_map.fragments[fragment_name], true)
7276
collect_fragment(context, runtime_type, context.fragments[fragment_name], field_fragment_map)
7377
else
74-
field_fragment_map
78+
{context, field_fragment_map}
7579
end
76-
_ -> field_fragment_map
80+
_ -> {context, field_fragment_map}
7781
end
7882
end
7983
end
8084

81-
@spec execute_fields(ExecutionContext.t, atom | Map, any, any) :: any
85+
@spec execute_fields(ExecutionContext.t, atom | Map, any, any) :: {ExecutionContext.t, map}
8286
defp execute_fields(context, parent_type, source_value, fields) when is_atom(parent_type) do
8387
execute_fields(context, apply(parent_type, :type, []), source_value, fields)
8488
end
8589

86-
@spec execute_fields(ExecutionContext.t, atom | Map, any, any) :: any
90+
@spec execute_fields(ExecutionContext.t, atom | Map, any, any) :: {ExecutionContext.t, map}
8791
defp execute_fields(context, parent_type, source_value, fields) do
88-
Enum.reduce fields, %{}, fn({field_name_ast, field_asts}, results) ->
92+
Enum.reduce fields, {context, %{}}, fn({field_name_ast, field_asts}, {context, results}) ->
8993
case resolve_field(context, parent_type, source_value, field_asts) do
90-
:undefined -> results
91-
value -> Map.put(results, field_name_ast.value, value)
94+
{context, :undefined} -> {context, results}
95+
{context, value} -> {context, Map.put(results, field_name_ast.value, value)}
9296
end
9397
end
9498
end
9599

96-
@spec execute_fields_serially(ExecutionContext.t, atom, map, any) :: any
100+
@spec execute_fields_serially(ExecutionContext.t, atom, map, any) :: {ExecutionContext.t, map}
97101
defp execute_fields_serially(context, parent_type, source_value, fields) do
98102
# call execute_fields because no async operations yet
99103
execute_fields(context, parent_type, source_value, fields)
100104
end
101105

102106
defp resolve_field(context, parent_type, source, field_asts) do
103107
field_ast = hd(field_asts)
108+
# FIXME: possible memory leak
104109
field_name = String.to_atom(field_ast.name.value)
105110

106111
if field_def = field_definition(parent_type, field_name) do
@@ -137,44 +142,46 @@ defmodule GraphQL.Execution.Executor do
137142
end
138143
complete_value_catching_error(context, return_type, field_asts, info, result)
139144
else
140-
:undefined
145+
{context, :undefined}
141146
end
142147
end
143148

149+
@spec complete_value_catching_error(ExecutionContext.t, any, GraphQL.Document.t, any, map) :: {ExecutionContext.t, map | nil}
144150
defp complete_value_catching_error(context, return_type, field_asts, info, result) do
145151
# TODO lots of error checking
146152
complete_value(context, return_type, field_asts, info, result)
147153
end
148154

149-
defp complete_value(_, _, _, _, nil), do: nil
155+
@spec complete_value(ExecutionContext.t, any, any, any, nil) :: {ExecutionContext.t, nil}
156+
defp complete_value(context, _, _, _, nil), do: {context, nil}
150157

151-
@spec complete_value(ExecutionContext.t, %ObjectType{}, GraphQL.Document.t, any, map) :: map
158+
@spec complete_value(ExecutionContext.t, %ObjectType{}, GraphQL.Document.t, any, map) :: {ExecutionContext.t, map}
152159
defp complete_value(context, %ObjectType{} = return_type, field_asts, _info, result) do
153-
sub_field_asts = collect_sub_fields(context, return_type, field_asts)
160+
{context, sub_field_asts} = collect_sub_fields(context, return_type, field_asts)
154161
execute_fields(context, return_type, result, sub_field_asts.fields)
155162
end
156163

157164
defp complete_value(context, %NonNull{ofType: inner_type}, field_asts, info, result) when is_atom(inner_type) do
158165
complete_value(context, %NonNull{ofType: apply(inner_type, :type, [])}, field_asts, info, result)
159166
end
160167

161-
@spec complete_value(ExecutionContext.t, %NonNull{}, GraphQL.Document.t, any, any) :: map
168+
@spec complete_value(ExecutionContext.t, %NonNull{}, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
162169
defp complete_value(context, %NonNull{ofType: inner_type}, field_asts, info, result) do
163170
# TODO: Null Checking
164171
complete_value(context, inner_type, field_asts, info, result)
165172
end
166173

167-
@spec complete_value(ExecutionContext.t, %Interface{}, GraphQL.Document.t, any, any) :: map
174+
@spec complete_value(ExecutionContext.t, %Interface{}, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
168175
defp complete_value(context, %Interface{} = return_type, field_asts, info, result) do
169176
runtime_type = AbstractType.get_object_type(return_type, result, info.schema)
170-
sub_field_asts = collect_sub_fields(context, runtime_type, field_asts)
177+
{context, sub_field_asts} = collect_sub_fields(context, runtime_type, field_asts)
171178
execute_fields(context, runtime_type, result, sub_field_asts.fields)
172179
end
173180

174-
@spec complete_value(ExecutionContext.t, %Union{}, GraphQL.Document.t, any, any) :: map
181+
@spec complete_value(ExecutionContext.t, %Union{}, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
175182
defp complete_value(context, %Union{} = return_type, field_asts, info, result) do
176183
runtime_type = AbstractType.get_object_type(return_type, result, info.schema)
177-
sub_field_asts = collect_sub_fields(context, runtime_type, field_asts)
184+
{context, sub_field_asts} = collect_sub_fields(context, runtime_type, field_asts)
178185
execute_fields(context, runtime_type, result, sub_field_asts.fields)
179186
end
180187

@@ -184,26 +191,28 @@ defmodule GraphQL.Execution.Executor do
184191

185192
@spec complete_value(ExecutionContext.t, %List{}, GraphQL.Document.t, any, any) :: map
186193
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)
194+
{context, result} = Enum.reduce result, {context, []}, fn(item, {context, acc}) ->
195+
{context, value} = complete_value_catching_error(context, list_type, field_asts, info, item)
196+
{context, [value] ++ acc}
189197
end
198+
{context, Enum.reverse(result)}
190199
end
191200

192201
defp complete_value(context, return_type, field_asts, info, result) when is_atom(return_type) do
193202
type = apply(return_type, :type, [])
194203
complete_value(context, type, field_asts, info, result)
195204
end
196205

197-
defp complete_value(_context, return_type, _field_asts, _info, result) do
198-
GraphQL.Types.serialize(return_type, result)
206+
defp complete_value(context, return_type, _field_asts, _info, result) do
207+
{context, GraphQL.Types.serialize(return_type, result)}
199208
end
200209

201210
defp collect_sub_fields(context, return_type, field_asts) do
202-
Enum.reduce field_asts, %{fields: %{}, fragments: %{}}, fn(field_ast, field_fragment_map) ->
211+
Enum.reduce field_asts, {context, %{fields: %{}, fragments: %{}}}, fn(field_ast, {context, field_fragment_map}) ->
203212
if selection_set = Map.get(field_ast, :selectionSet) do
204213
collect_fields(context, return_type, selection_set, field_fragment_map)
205214
else
206-
field_fragment_map
215+
{context, field_fragment_map}
207216
end
208217
end
209218
end
@@ -293,7 +302,7 @@ defmodule GraphQL.Execution.Executor do
293302
if condition_matches do
294303
collect_fields(context, runtime_type, selection.selectionSet, field_fragment_map)
295304
else
296-
field_fragment_map
305+
{context, field_fragment_map}
297306
end
298307
end
299308

0 commit comments

Comments
 (0)