class GraphQL::Schema::Visibility::Profile
This class filters the types, fields, arguments, enum values, and directives in a schema based on the given context.
It’s like Warden, but has some differences:
-
It doesn’t use Schema‘s top-level caches (eg Schema.references_to, Schema.possible_types, Schema.types)
-
It doesn’t hide
InterfaceorUniontypes when all their possible types are hidden. (Instead, those types should implement.visible?to hide in that case.) -
It checks
.visible?on root introspection types -
It can be used to cache profiles by name for re-use across queries
Attributes
Returns
-
Symbol, nil
Public Class Methods
# File lib/graphql/schema/visibility/profile.rb, line 23 def self.from_context(ctx, schema) if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self) types else schema.visibility.profile_for(ctx) end end
Returns
# File lib/graphql/schema/visibility/profile.rb, line 84 def initialize(name: nil, context:, schema:, visibility:) @name = name @context = context @schema = schema @visibility = visibility @all_types = {} @all_types_loaded = false @unvisited_types = [] @all_directives = nil @cached_visible = Hash.new { |h, member| h[member] = @schema.visible?(member, @context) }.compare_by_identity @cached_visible_fields = Hash.new { |h, owner| h[owner] = Hash.new do |h2, field| h2[field] = visible_field_for(owner, field) end.compare_by_identity }.compare_by_identity @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| h[type] = Hash.new do |h2, field_name| h2[field_name] = type.get_field(field_name, @context) end end.compare_by_identity @cached_parent_arguments = Hash.new do |h, arg_owner| h[arg_owner] = Hash.new do |h2, arg_name| h2[arg_name] = arg_owner.get_argument(arg_name, @context) end end.compare_by_identity @cached_possible_types = Hash.new { |h, type| h[type] = possible_types_for(type) }.compare_by_identity @cached_enum_values = Hash.new do |h, enum_t| values = non_duplicate_items(enum_t.enum_values(@context), @cached_visible) if values.size == 0 raise GraphQL::Schema::Enum::MissingValuesError.new(enum_t) end h[enum_t] = values end.compare_by_identity @cached_fields = Hash.new do |h, owner| h[owner] = non_duplicate_items(owner.all_field_definitions.each(&:ensure_loaded), @cached_visible_fields[owner]) end.compare_by_identity @cached_arguments = Hash.new do |h, owner| 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 # Combined cache for field(owner, field_name) — avoids repeated kind check + parent lookup + visibility check @cached_field_result = Hash.new { |h, owner| h[owner] = Hash.new { |h2, field_name| h2[field_name] = compute_field(owner, field_name) } }.compare_by_identity # Cache for type(type_name) — avoids repeated get_type + visibility + referenced? checks @cached_type_result = Hash.new { |h, type_name| h[type_name] = compute_type(type_name) } end
Public Instance Methods
Source
# File lib/graphql/schema/visibility/profile.rb, line 246 def all_types load_all_types @all_types.values end
Source
# File lib/graphql/schema/visibility/profile.rb, line 251 def all_types_h load_all_types @all_types end
Source
# File lib/graphql/schema/visibility/profile.rb, line 199 def argument(owner, arg_name) arg = @cached_parent_arguments[owner][arg_name] if arg.is_a?(Array) visible_arg = nil #: untyped arg.each do |arg_defn| if @cached_visible_arguments[owner][arg_defn] if visible_arg.nil? visible_arg = arg_defn else raise_duplicate_definition(visible_arg, arg_defn) end end end visible_arg else if arg && @cached_visible_arguments[owner][arg] arg else nil end end end
Source
# File lib/graphql/schema/visibility/profile.rb, line 195 def arguments(owner) @cached_arguments[owner] end
Source
# File lib/graphql/schema/visibility/profile.rb, line 260 def directive_exists?(dir_name) directives.any? { |d| d.graphql_name == dir_name } end
Source
# File lib/graphql/schema/visibility/profile.rb, line 264 def directives @all_directives ||= @visibility.all_directives.select { |dir| @cached_visible[dir] && @visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) } } end
Source
# File lib/graphql/schema/visibility/profile.rb, line 256 def enum_values(owner) @cached_enum_values[owner] end
Source
# File lib/graphql/schema/visibility/profile.rb, line 187 def field(owner, field_name) @cached_field_result[owner][field_name] end
# File lib/graphql/schema/visibility/profile.rb, line 159 def field_on_visible_interface?(field, owner) ints = owner.interface_type_memberships.map(&:abstract_type) field_name = field.graphql_name filtered_ints = interfaces(owner) any_interface_has_field = false #: bool any_interface_has_visible_field = false #: bool ints.each do |int_t| if (_int_f_defn = @cached_parent_fields[int_t][field_name]) any_interface_has_field = true if filtered_ints.include?(int_t) # TODO cycles, or maybe not necessary since previously checked? && @cached_visible_fields[owner][field] any_interface_has_visible_field = true break end end end if any_interface_has_field any_interface_has_visible_field else true end end
Source
# File lib/graphql/schema/visibility/profile.rb, line 191 def fields(owner) @cached_fields[owner] end
Source
# File lib/graphql/schema/visibility/profile.rb, line 36 def freeze @cached_visible.default_proc = nil @cached_visible.freeze @cached_visible_fields.default_proc = nil @cached_visible_fields.each do |type, fields| fields.default_proc = nil fields.freeze end @cached_visible_fields.freeze @cached_visible_arguments.default_proc = nil @cached_visible_arguments.each do |type, fields| fields.default_proc = nil fields.freeze end @cached_visible_arguments.freeze @cached_parent_fields.default_proc = nil @cached_parent_fields.each do |type, fields| fields.default_proc = nil fields.freeze end @cached_parent_fields.freeze @cached_parent_arguments.default_proc = nil @cached_parent_arguments.each do |type, args| args.default_proc = nil args.freeze end @cached_parent_arguments.freeze @cached_possible_types.default_proc = nil @cached_possible_types.freeze @cached_enum_values.default_proc = nil @cached_enum_values.freeze @cached_fields.default_proc = nil @cached_fields.freeze @cached_arguments.default_proc = nil @cached_arguments.freeze @loadable_possible_types.default_proc = nil @loadable_possible_types.freeze @cached_field_result.default_proc = nil @cached_field_result.each do |_, h| h.default_proc = nil h.freeze end @cached_field_result.freeze @cached_type_result.default_proc = nil @cached_type_result.freeze super end
Calls superclass method
Source
# File lib/graphql/schema/visibility/profile.rb, line 226 def interfaces(obj_or_int_type) ints = obj_or_int_type.interface_type_memberships .select { |itm| @cached_visible[itm] && @cached_visible[itm.abstract_type] } .map!(&:abstract_type) ints.uniq! # Remove any duplicate interfaces implemented via other interfaces ints end
Source
# File lib/graphql/schema/visibility/profile.rb, line 270 def loadable?(t, _ctx) @cached_visible[t] && !referenced?(t) end
# File lib/graphql/schema/visibility/profile.rb, line 274 def loadable_possible_types(t, _ctx) @loadable_possible_types[t] end
Source
# File lib/graphql/schema/visibility/profile.rb, line 278 def loaded_types @all_types.values end
Source
# File lib/graphql/schema/visibility/profile.rb, line 238 def mutation_root ((t = @schema.mutation) && @cached_visible[t]) ? t : nil end
Source
# File lib/graphql/schema/visibility/profile.rb, line 222 def possible_types(type) @cached_possible_types[type] end
Source
# File lib/graphql/schema/visibility/profile.rb, line 291 def preload load_all_types @all_types.each do |type_name, type_defn| type(type_name) if type_defn.kind.fields? fields(type_defn).each do |f| field(type_defn, f.graphql_name) arguments(f).each do |arg| argument(f, arg.graphql_name) end resolver_class = f.resolver if resolver_class arguments(resolver_class).each do |arg| argument(resolver_class, arg.graphql_name) end end end @schema.introspection_system.dynamic_fields.each do |f| field(type_defn, f.graphql_name) end interfaces(type_defn) elsif type_defn.kind.input_object? arguments(type_defn).each do |arg| argument(type_defn, arg.graphql_name) end elsif type_defn.kind.enum? enum_values(type_defn) elsif type_defn.kind.union? loadable_possible_types(type_defn, @context) @schema.introspection_system.dynamic_fields.each do |f| field(type_defn, f.graphql_name) end end possible_types(type_defn) end if @schema.query @schema.introspection_system.entry_points.each do |f| arguments(f).each do |arg| argument(f, arg.graphql_name) end field(@schema.query, f.graphql_name) end end @schema.introspection_system.dynamic_fields.each do |f| arguments(f).each do |arg| argument(f, arg.graphql_name) end end directives.each do |directive| arguments(directive).each do |directive_argument| argument(directive, directive_argument.graphql_name) end end @cached_arguments.each do |owner, args| @cached_parent_arguments[owner] args.each do |arg| if (loads_type = arg.loads) loadable?(loads_type, @context) possible_types(loads_type) if loads_type.kind.union? loadable_possible_types(loads_type, @context) end end end end end
Source
# File lib/graphql/schema/visibility/profile.rb, line 234 def query_root ((t = @schema.query) && @cached_visible[t]) ? t : nil end
Source
# File lib/graphql/schema/visibility/profile.rb, line 282 def reachable_type?(type_name) load_all_types !!@all_types[type_name] end
Source
# File lib/graphql/schema/visibility/profile.rb, line 242 def subscription_root ((t = @schema.subscription) && @cached_visible[t]) ? t : nil end
Source
# File lib/graphql/schema/visibility/profile.rb, line 183 def type(type_name) @cached_type_result[type_name] end
# File lib/graphql/schema/visibility/profile.rb, line 287 def visible_enum_value?(enum_value, _ctx = nil) @cached_visible[enum_value] end