Skip to content

Commit

Permalink
Add possible_types check for unions that are used only for loads:
Browse files Browse the repository at this point in the history
  • Loading branch information
rmosolgo committed Dec 3, 2024
1 parent 46af139 commit 93ab788
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 7 deletions.
18 changes: 13 additions & 5 deletions lib/graphql/schema/member/has_arguments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,8 @@ def authorize_application_object(argument, id, context, loaded_application_objec
if application_object.nil?
nil
else
maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
arg_loads_type = argument.loads
maybe_lazy_resolve_type = context.schema.resolve_type(arg_loads_type, application_object, context)
context.query.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
Expand All @@ -368,10 +369,17 @@ def authorize_application_object(argument, id, context, loaded_application_objec
# application_object is already assigned
end

if !(
context.types.possible_types(argument.loads).include?(application_object_type) ||
context.types.loadable?(argument.loads, context)
)
passes_possible_types_check = if context.types.loadable?(arg_loads_type, context)
if arg_loads_type.kind.union?
# This union is used in `loads:` but not otherwise visible to this query
context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type)
else
true
end
else
context.types.possible_types(arg_loads_type).include?(application_object_type)
end
if !passes_possible_types_check
err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
application_object = load_application_object_failed(err)
end
Expand Down
1 change: 1 addition & 0 deletions lib/graphql/schema/visibility/migration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def loaded_types
:all_types_h,
:fields,
:loadable?,
:loadable_possible_types,
:type,
:arguments,
:argument,
Expand Down
6 changes: 6 additions & 0 deletions lib/graphql/schema/visibility/profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ def initialize(name: nil, context:, schema:)
@cached_arguments = Hash.new do |h, owner|
h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
end.compare_by_identity

@loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
end

def field_on_visible_interface?(field, owner)
Expand Down Expand Up @@ -249,6 +251,10 @@ def loadable?(t, _ctx)
!@all_types[t.graphql_name] && @cached_visible[t]
end

def loadable_possible_types(t, _ctx)
@loadable_possible_types[t]
end

def loaded_types
@all_types.values
end
Expand Down
15 changes: 14 additions & 1 deletion lib/graphql/schema/warden.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
def arguments(owner, ctx); owner.arguments(ctx); end
def loadable?(type, ctx); type.visible?(ctx); end
def loadable_possible_types(type, ctx); type.possible_types(ctx); end
def visibility_profile
@visibility_profile ||= Warden::VisibilityProfile.new(self)
end
Expand Down Expand Up @@ -106,6 +107,7 @@ def fields(type_defn); type_defn.all_field_definitions; end # rubocop:disable De
def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
def reachable_type?(type_name); true; end
def loadable?(type, _ctx); true; end
def loadable_possible_types(union_type, _ctx); union_type.possible_types; end
def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
def possible_types(type_defn); @schema.possible_types(type_defn, Query::NullContext.instance, false); end
def interfaces(obj_type); obj_type.interfaces; end
Expand Down Expand Up @@ -180,6 +182,10 @@ def loadable?(t, ctx) # TODO remove ctx here?
@warden.loadable?(t, ctx)
end

def loadable_possible_types(t, ctx)
@warden.loadable_possible_types(t, ctx)
end

def reachable_type?(type_name)
!!@warden.reachable_type?(type_name)
end
Expand All @@ -204,7 +210,7 @@ def initialize(context:, schema:)
@visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
@visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships =
@visible_and_reachable_type = @unions = @unfiltered_interfaces =
@reachable_type_set = @visibility_profile =
@reachable_type_set = @visibility_profile = @loadable_possible_types =
nil
@skip_warning = schema.plugins.any? { |(plugin, _opts)| plugin == GraphQL::Schema::Warden }
end
Expand All @@ -229,6 +235,13 @@ def loadable?(type, _ctx)
!reachable_type_set.include?(type) && visible_type?(type)
end

def loadable_possible_types(union_type, _ctx)
@loadable_possible_types ||= read_through do |t|
t.possible_types # unfiltered
end
@loadable_possible_types[union_type]
end

# @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
def get_type(type_name)
@visible_types ||= read_through do |name|
Expand Down
3 changes: 2 additions & 1 deletion spec/graphql/schema/union_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,8 @@ def self.resolve_type(abs_type, obj, ctx)
assert_equal "Video", res["data"]["mediaItemType"]

res = UnionLoadsSchema.execute(query_str, variables: { mediaId: "Post/Year in Review" })
assert_nil res["data"]
assert_nil res["data"]["mediaItemType"]
assert_equal ["No object found for `id: \"Post/Year in Review\"`"], res["errors"].map { |e| e["message"] }
end
end
end

0 comments on commit 93ab788

Please sign in to comment.