Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support returning [Type, nil] from resolve_type #4130

Merged
merged 1 commit into from Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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