Skip to content

Commit

Permalink
Merge pull request #4130 from rmosolgo/nil-object-from-resolve-type
Browse files Browse the repository at this point in the history
Support returning [Type, nil] from resolve_type
  • Loading branch information
rmosolgo committed Jul 4, 2022
2 parents 4905982 + b0b075a commit 28bb436
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 22 deletions.
12 changes: 8 additions & 4 deletions lib/graphql/execution/interpreter/runtime.rb
Expand Up @@ -685,12 +685,16 @@ def continue_field(path, value, owner_type, field, current_type, ast_node, next_
set_result(selection_result, result_name, r)
r
when "UNION", "INTERFACE"
resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
resolved_value ||= value
resolved_type_or_lazy = resolve_type(current_type, value, path)
after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type_result|
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
resolved_type, resolved_value = resolved_type_result
else
resolved_type = resolved_type_result
resolved_value = value
end

after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type|
possible_types = query.possible_types(current_type)

if !possible_types.include?(resolved_type)
parent_type = field.owner_type
err_class = current_type::UnresolvedTypeError
Expand Down
12 changes: 10 additions & 2 deletions lib/graphql/schema.rb
Expand Up @@ -754,13 +754,21 @@ def handle_or_reraise(context, err)
# rubocop:disable Lint/DuplicateMethods
module ResolveTypeWithType
def resolve_type(type, obj, ctx)
first_resolved_type, resolved_value = if type.is_a?(Module) && type.respond_to?(:resolve_type)
maybe_lazy_resolve_type_result = if type.is_a?(Module) && type.respond_to?(:resolve_type)
type.resolve_type(obj, ctx)
else
super
end

after_lazy(first_resolved_type) do |resolved_type|
after_lazy(maybe_lazy_resolve_type_result) do |resolve_type_result|
if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
resolved_type = resolve_type_result[0]
resolved_value = resolve_type_result[1]
else
resolved_type = resolve_type_result
resolved_value = obj
end

if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind))
if resolved_value
[resolved_type, resolved_value]
Expand Down
10 changes: 8 additions & 2 deletions lib/graphql/schema/member/has_arguments.rb
Expand Up @@ -323,8 +323,14 @@ def authorize_application_object(argument, id, context, loaded_application_objec
end
# Double-check that the located object is actually of this type
# (Don't want to allow arbitrary access to objects this way)
resolved_application_object_type = context.schema.resolve_type(argument.loads, application_object, context)
context.schema.after_lazy(resolved_application_object_type) do |application_object_type|
maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
context.schema.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
application_object_type, application_object = resolve_type_result
else
application_object_type = resolve_type_result
# application_object is already assigned
end
possible_object_types = context.warden.possible_types(argument.loads)
if !possible_object_types.include?(application_object_type)
err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
Expand Down
55 changes: 41 additions & 14 deletions spec/graphql/schema/union_spec.rb
Expand Up @@ -67,24 +67,32 @@
assert_equal "Fragment on Ensemble can't be spread inside PerformingAct", res.to_h["errors"].first["message"]
end

it "can cast the object after resolving the type" do
describe "two-value type resolution" do
Box = Struct.new(:value)

class Schema < GraphQL::Schema
class A < GraphQL::Schema::Object
field :a, String, null: false, method: :itself
end

class B < GraphQL::Schema::Object
field :b, String, method: :itself
end

class MyUnion < GraphQL::Schema::Union
possible_types A
possible_types A, B

def self.resolve_type(object, ctx)
[A, object.value]
if object.value == "return-nil"
[B, nil]
else
[A, object.value]
end
end
end

class Query < GraphQL::Schema::Object
field :my_union, MyUnion, null: false
field :my_union, MyUnion

def my_union
Box.new(context[:value])
Expand All @@ -94,19 +102,38 @@ def my_union
query(Query)
end

query_str = <<-GRAPHQL
{
myUnion {
... on A { a }
it "can cast the object after resolving the type" do

query_str = <<-GRAPHQL
{
myUnion {
... on A { a }
}
}
}
GRAPHQL
GRAPHQL

res = Schema.execute(query_str, context: { value: "unwrapped" })
res = Schema.execute(query_str, context: { value: "unwrapped" })

assert_equal({
'data' => { 'myUnion' => { 'a' => 'unwrapped' } }
}, res.to_h)
assert_equal({
'data' => { 'myUnion' => { 'a' => 'unwrapped' } }
}, res.to_h)
end

it "uses `nil` when returned from resolve_type" do
query_str = <<-GRAPHQL
{
myUnion {
... on B { b }
}
}
GRAPHQL

res = Schema.execute(query_str, context: { value: "return-nil" })

assert_equal({
'data' => { 'myUnion' => { 'b' => nil } }
}, res.to_h)
end
end
end

Expand Down

0 comments on commit 28bb436

Please sign in to comment.