Skip to content

Visibility: hide unreferenced arg types #5291

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

Merged
merged 3 commits into from
Apr 7, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions lib/graphql/schema/visibility/profile.rb
Original file line number Diff line number Diff line change
@@ -47,12 +47,21 @@ def initialize(name: nil, context:, schema:)
end.compare_by_identity
}.compare_by_identity

@cached_visible_arguments = Hash.new do |h, arg|
h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
true
else
false
end
@cached_visible_arguments = Hash.new do |h, owner|
h[owner] = Hash.new do |h2, arg|
h2[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
case owner
when GraphQL::Schema::Field
@cached_visible_fields[owner.owner][owner]
when Class
@cached_visible[owner]
else
raise "Unexpected argument owner for `#{arg.path}`: #{owner.inspect}"
end
else
false
end
end.compare_by_identity
end.compare_by_identity

@cached_parent_fields = Hash.new do |h, type|
@@ -82,7 +91,7 @@ def initialize(name: nil, context:, schema:)
end.compare_by_identity

@cached_arguments = Hash.new do |h, owner|
h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments[owner])
end.compare_by_identity

@loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
@@ -180,7 +189,7 @@ def argument(owner, arg_name)
if arg.is_a?(Array)
visible_arg = nil
arg.each do |arg_defn|
if @cached_visible_arguments[arg_defn]
if @cached_visible_arguments[owner][arg_defn]
if visible_arg.nil?
visible_arg = arg_defn
else
@@ -190,7 +199,7 @@ def argument(owner, arg_name)
end
visible_arg
else
if arg && @cached_visible_arguments[arg]
if arg && @cached_visible_arguments[owner][arg]
arg
else
nil
@@ -292,7 +301,7 @@ def load_all_types
@all_types_loaded = true
visit = Visibility::Visit.new(@schema) do |member|
if member.is_a?(Module) && member.respond_to?(:kind)
if @cached_visible[member]
if @cached_visible[member] && referenced?(member)
type_name = member.graphql_name
if (prev_t = @all_types[type_name]) && !prev_t.equal?(member)
raise_duplicate_definition(member, prev_t)
@@ -312,7 +321,18 @@ def load_all_types
end

def referenced?(type_defn)
@schema.visibility.all_references[type_defn].any? { |r| r == true || @cached_visible[r] }
@schema.visibility.all_references[type_defn].any? do |ref|
case ref
when GraphQL::Schema::Argument
@cached_visible_arguments[ref.owner][ref]
when GraphQL::Schema::Field
@cached_visible_fields[ref.owner][ref]
when Module
@cached_visible[ref]
when true
true
end
end
end

def possible_types_for(type)
30 changes: 30 additions & 0 deletions spec/graphql/schema/visibility_spec.rb
Original file line number Diff line number Diff line change
@@ -16,6 +16,18 @@ def visible?(ctx)

class BaseObject < GraphQL::Schema::Object
field_class(BaseField)

def self.visible?(ctx)
(@admin_only ? !!ctx[:is_admin] : true) && super
end

def self.admin_only(new_value = nil)
if new_value.nil?
@admin_only
else
@admin_only = new_value
end
end
end

class Product < BaseObject
@@ -24,8 +36,21 @@ class Product < BaseObject
field :cost_of_goods_sold, Integer, admin_only: true
end

class Widget < BaseObject
admin_only(true)
field :name, String
end

class WidgetKind < GraphQL::Schema::Enum
value :FOO
value :BAR
end

class Query < BaseObject
field :products, [Product]
field :widget, Widget do
argument :type, WidgetKind
end

def products
[{ name: "Pool Noodle", price: 100, cost_of_goods_sold: 5 }]
@@ -55,6 +80,11 @@ def exec_query(...)
end
end

it "hides unused arguments" do
schema_sdl = VisSchema.to_definition(context: { visibility_profile: :public })
refute_includes schema_sdl, "WidgetKind"
end

describe "running queries" do
it "requires context[:visibility]" do
err = assert_raises ArgumentError do