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

Commit c253b86

Browse files
author
Josh Price
committed
Merge pull request #87 from freshtonic/refactor/executor
Visitor and executor refactorings
2 parents f6d3651 + 8f3753c commit c253b86

13 files changed

Lines changed: 118 additions & 121 deletions

File tree

lib/graphql/execution/executor.ex

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ defmodule GraphQL.Execution.Executor do
4545
:mutation ->
4646
{context, result} = execute_fields_serially(context, type, root_value, fields)
4747
{:ok, result, context.errors}
48-
:subscription -> {:error, "Subscriptions not currently supported"}
49-
_ -> {:error, "Can only execute queries, mutations and subscriptions"}
48+
:subscription ->
49+
{:error, "Subscriptions not currently supported"}
50+
_ ->
51+
{:error, "Can only execute queries, mutations and subscriptions"}
5052
end
5153
end
5254

@@ -107,15 +109,10 @@ defmodule GraphQL.Execution.Executor do
107109
end
108110
end
109111

110-
@spec execute_fields(ExecutionContext.t, atom | Map, any, any) :: {ExecutionContext.t, map}
111-
defp execute_fields(context, parent_type, source_value, fields) when is_atom(parent_type) do
112-
execute_fields(context, apply(parent_type, :type, []), source_value, fields)
113-
end
114-
115112
@spec execute_fields(ExecutionContext.t, atom | Map, any, any) :: {ExecutionContext.t, map}
116113
defp execute_fields(context, parent_type, source_value, fields) do
117114
Enum.reduce fields, {context, %{}}, fn({field_name_ast, field_asts}, {context, results}) ->
118-
case resolve_field(context, parent_type, source_value, field_asts) do
115+
case resolve_field(context, unwrap_type(parent_type), source_value, field_asts) do
119116
{context, :undefined} -> {context, results}
120117
{context, value} -> {context, Map.put(results, field_name_ast.value, value)}
121118
end
@@ -130,8 +127,7 @@ defmodule GraphQL.Execution.Executor do
130127

131128
defp resolve_field(context, parent_type, source, field_asts) do
132129
field_ast = hd(field_asts)
133-
# FIXME: possible memory leak with atoms
134-
field_name = String.to_atom(field_ast.name.value)
130+
field_name = String.to_existing_atom(field_ast.name.value)
135131

136132
if field_def = field_definition(parent_type, field_name) do
137133
return_type = field_def.type
@@ -156,7 +152,7 @@ defmodule GraphQL.Execution.Executor do
156152

157153
case FieldResolver.resolve(field_def, source, args, info) do
158154
{:ok, result} ->
159-
complete_value_catching_error(context, return_type, field_asts, info, result)
155+
complete_value(return_type, context, field_asts, info, result)
160156
{:error, message} ->
161157
{ExecutionContext.report_error(context, message), nil}
162158
end
@@ -165,65 +161,50 @@ defmodule GraphQL.Execution.Executor do
165161
end
166162
end
167163

168-
@spec complete_value_catching_error(ExecutionContext.t, any, GraphQL.Document.t, any, map) :: {ExecutionContext.t, map | nil}
169-
defp complete_value_catching_error(context, return_type, field_asts, info, result) do
170-
# TODO lots of error checking
171-
complete_value(context, return_type, field_asts, info, result)
172-
end
173-
174-
@spec complete_value(ExecutionContext.t, any, any, any, nil) :: {ExecutionContext.t, nil}
175-
defp complete_value(context, _, _, _, nil), do: {context, nil}
164+
@spec complete_value(any, ExecutionContext.t, any, any, nil) :: {ExecutionContext.t, nil}
165+
defp complete_value(_, context, _, _, nil), do: {context, nil}
176166

177-
@spec complete_value(ExecutionContext.t, %ObjectType{}, GraphQL.Document.t, any, map) :: {ExecutionContext.t, map}
178-
defp complete_value(context, %ObjectType{} = return_type, field_asts, _info, result) do
167+
@spec complete_value(%ObjectType{}, ExecutionContext.t, GraphQL.Document.t, any, map) :: {ExecutionContext.t, map}
168+
defp complete_value(%ObjectType{} = return_type, context, field_asts, _info, result) do
179169
{context, sub_field_asts} = collect_sub_fields(context, return_type, field_asts)
180170
execute_fields(context, return_type, result, sub_field_asts.fields)
181171
end
182172

183-
defp complete_value(context, %NonNull{ofType: inner_type}, field_asts, info, result) when is_atom(inner_type) do
184-
complete_value(context, %NonNull{ofType: apply(inner_type, :type, [])}, field_asts, info, result)
185-
end
186-
187-
@spec complete_value(ExecutionContext.t, %NonNull{}, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
188-
defp complete_value(context, %NonNull{ofType: inner_type}, field_asts, info, result) do
173+
@spec complete_value(%NonNull{}, ExecutionContext.t, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
174+
defp complete_value(%NonNull{ofType: inner_type}, context, field_asts, info, result) do
189175
# TODO: Null Checking
190-
complete_value(context, inner_type, field_asts, info, result)
176+
complete_value(unwrap_type(inner_type), context, field_asts, info, result)
191177
end
192178

193-
@spec complete_value(ExecutionContext.t, %Interface{}, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
194-
defp complete_value(context, %Interface{} = return_type, field_asts, info, result) do
179+
@spec complete_value(%Interface{}, ExecutionContext.t, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
180+
defp complete_value(%Interface{} = return_type, context, field_asts, info, result) do
195181
runtime_type = AbstractType.get_object_type(return_type, result, info.schema)
196182
{context, sub_field_asts} = collect_sub_fields(context, runtime_type, field_asts)
197183
execute_fields(context, runtime_type, result, sub_field_asts.fields)
198184
end
199185

200-
@spec complete_value(ExecutionContext.t, %Union{}, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
201-
defp complete_value(context, %Union{} = return_type, field_asts, info, result) do
186+
@spec complete_value(%Union{}, ExecutionContext.t, GraphQL.Document.t, any, any) :: {ExecutionContext.t, map}
187+
defp complete_value(%Union{} = return_type, context, field_asts, info, result) do
202188
runtime_type = AbstractType.get_object_type(return_type, result, info.schema)
203189
{context, sub_field_asts} = collect_sub_fields(context, runtime_type, field_asts)
204190
execute_fields(context, runtime_type, result, sub_field_asts.fields)
205191
end
206192

207-
defp complete_value(context, %List{ofType: list_type}, field_asts, info, result) when is_atom(list_type) do
208-
complete_value(context, %List{ofType: apply(list_type, :type, [])}, field_asts, info, result)
209-
end
210-
211-
@spec complete_value(ExecutionContext.t, %List{}, GraphQL.Document.t, any, any) :: map
212-
defp complete_value(context, %List{ofType: list_type}, field_asts, info, result) do
193+
@spec complete_value(%List{}, ExecutionContext.t, GraphQL.Document.t, any, any) :: map
194+
defp complete_value(%List{ofType: list_type}, context, field_asts, info, result) do
213195
{context, result} = Enum.reduce result, {context, []}, fn(item, {context, acc}) ->
214-
{context, value} = complete_value_catching_error(context, list_type, field_asts, info, item)
196+
{context, value} = complete_value(unwrap_type(list_type), context, field_asts, info, item)
215197
{context, [value] ++ acc}
216198
end
217199
{context, Enum.reverse(result)}
218200
end
219201

220-
defp complete_value(context, return_type, field_asts, info, result) when is_atom(return_type) do
221-
type = apply(return_type, :type, [])
222-
complete_value(context, type, field_asts, info, result)
202+
defp complete_value(return_type, context, field_asts, info, result) when is_atom(return_type) do
203+
complete_value(unwrap_type(return_type), context, field_asts, info, result)
223204
end
224205

225-
defp complete_value(context, return_type, _field_asts, _info, result) do
226-
{context, GraphQL.Types.serialize(return_type, result)}
206+
defp complete_value(return_type, context, _field_asts, _info, result) do
207+
{context, GraphQL.Types.serialize(unwrap_type(return_type), result)}
227208
end
228209

229210
defp collect_sub_fields(context, return_type, field_asts) do
@@ -311,7 +292,7 @@ defmodule GraphQL.Execution.Executor do
311292
def value_from_ast(nil, _, _), do: nil # remove once NonNull is actually done..
312293

313294
def value_from_ast(value_ast, type, variable_values) when is_atom(type) do
314-
value_from_ast(value_ast, apply(type, :type, []), variable_values)
295+
value_from_ast(value_ast, type.type, variable_values)
315296
end
316297

317298
def value_from_ast(value_ast, type, _) do
@@ -355,4 +336,7 @@ defmodule GraphQL.Execution.Executor do
355336
true -> runtime_type.name == typed_condition.name
356337
end
357338
end
339+
340+
defp unwrap_type(type) when is_atom(type), do: type.type
341+
defp unwrap_type(type), do: type
358342
end

lib/graphql/execution/field_resolver.ex

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,20 @@ defmodule GraphQL.Execution.FieldResolver do
33
alias GraphQL.Execution.Resolvable
44

55
def resolve(field_def, source, args, info) do
6-
Resolvable.resolve(Map.get(field_def, :resolve), deref_source(source), args, info)
6+
Resolvable.resolve(
7+
Map.get(field_def, :resolve),
8+
unwrap_type(source),
9+
args,
10+
info
11+
)
712
end
813

9-
defp deref_source(nil), do: nil
10-
defp deref_source(source) when is_atom(source) do
11-
apply(source, :type, [])
14+
# FIXME: this `unwrap_type` logic is duplicated in a few places.
15+
defp unwrap_type(nil), do: nil
16+
defp unwrap_type(source) when is_atom(source) do
17+
source.type
1218
end
13-
defp deref_source(source), do: source
19+
defp unwrap_type(source), do: source
1420
end
1521

1622

lib/graphql/execution/resolvable.ex

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,16 @@ alias GraphQL.Execution.ResolveWrapper
2020
defimpl GraphQL.Execution.Resolvable, for: Function do
2121
def resolve(fun, source, args, info) do
2222
ResolveWrapper.wrap fn() ->
23-
fun.(source, args, info)
23+
case arity(fun) do
24+
0 -> fun.()
25+
1 -> fun.(source)
26+
2 -> fun.(source, args)
27+
3 -> fun.(source, args, info)
28+
end
2429
end
2530
end
31+
32+
defp arity(fun), do: :erlang.fun_info(fun)[:arity]
2633
end
2734

2835
defimpl GraphQL.Execution.Resolvable, for: Tuple do

lib/graphql/lang/ast/parallel_visitor.ex

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ defmodule GraphQL.Lang.AST.ParallelVisitor do
1111
This code based on the graphql-js *visitInParallel* function.
1212
"""
1313

14-
alias GraphQL.Lang.AST.Visitor
15-
alias GraphQL.Lang.AST.InitialisingVisitor
16-
alias GraphQL.Lang.AST.PostprocessingVisitor
14+
alias GraphQL.Lang.AST.{
15+
Visitor,
16+
InitialisingVisitor,
17+
PostprocessingVisitor
18+
}
1719

1820
defstruct visitors: []
1921

lib/graphql/lang/ast/type_info_visitor.ex

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ defmodule GraphQL.Lang.AST.TypeInfoVisitor do
9494
type = case node.operation do
9595
:query -> accumulator[:type_info].schema.query
9696
:mutation -> accumulator[:type_info].schema.mutation
97-
# TODO 'subscription' -> schema.subscription
9897
_ -> raise "node operation #{node.operation} not handled"
9998
end
10099
stack_push(:type_stack, type)

lib/graphql/type/introspection.ex

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,24 @@ defmodule GraphQL.Type.Introspection do
3535
types: %{
3636
description: "A list of all types supported by this server.",
3737
type: %NonNull{ofType: %List{ofType: %NonNull{ofType: Type}}},
38-
resolve: fn(schema, _, _) ->
38+
resolve: fn(schema) ->
3939
Map.values(schema.type_cache)
4040
end
4141
},
4242
queryType: %{
4343
description: "The type that query operations will be rooted at.",
4444
type: %NonNull{ofType: Type},
45-
resolve: fn(%{query: query}, _, _) -> query end
45+
resolve: fn(%{query: query}) -> query end
4646
},
4747
mutationType: %{
4848
description: "If this server supports mutation, the type that mutation operations will be rooted at.",
4949
type: Type,
50-
resolve: fn(%{mutation: mutation}, _, _) -> mutation end
50+
resolve: fn(%{mutation: mutation}) -> mutation end
5151
},
5252
subscriptionType: %{
5353
description: "If this server support subscription, the type that subscription operations will be rooted at.",
5454
type: Type,
55-
resolve: nil #fn(%{subscription: subscription}, _, _) -> subscription end
55+
resolve: nil #fn(%{subscription: subscription}, _, _,_) -> subscription end
5656
},
5757
directives: %{
5858
description: "A list of all directives supported by this server.",
@@ -112,7 +112,7 @@ defmodule GraphQL.Type.Introspection do
112112
fields: %{
113113
kind: %{
114114
type: %NonNull{ofType: TypeKind},
115-
resolve: fn(schema, _, _) ->
115+
resolve: fn(schema) ->
116116
case schema do
117117
%ObjectType{} -> "OBJECT"
118118
%Interface{} -> "INTERFACE"
@@ -137,22 +137,22 @@ defmodule GraphQL.Type.Introspection do
137137
type: %List{ofType: %NonNull{ofType: Field}},
138138
args: %{includeDeprecated: %{type: %Boolean{}, defaultValue: false}},
139139
resolve: fn
140-
(%ObjectType{} = schema, _, _) ->
140+
(%ObjectType{} = schema) ->
141141
thunk_fields = CompositeType.get_fields(schema)
142142
Enum.map(thunk_fields, fn({n, v}) -> Map.put(v, :name, n) end)
143143
# |> filter_deprecated
144-
(%Interface{} = schema, _, _) ->
144+
(%Interface{} = schema) ->
145145
thunk_fields = CompositeType.get_fields(schema)
146146
Enum.map(thunk_fields, fn({n, v}) -> Map.put(v, :name, n) end)
147-
(_, _, _) -> nil
147+
(_) -> nil
148148
end
149149
},
150150
interfaces: %{
151151
type: %List{ofType: %NonNull{ofType: Type}},
152152
resolve: fn
153-
(%ObjectType{} = schema, _, _) ->
153+
(%ObjectType{} = schema) ->
154154
schema.interfaces
155-
(_, _, _) -> nil
155+
(_) -> nil
156156
end
157157
},
158158
possibleTypes: %{
@@ -169,22 +169,22 @@ defmodule GraphQL.Type.Introspection do
169169
type: %List{ofType: %NonNull{ofType: EnumValue}},
170170
args: %{includeDeprecated: %{type: %Boolean{}, defaultValue: false}},
171171
resolve: fn
172-
(%GraphQL.Type.Enum{} = schema, _, _) -> schema.values
173-
(_, _, _) -> nil
172+
(%GraphQL.Type.Enum{} = schema) -> schema.values
173+
(_) -> nil
174174
end
175175
},
176176
inputFields: %{
177177
type: %List{ofType: %NonNull{ofType: InputValue}},
178178
resolve: fn
179-
(%GraphQL.Type.Input{} = type, _args, _info) ->
179+
(%GraphQL.Type.Input{} = type) ->
180180
fields = type.fields
181181
Enum.map(Map.keys(fields), fn(key) ->
182182
%{
183183
name: key,
184184
type: fields[key].type
185185
}
186186
end)
187-
(_, _, _) -> nil
187+
(_) -> nil
188188
end
189189
},
190190
ofType: %{type: Type}
@@ -251,9 +251,9 @@ defmodule GraphQL.Type.Introspection do
251251
args: %{
252252
type: %NonNull{ofType: %List{ofType: %NonNull{ofType: InputValue}}},
253253
resolve: fn
254-
(%{args: _args} = schema, _, _) ->
254+
(%{args: _args} = schema) ->
255255
Enum.map(schema.args, fn({name, v}) -> Map.put(v, :name, name) end)
256-
(_, _, _) -> []
256+
(_) -> []
257257
end
258258
},
259259
type: %{type: %NonNull{ofType: Type}},

lib/graphql/validation/validator.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ defmodule GraphQL.Validation.Validator do
2525
Short circuits the validations entirely when there are no rules specified.
2626
This is useful for examining the performance impact of validations.
2727
"""
28-
def validate_with_rules(schema, document, rules) when length(rules) == 0, do: :ok
28+
def validate_with_rules(_schema, _document, rules) when length(rules) == 0, do: :ok
2929

3030
@doc """
3131
For performance testing with a single rule, the overhead of the ParallelVisitor

test/graphql/execution/executor_blog_schema_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ defmodule GraphQL.Execution.Executor.ExecutorBlogSchemaTest do
5858
height: %{type: %Int{}}
5959
},
6060
type: image,
61-
resolve: fn(o, %{width: w, height: h}, _) -> o.pic.(w, h) end
61+
resolve: fn(o, %{width: w, height: h}) -> o.pic.(w, h) end
6262
},
6363
recentArticle: nil
6464
}
@@ -86,11 +86,11 @@ defmodule GraphQL.Execution.Executor.ExecutorBlogSchemaTest do
8686
article: %{
8787
type: article,
8888
args: %{id: %{type: %ID{}}},
89-
resolve: fn(_, %{id: id}, _) -> make_article(id) end
89+
resolve: fn(_, %{id: id}) -> make_article(id) end
9090
},
9191
feed: %{
9292
type: %List{ofType: article},
93-
resolve: fn(_, _, _) -> for id <- 1..2, do: make_article(id) end
93+
resolve: fn() -> for id <- 1..2, do: make_article(id) end
9494
}
9595
}
9696
}

0 commit comments

Comments
 (0)