Skip to content

Commit a03b48b

Browse files
committed
feat(DeferredExecution) update for latest changes
1 parent f4c900e commit a03b48b

File tree

11 files changed

+78
-20
lines changed

11 files changed

+78
-20
lines changed

lib/graphql/execution/deferred_execution.rb

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ class DeferredExecution
5151
# result was defered, so it should be empty in the patch.
5252
DEFERRED_RESULT = :__deferred_result__
5353

54+
# TODO: this is necessary to send the error to a context where
55+
# the parent type name is available
56+
class InnerInvalidNullError < GraphQL::Error
57+
attr_reader :field_name, :value
58+
def initialize(field_name, value)
59+
@field_name = field_name
60+
@value = value
61+
end
62+
end
63+
5464
# Execute `ast_operation`, a member of `query_object`, starting from `root_type`.
5565
#
5666
# Results will be sent to `query_object.context[CONTEXT_PATCH_TARGET]` in the form
@@ -260,7 +270,14 @@ def resolve_frame(scope, thread, frame)
260270
else
261271
raise("No defined resolution for #{ast_node.class.name} (#{ast_node})")
262272
end
263-
rescue GraphQL::InvalidNullError => err
273+
rescue InvalidNullError, InnerInvalidNullError => inner_err
274+
# TODO omg
275+
err = if inner_err.is_a?(InnerInvalidNullError)
276+
GraphQL::InvalidNullError.new(type_defn.name, inner_err.field_name, inner_err.value)
277+
else
278+
inner_err
279+
end
280+
264281
if return_type_defn && return_type_defn.kind.non_null?
265282
raise(err)
266283
else
@@ -278,9 +295,9 @@ def resolve_selections(scope, thread, outer_frame)
278295

279296
resolved_selections = merged_selections.each_with_object({}) do |(name, irep_selection), memo|
280297
field_applies_to_type = irep_selection.definitions.any? do |child_type, defn|
281-
GraphQL::Execution::Typecast.compatible?(outer_frame.value, child_type, outer_frame.type, query.context)
298+
GraphQL::Execution::Typecast.compatible?(child_type, outer_frame.type, query.context)
282299
end
283-
if field_applies_to_type && !GraphQL::Execution::DirectiveChecks.skip?(irep_selection, query)
300+
if field_applies_to_type && irep_selection.included?
284301
selection_key = irep_selection.name
285302

286303
inner_frame = ExecFrame.new(
@@ -336,9 +353,19 @@ def resolve_field_frame(scope, thread, frame, field_defn)
336353
resolve_fn_value = err
337354
end
338355

339-
if resolve_fn_value.is_a?(GraphQL::ExecutionError)
356+
case resolve_fn_value
357+
when GraphQL::ExecutionError
340358
thread.errors << resolve_fn_value
341359
resolve_fn_value.ast_node = ast_node
360+
resolve_fn_value.path = frame.path
361+
when Array
362+
resolve_fn_value.each_with_index do |item, idx|
363+
if item.is_a?(GraphQL::ExecutionError)
364+
item.ast_node = ast_node
365+
item.path = frame.path + [idx]
366+
thread.errors << item
367+
end
368+
end
342369
end
343370

344371
resolve_fn_value
@@ -351,7 +378,7 @@ def resolve_field_frame(scope, thread, frame, field_defn)
351378
def resolve_value(scope, thread, frame, value, type_defn)
352379
if value.nil? || value.is_a?(GraphQL::ExecutionError)
353380
if type_defn.kind.non_null?
354-
raise GraphQL::InvalidNullError.new(frame.node.ast_node.name, value)
381+
raise InnerInvalidNullError.new(frame.node.ast_node.name, value)
355382
else
356383
nil
357384
end
@@ -360,10 +387,10 @@ def resolve_value(scope, thread, frame, value, type_defn)
360387
when GraphQL::TypeKinds::SCALAR, GraphQL::TypeKinds::ENUM
361388
type_defn.coerce_result(value)
362389
when GraphQL::TypeKinds::INTERFACE, GraphQL::TypeKinds::UNION
363-
resolved_type = type_defn.resolve_type(value, scope)
390+
resolved_type = scope.schema.resolve_type(value, scope.query.context)
364391

365-
if !resolved_type.is_a?(GraphQL::ObjectType)
366-
raise GraphQL::ObjectType::UnresolvedTypeError.new(frame.node.definition_name, type_defn, frame.node.parent.return_type)
392+
if !resolved_type.is_a?(GraphQL::ObjectType) || !scope.schema.possible_types(type_defn).include?(resolved_type)
393+
raise GraphQL::ObjectType::UnresolvedTypeError.new(frame.node.definition_name, scope.schema, type_defn, frame.node.parent.return_type, resolved_type)
367394
else
368395
resolve_value(scope, thread, frame, value, resolved_type)
369396
end

lib/graphql/internal_representation/node.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def owner
8080
end
8181
end
8282

83+
# TODO? This can be deprecated: ExecFrames track their paths
8384
def path
8485
if parent
8586
path = parent.path

lib/graphql/schema.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ class Schema
6262
:query_analyzers, :middleware
6363

6464
DIRECTIVES = [
65-
GraphQL::Directive::SkipDirective,
6665
GraphQL::Directive::IncludeDirective,
66+
GraphQL::Directive::SkipDirective,
6767
GraphQL::Directive::DeprecatedDirective,
6868
GraphQL::Directive::DeferDirective,
6969
GraphQL::Directive::StreamDirective,

lib/graphql/schema/printer.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ def print_schema_definition(schema)
5656
private_constant :BUILTIN_SCALARS
5757

5858
def is_spec_directive(directive)
59-
['skip', 'include', 'deprecated'].include?(directive.name)
59+
# TODO: make defer & stream opt-in
60+
['skip', 'include', 'deprecated', 'defer', 'stream'].include?(directive.name)
6061
end
6162

6263
def is_introspection_type(type)

spec/graphql/execution/deferred_execution_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,12 @@ def merged_result
218218
result
219219
assert_equal(3, collector.patches.length)
220220
assert_equal([], collector.patches[0][:path])
221-
assert_equal([{"message" => "There was an execution error", "locations"=>[{"line"=>3, "column"=>11}]}], collector.patches[0][:value]["errors"])
221+
assert_equal([{"message" => "There was an execution error", "locations"=>[{"line"=>3, "column"=>11}], "path"=>["error1"]}], collector.patches[0][:value]["errors"])
222222
assert_equal({"error1"=>nil}, collector.patches[0][:value]["data"])
223223
assert_equal(["errors", 1], collector.patches[1][:path])
224-
assert_equal({"message"=>"There was an execution error", "locations"=>[{"line"=>4, "column"=>11}]}, collector.patches[1][:value])
224+
assert_equal({"message"=>"There was an execution error", "locations"=>[{"line"=>4, "column"=>11}], "path"=>["error2"]}, collector.patches[1][:value])
225225
assert_equal(["errors", 2], collector.patches[2][:path])
226-
assert_equal({"message"=>"There was an execution error", "locations"=>[{"line"=>5, "column"=>11}]}, collector.patches[2][:value])
226+
assert_equal({"message"=>"There was an execution error", "locations"=>[{"line"=>5, "column"=>11}], "path"=>["error3"]}, collector.patches[2][:value])
227227
end
228228
end
229229

@@ -334,7 +334,7 @@ def merged_result
334334
}
335335
end
336336
end
337-
schema = GraphQL::Schema.new(query: query_type)
337+
schema = GraphQL::Schema.define(query: query_type)
338338
schema.query_execution_strategy = GraphQL::Execution::DeferredExecution
339339
schema
340340
}

spec/graphql/execution_error_spec.rb

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,15 @@
6767
{ "flavor" => "Brie" },
6868
{ "flavor" => "Gouda" },
6969
{ "flavor" => "Manchego" },
70-
{ "source" => "COW", "executionError" => nil }
70+
{ "source" => "COW", "executionError" => nil },
71+
{ "source" => "COW", "executionError" => nil },
7172
],
7273
"dairyErrors" => [
7374
{ "__typename" => "Cheese" },
7475
nil,
7576
{ "__typename" => "Cheese" },
76-
{ "__typename" => "Milk" }
77+
{ "__typename" => "Milk" },
78+
{ "__typename" => "Milk" },
7779
],
7880
"dairy" => {
7981
"milks" => [
@@ -84,7 +86,8 @@
8486
{ "__typename" => "Cheese" },
8587
{ "__typename" => "Cheese" },
8688
{ "__typename" => "Cheese" },
87-
{ "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil }
89+
{ "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil },
90+
{ "__typename" => "Milk", "origin" => "Modernity", "executionError" => nil },
8891
]
8992
}
9093
]
@@ -107,6 +110,11 @@
107110
"locations"=>[{"line"=>22, "column"=>11}],
108111
"path"=>["allDairy", 3, "executionError"]
109112
},
113+
{
114+
"message"=>"There was an execution error",
115+
"locations"=>[{"line"=>22, "column"=>11}],
116+
"path"=>["allDairy", 4, "executionError"]
117+
},
110118
{
111119
"message"=>"missing dairy",
112120
"locations"=>[{"line"=>25, "column"=>7}],
@@ -122,6 +130,11 @@
122130
"locations"=>[{"line"=>36, "column"=>15}],
123131
"path"=>["dairy", "milks", 0, "allDairy", 3, "executionError"]
124132
},
133+
{
134+
"message"=>"There was an execution error",
135+
"locations"=>[{"line"=>36, "column"=>15}],
136+
"path"=>["dairy", "milks", 0, "allDairy", 4, "executionError"]
137+
},
125138
{
126139
"message"=>"There was an execution error",
127140
"locations"=>[{"line"=>41, "column"=>7}],
@@ -163,7 +176,8 @@
163176
{ "__typename" => "Cheese" },
164177
{ "__typename" => "Cheese" },
165178
{ "__typename" => "Cheese" },
166-
{ "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil }
179+
{ "__typename" => "Milk", "origin" => "Antiquity", "executionError" => nil },
180+
{ "__typename" => "Milk", "origin" => "Modernity", "executionError" => nil },
167181
]
168182
}
169183
]
@@ -179,7 +193,12 @@
179193
"message"=>"There was an execution error",
180194
"locations"=>[{"line"=>11, "column"=>15}],
181195
"path"=>["dairy", "milks", 0, "allDairy", 3, "executionError"]
182-
}
196+
},
197+
{
198+
"message"=>"There was an execution error",
199+
"locations"=>[{"line"=>11, "column"=>15}],
200+
"path"=>["dairy", "milks", 0, "allDairy", 4, "executionError"]
201+
},
183202
]
184203
}
185204
assert_equal(expected_result, result)

spec/graphql/interface_type_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
{"__typename"=>"Cheese", "origin"=>"Netherlands"},
7878
{"__typename"=>"Cheese", "origin"=>"Spain"},
7979
{"__typename"=>"Milk", "origin"=>"Antiquity"},
80+
{"__typename"=>"Milk", "origin"=>"Modernity"},
8081
]
8182

8283
assert_equal expected_data, result["data"]["allEdible"]

spec/graphql/introspection/schema_type_spec.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
{"name"=>"fromSource"},
3232
{"name"=>"maybeNull"},
3333
{"name"=>"milk"},
34+
{"name"=>"milks"},
3435
{"name"=>"root"},
3536
{"name"=>"searchDairy"},
3637
]

spec/graphql/schema/printer_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@
154154
reason: String = "No longer supported"
155155
) on FIELD_DEFINITION | ENUM_VALUE
156156
157+
# Push this part of the query in a later patch
158+
directive @defer on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
159+
160+
# Push items from this list in sequential patches
161+
directive @stream on FIELD
162+
157163
# A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
158164
#
159165
# In some cases, you need to provide options to alter GraphQL's execution behavior

spec/graphql/union_type_spec.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
{"dairyName"=>"Cheese"},
4848
{"dairyName"=>"Cheese"},
4949
{"dairyName"=>"Milk", "bevName"=>"Milk", "flavors"=>["Natural", "Chocolate", "Strawberry"]},
50+
{"dairyName"=>"Milk", "bevName"=>"Milk", "flavors"=>["Natural"]},
5051
]
52+
5153
assert_equal expected_result, result["data"]["allDairy"]
5254
end
5355
end

0 commit comments

Comments
 (0)