graphql 2.3.7 → 2.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_resolver.erb +2 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/field_usage.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +3 -3
- data/lib/graphql/analysis/visitor.rb +8 -7
- data/lib/graphql/analysis.rb +4 -4
- data/lib/graphql/autoload.rb +37 -0
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +7 -6
- data/lib/graphql/dataloader/source.rb +7 -4
- data/lib/graphql/dataloader.rb +40 -19
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +13 -9
- data/lib/graphql/execution/interpreter/runtime.rb +35 -31
- data/lib/graphql/execution/interpreter.rb +6 -4
- data/lib/graphql/execution/lookahead.rb +18 -11
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +6 -11
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/document_from_schema_definition.rb +62 -34
- data/lib/graphql/language/lexer.rb +18 -15
- data/lib/graphql/language/nodes.rb +24 -16
- data/lib/graphql/language/parser.rb +14 -1
- data/lib/graphql/language/printer.rb +31 -15
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +6 -6
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +13 -6
- data/lib/graphql/query/null_context.rb +3 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +72 -18
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +2 -1
- data/lib/graphql/schema/always_visible.rb +6 -2
- data/lib/graphql/schema/argument.rb +14 -1
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive.rb +1 -1
- data/lib/graphql/schema/enum.rb +71 -23
- data/lib/graphql/schema/enum_value.rb +10 -2
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +102 -47
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +5 -2
- data/lib/graphql/schema/input_object.rb +90 -39
- data/lib/graphql/schema/interface.rb +22 -5
- data/lib/graphql/schema/introspection_system.rb +5 -16
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
- data/lib/graphql/schema/member/has_arguments.rb +25 -20
- data/lib/graphql/schema/member/has_directives.rb +3 -3
- data/lib/graphql/schema/member/has_fields.rb +26 -6
- data/lib/graphql/schema/member/has_interfaces.rb +4 -4
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -14
- data/lib/graphql/schema/subscription.rb +2 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator/required_validator.rb +28 -4
- data/lib/graphql/schema/validator.rb +3 -1
- data/lib/graphql/schema/visibility/migration.rb +187 -0
- data/lib/graphql/schema/visibility/profile.rb +353 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +294 -0
- data/lib/graphql/schema/warden.rb +166 -16
- data/lib/graphql/schema.rb +348 -94
- data/lib/graphql/static_validation/base_visitor.rb +6 -5
- data/lib/graphql/static_validation/literal_validator.rb +4 -4
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -7
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
- data/lib/graphql/static_validation/validation_context.rb +18 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions.rb +6 -4
- data/lib/graphql/testing/helpers.rb +10 -6
- data/lib/graphql/tracing/notifications_trace.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +81 -45
- metadata +31 -8
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
| @@ -0,0 +1,294 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
            require "graphql/schema/visibility/profile"
         | 
| 3 | 
            +
            require "graphql/schema/visibility/migration"
         | 
| 4 | 
            +
            require "graphql/schema/visibility/visit"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module GraphQL
         | 
| 7 | 
            +
              class Schema
         | 
| 8 | 
            +
                # Use this plugin to make some parts of your schema hidden from some viewers.
         | 
| 9 | 
            +
                #
         | 
| 10 | 
            +
                class Visibility
         | 
| 11 | 
            +
                  # @param schema [Class<GraphQL::Schema>]
         | 
| 12 | 
            +
                  # @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
         | 
| 13 | 
            +
                  # @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
         | 
| 14 | 
            +
                  # @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
         | 
| 15 | 
            +
                  def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails) ? Rails.env.production? : nil), migration_errors: false)
         | 
| 16 | 
            +
                    schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
         | 
| 17 | 
            +
                    if preload
         | 
| 18 | 
            +
                      schema.visibility.preload
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
         | 
| 23 | 
            +
                    @schema = schema
         | 
| 24 | 
            +
                    schema.use_visibility_profile = true
         | 
| 25 | 
            +
                    schema.visibility_profile_class = if migration_errors
         | 
| 26 | 
            +
                      Visibility::Migration
         | 
| 27 | 
            +
                    else
         | 
| 28 | 
            +
                      Visibility::Profile
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                    @preload = preload
         | 
| 31 | 
            +
                    @profiles = profiles
         | 
| 32 | 
            +
                    @cached_profiles = {}
         | 
| 33 | 
            +
                    @dynamic = dynamic
         | 
| 34 | 
            +
                    @migration_errors = migration_errors
         | 
| 35 | 
            +
                    # Top-level type caches:
         | 
| 36 | 
            +
                    @visit = nil
         | 
| 37 | 
            +
                    @interface_type_memberships = nil
         | 
| 38 | 
            +
                    @directives = nil
         | 
| 39 | 
            +
                    @types = nil
         | 
| 40 | 
            +
                    @all_references = nil
         | 
| 41 | 
            +
                    @loaded_all = false
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def all_directives
         | 
| 45 | 
            +
                    load_all
         | 
| 46 | 
            +
                    @directives
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def all_interface_type_memberships
         | 
| 50 | 
            +
                    load_all
         | 
| 51 | 
            +
                    @interface_type_memberships
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def all_references
         | 
| 55 | 
            +
                    load_all
         | 
| 56 | 
            +
                    @all_references
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def get_type(type_name)
         | 
| 60 | 
            +
                    load_all
         | 
| 61 | 
            +
                    @types[type_name]
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                  def preload?
         | 
| 65 | 
            +
                    @preload
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  def preload
         | 
| 69 | 
            +
                    # Traverse the schema now (and in the *_configured hooks below)
         | 
| 70 | 
            +
                    # To make sure things are loaded during boot
         | 
| 71 | 
            +
                    @preloaded_types = Set.new
         | 
| 72 | 
            +
                    types_to_visit = [
         | 
| 73 | 
            +
                      @schema.query,
         | 
| 74 | 
            +
                      @schema.mutation,
         | 
| 75 | 
            +
                      @schema.subscription,
         | 
| 76 | 
            +
                      *@schema.introspection_system.types.values,
         | 
| 77 | 
            +
                      *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
         | 
| 78 | 
            +
                      *@schema.orphan_types,
         | 
| 79 | 
            +
                    ]
         | 
| 80 | 
            +
                    # Root types may have been nil:
         | 
| 81 | 
            +
                    types_to_visit.compact!
         | 
| 82 | 
            +
                    ensure_all_loaded(types_to_visit)
         | 
| 83 | 
            +
                    @profiles.each do |profile_name, example_ctx|
         | 
| 84 | 
            +
                      example_ctx[:visibility_profile] = profile_name
         | 
| 85 | 
            +
                      prof = profile_for(example_ctx, profile_name)
         | 
| 86 | 
            +
                      prof.all_types # force loading
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  # @api private
         | 
| 91 | 
            +
                  def query_configured(query_type)
         | 
| 92 | 
            +
                    if @preload
         | 
| 93 | 
            +
                      ensure_all_loaded([query_type])
         | 
| 94 | 
            +
                    end
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  # @api private
         | 
| 98 | 
            +
                  def mutation_configured(mutation_type)
         | 
| 99 | 
            +
                    if @preload
         | 
| 100 | 
            +
                      ensure_all_loaded([mutation_type])
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  # @api private
         | 
| 105 | 
            +
                  def subscription_configured(subscription_type)
         | 
| 106 | 
            +
                    if @preload
         | 
| 107 | 
            +
                      ensure_all_loaded([subscription_type])
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  # @api private
         | 
| 112 | 
            +
                  def orphan_types_configured(orphan_types)
         | 
| 113 | 
            +
                    if @preload
         | 
| 114 | 
            +
                      ensure_all_loaded(orphan_types)
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  # @api private
         | 
| 119 | 
            +
                  def introspection_system_configured(introspection_system)
         | 
| 120 | 
            +
                    if @preload
         | 
| 121 | 
            +
                      introspection_types = [
         | 
| 122 | 
            +
                        *@schema.introspection_system.types.values,
         | 
| 123 | 
            +
                        *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
         | 
| 124 | 
            +
                      ]
         | 
| 125 | 
            +
                      ensure_all_loaded(introspection_types)
         | 
| 126 | 
            +
                    end
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  # Make another Visibility for `schema` based on this one
         | 
| 130 | 
            +
                  # @return [Visibility]
         | 
| 131 | 
            +
                  # @api private
         | 
| 132 | 
            +
                  def dup_for(other_schema)
         | 
| 133 | 
            +
                    self.class.new(
         | 
| 134 | 
            +
                      other_schema,
         | 
| 135 | 
            +
                      dynamic: @dynamic,
         | 
| 136 | 
            +
                      preload: @preload,
         | 
| 137 | 
            +
                      profiles: @profiles,
         | 
| 138 | 
            +
                      migration_errors: @migration_errors
         | 
| 139 | 
            +
                    )
         | 
| 140 | 
            +
                  end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                  def migration_errors?
         | 
| 143 | 
            +
                    @migration_errors
         | 
| 144 | 
            +
                  end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                  attr_reader :cached_profiles
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  def profile_for(context, visibility_profile)
         | 
| 149 | 
            +
                    if !@profiles.empty?
         | 
| 150 | 
            +
                      if visibility_profile.nil?
         | 
| 151 | 
            +
                        if @dynamic
         | 
| 152 | 
            +
                          if context.is_a?(Query::NullContext)
         | 
| 153 | 
            +
                            top_level_profile
         | 
| 154 | 
            +
                          else
         | 
| 155 | 
            +
                            @schema.visibility_profile_class.new(context: context, schema: @schema)
         | 
| 156 | 
            +
                          end
         | 
| 157 | 
            +
                        elsif !@profiles.empty?
         | 
| 158 | 
            +
                          raise ArgumentError, "#{@schema} expects a visibility profile, but `visibility_profile:` wasn't passed. Provide a `visibility_profile:` value or add `dynamic: true` to your visibility configuration."
         | 
| 159 | 
            +
                        end
         | 
| 160 | 
            +
                      elsif !@profiles.include?(visibility_profile)
         | 
| 161 | 
            +
                        raise ArgumentError, "`#{visibility_profile.inspect}` isn't allowed for `visibility_profile:` (must be one of #{@profiles.keys.map(&:inspect).join(", ")}). Or, add `#{visibility_profile.inspect}` to the list of profiles in the schema definition."
         | 
| 162 | 
            +
                      else
         | 
| 163 | 
            +
                        @cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: context, schema: @schema)
         | 
| 164 | 
            +
                      end
         | 
| 165 | 
            +
                    elsif context.is_a?(Query::NullContext)
         | 
| 166 | 
            +
                      top_level_profile
         | 
| 167 | 
            +
                    else
         | 
| 168 | 
            +
                      @schema.visibility_profile_class.new(context: context, schema: @schema)
         | 
| 169 | 
            +
                    end
         | 
| 170 | 
            +
                  end
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                  attr_reader :top_level
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                  # @api private
         | 
| 175 | 
            +
                  attr_reader :unfiltered_interface_type_memberships
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                  def top_level_profile(refresh: false)
         | 
| 178 | 
            +
                    if refresh
         | 
| 179 | 
            +
                      @top_level_profile = nil
         | 
| 180 | 
            +
                    end
         | 
| 181 | 
            +
                    @top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema)
         | 
| 182 | 
            +
                  end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                  private
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                  def ensure_all_loaded(types_to_visit)
         | 
| 187 | 
            +
                    while (type = types_to_visit.shift)
         | 
| 188 | 
            +
                      if type.kind.fields? && @preloaded_types.add?(type)
         | 
| 189 | 
            +
                        type.all_field_definitions.each do |field_defn|
         | 
| 190 | 
            +
                          field_defn.ensure_loaded
         | 
| 191 | 
            +
                          types_to_visit << field_defn.type.unwrap
         | 
| 192 | 
            +
                        end
         | 
| 193 | 
            +
                      end
         | 
| 194 | 
            +
                    end
         | 
| 195 | 
            +
                    top_level_profile(refresh: true)
         | 
| 196 | 
            +
                    nil
         | 
| 197 | 
            +
                  end
         | 
| 198 | 
            +
             | 
| 199 | 
            +
                  def load_all(types: nil)
         | 
| 200 | 
            +
                    if @visit.nil?
         | 
| 201 | 
            +
                      # Set up the visit system
         | 
| 202 | 
            +
                      @interface_type_memberships = Hash.new { |h, interface_type| h[interface_type] = [] }.compare_by_identity
         | 
| 203 | 
            +
                      @directives = []
         | 
| 204 | 
            +
                      @types = {} # String => Module
         | 
| 205 | 
            +
                      @all_references = Hash.new { |h, member| h[member] = Set.new.compare_by_identity }.compare_by_identity
         | 
| 206 | 
            +
                      @unions_for_references = Set.new
         | 
| 207 | 
            +
                      @visit = Visibility::Visit.new(@schema) do |member|
         | 
| 208 | 
            +
                        if member.is_a?(Module)
         | 
| 209 | 
            +
                          type_name = member.graphql_name
         | 
| 210 | 
            +
                          if (prev_t = @types[type_name])
         | 
| 211 | 
            +
                            if prev_t.is_a?(Array)
         | 
| 212 | 
            +
                              prev_t << member
         | 
| 213 | 
            +
                            else
         | 
| 214 | 
            +
                              @types[type_name] = [member, prev_t]
         | 
| 215 | 
            +
                            end
         | 
| 216 | 
            +
                          else
         | 
| 217 | 
            +
                            @types[member.graphql_name] = member
         | 
| 218 | 
            +
                          end
         | 
| 219 | 
            +
                          member.directives.each { |dir| @all_references[dir.class] << member }
         | 
| 220 | 
            +
                          if member < GraphQL::Schema::Directive
         | 
| 221 | 
            +
                            @directives << member
         | 
| 222 | 
            +
                          elsif member.respond_to?(:interface_type_memberships)
         | 
| 223 | 
            +
                            member.interface_type_memberships.each do |itm|
         | 
| 224 | 
            +
                              @all_references[itm.abstract_type] << member
         | 
| 225 | 
            +
                              @interface_type_memberships[itm.abstract_type] << itm
         | 
| 226 | 
            +
                            end
         | 
| 227 | 
            +
                          elsif member < GraphQL::Schema::Union
         | 
| 228 | 
            +
                            @unions_for_references << member
         | 
| 229 | 
            +
                          end
         | 
| 230 | 
            +
                        elsif member.is_a?(GraphQL::Schema::Argument)
         | 
| 231 | 
            +
                          member.validate_default_value
         | 
| 232 | 
            +
                          @all_references[member.type.unwrap] << member
         | 
| 233 | 
            +
                          if !(dirs = member.directives).empty?
         | 
| 234 | 
            +
                            dir_owner = member.owner
         | 
| 235 | 
            +
                            if dir_owner.respond_to?(:owner)
         | 
| 236 | 
            +
                              dir_owner = dir_owner.owner
         | 
| 237 | 
            +
                            end
         | 
| 238 | 
            +
                            dirs.each { |dir| @all_references[dir.class] << dir_owner }
         | 
| 239 | 
            +
                          end
         | 
| 240 | 
            +
                        elsif member.is_a?(GraphQL::Schema::Field)
         | 
| 241 | 
            +
                          @all_references[member.type.unwrap] << member
         | 
| 242 | 
            +
                          if !(dirs = member.directives).empty?
         | 
| 243 | 
            +
                            dir_owner = member.owner
         | 
| 244 | 
            +
                            dirs.each { |dir| @all_references[dir.class] << dir_owner }
         | 
| 245 | 
            +
                          end
         | 
| 246 | 
            +
                        elsif member.is_a?(GraphQL::Schema::EnumValue)
         | 
| 247 | 
            +
                          if !(dirs = member.directives).empty?
         | 
| 248 | 
            +
                            dir_owner = member.owner
         | 
| 249 | 
            +
                            dirs.each { |dir| @all_references[dir.class] << dir_owner }
         | 
| 250 | 
            +
                          end
         | 
| 251 | 
            +
                        end
         | 
| 252 | 
            +
                        true
         | 
| 253 | 
            +
                      end
         | 
| 254 | 
            +
             | 
| 255 | 
            +
                      @schema.root_types.each { |t| @all_references[t] << true }
         | 
| 256 | 
            +
                      @schema.introspection_system.types.each_value { |t| @all_references[t] << true }
         | 
| 257 | 
            +
                      @schema.directives.each_value { |dir_class| @all_references[dir_class] << true }
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                      @visit.visit_each(types: []) # visit default directives
         | 
| 260 | 
            +
                    end
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                    if types
         | 
| 263 | 
            +
                      @visit.visit_each(types: types, directives: [])
         | 
| 264 | 
            +
                    elsif @loaded_all == false
         | 
| 265 | 
            +
                      @loaded_all = true
         | 
| 266 | 
            +
                      @visit.visit_each
         | 
| 267 | 
            +
                    else
         | 
| 268 | 
            +
                      # already loaded all
         | 
| 269 | 
            +
                      return
         | 
| 270 | 
            +
                    end
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                    # TODO: somehow don't iterate over all these,
         | 
| 273 | 
            +
                    # only the ones that may have been modified
         | 
| 274 | 
            +
                    @interface_type_memberships.each do |int_type, type_memberships|
         | 
| 275 | 
            +
                      referers = @all_references[int_type].select { |r| r.is_a?(GraphQL::Schema::Field) }
         | 
| 276 | 
            +
                      if !referers.empty?
         | 
| 277 | 
            +
                        type_memberships.each do |type_membership|
         | 
| 278 | 
            +
                          implementor_type = type_membership.object_type
         | 
| 279 | 
            +
                          # Add new items only:
         | 
| 280 | 
            +
                          @all_references[implementor_type] |= referers
         | 
| 281 | 
            +
                        end
         | 
| 282 | 
            +
                      end
         | 
| 283 | 
            +
                    end
         | 
| 284 | 
            +
             | 
| 285 | 
            +
                    @unions_for_references.each do |union_type|
         | 
| 286 | 
            +
                      refs = @all_references[union_type]
         | 
| 287 | 
            +
                      union_type.all_possible_types.each do |object_type|
         | 
| 288 | 
            +
                        @all_references[object_type] |= refs # Add new items
         | 
| 289 | 
            +
                      end
         | 
| 290 | 
            +
                    end
         | 
| 291 | 
            +
                  end
         | 
| 292 | 
            +
                end
         | 
| 293 | 
            +
              end
         | 
| 294 | 
            +
            end
         | 
| @@ -19,6 +19,17 @@ module GraphQL | |
| 19 19 | 
             
                    PassThruWarden
         | 
| 20 20 | 
             
                  end
         | 
| 21 21 |  | 
| 22 | 
            +
                  def self.types_from_context(context)
         | 
| 23 | 
            +
                    context.types || PassThruWarden
         | 
| 24 | 
            +
                  rescue NoMethodError
         | 
| 25 | 
            +
                    # this might be a hash which won't respond to #warden
         | 
| 26 | 
            +
                    PassThruWarden
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def self.use(schema)
         | 
| 30 | 
            +
                    # no-op
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 22 33 | 
             
                  # @param visibility_method [Symbol] a Warden method to call for this entry
         | 
| 23 34 | 
             
                  # @param entry [Object, Array<Object>] One or more definitions for a given name in a GraphQL Schema
         | 
| 24 35 | 
             
                  # @param context [GraphQL::Query::Context]
         | 
| @@ -61,23 +72,32 @@ module GraphQL | |
| 61 72 | 
             
                      def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
         | 
| 62 73 | 
             
                      def arguments(owner, ctx); owner.arguments(ctx); end
         | 
| 63 74 | 
             
                      def loadable?(type, ctx); type.visible?(ctx); end
         | 
| 75 | 
            +
                      def visibility_profile
         | 
| 76 | 
            +
                        @visibility_profile ||= Warden::VisibilityProfile.new(self)
         | 
| 77 | 
            +
                      end
         | 
| 64 78 | 
             
                    end
         | 
| 65 79 | 
             
                  end
         | 
| 66 80 |  | 
| 67 81 | 
             
                  class NullWarden
         | 
| 68 82 | 
             
                    def initialize(_filter = nil, context:, schema:)
         | 
| 69 83 | 
             
                      @schema = schema
         | 
| 84 | 
            +
                      @visibility_profile = Warden::VisibilityProfile.new(self)
         | 
| 70 85 | 
             
                    end
         | 
| 71 86 |  | 
| 87 | 
            +
                    # No-op, but for compatibility:
         | 
| 88 | 
            +
                    attr_writer :skip_warning
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                    attr_reader :visibility_profile
         | 
| 91 | 
            +
             | 
| 72 92 | 
             
                    def visible_field?(field_defn, _ctx = nil, owner = nil); true; end
         | 
| 73 93 | 
             
                    def visible_argument?(arg_defn, _ctx = nil); true; end
         | 
| 74 94 | 
             
                    def visible_type?(type_defn, _ctx = nil); true; end
         | 
| 75 | 
            -
                    def visible_enum_value?(enum_value, _ctx = nil);  | 
| 95 | 
            +
                    def visible_enum_value?(enum_value, _ctx = nil); enum_value.visible?(Query::NullContext.instance); end
         | 
| 76 96 | 
             
                    def visible_type_membership?(type_membership, _ctx = nil); true; end
         | 
| 77 97 | 
             
                    def interface_type_memberships(obj_type, _ctx = nil); obj_type.interface_type_memberships; end
         | 
| 78 | 
            -
                    def get_type(type_name); @schema.get_type(type_name); end # rubocop:disable Development/ContextIsPassedCop
         | 
| 98 | 
            +
                    def get_type(type_name); @schema.get_type(type_name, Query::NullContext.instance, false); end # rubocop:disable Development/ContextIsPassedCop
         | 
| 79 99 | 
             
                    def arguments(argument_owner, ctx = nil); argument_owner.all_argument_definitions; end
         | 
| 80 | 
            -
                    def enum_values(enum_defn); enum_defn.enum_values; end # rubocop:disable Development/ContextIsPassedCop
         | 
| 100 | 
            +
                    def enum_values(enum_defn); enum_defn.enum_values(Query::NullContext.instance); end # rubocop:disable Development/ContextIsPassedCop
         | 
| 81 101 | 
             
                    def get_argument(parent_type, argument_name); parent_type.get_argument(argument_name); end # rubocop:disable Development/ContextIsPassedCop
         | 
| 82 102 | 
             
                    def types; @schema.types; end # rubocop:disable Development/ContextIsPassedCop
         | 
| 83 103 | 
             
                    def root_type_for_operation(op_name); @schema.root_type_for_operation(op_name); end
         | 
| @@ -87,10 +107,88 @@ module GraphQL | |
| 87 107 | 
             
                    def reachable_type?(type_name); true; end
         | 
| 88 108 | 
             
                    def loadable?(type, _ctx); true; end
         | 
| 89 109 | 
             
                    def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
         | 
| 90 | 
            -
                    def possible_types(type_defn); @schema.possible_types(type_defn); end
         | 
| 110 | 
            +
                    def possible_types(type_defn); @schema.possible_types(type_defn, Query::NullContext.instance, false); end
         | 
| 91 111 | 
             
                    def interfaces(obj_type); obj_type.interfaces; end
         | 
| 92 112 | 
             
                  end
         | 
| 93 113 |  | 
| 114 | 
            +
                  def visibility_profile
         | 
| 115 | 
            +
                    @visibility_profile ||= VisibilityProfile.new(self)
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  class VisibilityProfile
         | 
| 119 | 
            +
                    def initialize(warden)
         | 
| 120 | 
            +
                      @warden = warden
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                    def directives
         | 
| 124 | 
            +
                      @warden.directives
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    def directive_exists?(dir_name)
         | 
| 128 | 
            +
                      @warden.directives.any? { |d| d.graphql_name == dir_name }
         | 
| 129 | 
            +
                    end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                    def type(name)
         | 
| 132 | 
            +
                      @warden.get_type(name)
         | 
| 133 | 
            +
                    end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    def field(owner, field_name)
         | 
| 136 | 
            +
                      @warden.get_field(owner, field_name)
         | 
| 137 | 
            +
                    end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                    def argument(owner, arg_name)
         | 
| 140 | 
            +
                      @warden.get_argument(owner, arg_name)
         | 
| 141 | 
            +
                    end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                    def query_root
         | 
| 144 | 
            +
                      @warden.root_type_for_operation("query")
         | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    def mutation_root
         | 
| 148 | 
            +
                      @warden.root_type_for_operation("mutation")
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                    def subscription_root
         | 
| 152 | 
            +
                      @warden.root_type_for_operation("subscription")
         | 
| 153 | 
            +
                    end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                    def arguments(owner)
         | 
| 156 | 
            +
                      @warden.arguments(owner)
         | 
| 157 | 
            +
                    end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                    def fields(owner)
         | 
| 160 | 
            +
                      @warden.fields(owner)
         | 
| 161 | 
            +
                    end
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                    def possible_types(type)
         | 
| 164 | 
            +
                      @warden.possible_types(type)
         | 
| 165 | 
            +
                    end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    def enum_values(enum_type)
         | 
| 168 | 
            +
                      @warden.enum_values(enum_type)
         | 
| 169 | 
            +
                    end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                    def all_types
         | 
| 172 | 
            +
                      @warden.reachable_types
         | 
| 173 | 
            +
                    end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                    def interfaces(obj_type)
         | 
| 176 | 
            +
                      @warden.interfaces(obj_type)
         | 
| 177 | 
            +
                    end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                    def loadable?(t, ctx) # TODO remove ctx here?
         | 
| 180 | 
            +
                      @warden.loadable?(t, ctx)
         | 
| 181 | 
            +
                    end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                    def reachable_type?(type_name)
         | 
| 184 | 
            +
                      !!@warden.reachable_type?(type_name)
         | 
| 185 | 
            +
                    end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                    def visible_enum_value?(enum_value, ctx = nil)
         | 
| 188 | 
            +
                      @warden.visible_enum_value?(enum_value, ctx)
         | 
| 189 | 
            +
                    end
         | 
| 190 | 
            +
                  end
         | 
| 191 | 
            +
             | 
| 94 192 | 
             
                  # @param context [GraphQL::Query::Context]
         | 
| 95 193 | 
             
                  # @param schema [GraphQL::Schema]
         | 
| 96 194 | 
             
                  def initialize(context:, schema:)
         | 
| @@ -100,17 +198,19 @@ module GraphQL | |
| 100 198 | 
             
                    @mutation = @schema.mutation
         | 
| 101 199 | 
             
                    @subscription = @schema.subscription
         | 
| 102 200 | 
             
                    @context = context
         | 
| 103 | 
            -
                    @visibility_cache = read_through { |m| schema | 
| 104 | 
            -
                    @visibility_cache.compare_by_identity
         | 
| 201 | 
            +
                    @visibility_cache = read_through { |m| check_visible(schema, m) }
         | 
| 105 202 | 
             
                    # Initialize all ivars to improve object shape consistency:
         | 
| 106 203 | 
             
                    @types = @visible_types = @reachable_types = @visible_parent_fields =
         | 
| 107 204 | 
             
                      @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
         | 
| 108 205 | 
             
                      @visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships =
         | 
| 109 206 | 
             
                      @visible_and_reachable_type = @unions = @unfiltered_interfaces =
         | 
| 110 | 
            -
                      @reachable_type_set =
         | 
| 207 | 
            +
                      @reachable_type_set = @visibility_profile =
         | 
| 111 208 | 
             
                        nil
         | 
| 209 | 
            +
                    @skip_warning = schema.plugins.any? { |(plugin, _opts)| plugin == GraphQL::Schema::Warden }
         | 
| 112 210 | 
             
                  end
         | 
| 113 211 |  | 
| 212 | 
            +
                  attr_writer :skip_warning
         | 
| 213 | 
            +
             | 
| 114 214 | 
             
                  # @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
         | 
| 115 215 | 
             
                  def types
         | 
| 116 216 | 
             
                    @types ||= begin
         | 
| @@ -132,7 +232,7 @@ module GraphQL | |
| 132 232 | 
             
                  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
         | 
| 133 233 | 
             
                  def get_type(type_name)
         | 
| 134 234 | 
             
                    @visible_types ||= read_through do |name|
         | 
| 135 | 
            -
                      type_defn = @schema.get_type(name, @context)
         | 
| 235 | 
            +
                      type_defn = @schema.get_type(name, @context, false)
         | 
| 136 236 | 
             
                      if type_defn && visible_and_reachable_type?(type_defn)
         | 
| 137 237 | 
             
                        type_defn
         | 
| 138 238 | 
             
                      else
         | 
| @@ -179,7 +279,7 @@ module GraphQL | |
| 179 279 | 
             
                  # @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
         | 
| 180 280 | 
             
                  def possible_types(type_defn)
         | 
| 181 281 | 
             
                    @visible_possible_types ||= read_through { |type_defn|
         | 
| 182 | 
            -
                      pt = @schema.possible_types(type_defn, @context)
         | 
| 282 | 
            +
                      pt = @schema.possible_types(type_defn, @context, false)
         | 
| 183 283 | 
             
                      pt.select { |t| visible_and_reachable_type?(t) }
         | 
| 184 284 | 
             
                    }
         | 
| 185 285 | 
             
                    @visible_possible_types[type_defn]
         | 
| @@ -197,7 +297,7 @@ module GraphQL | |
| 197 297 | 
             
                  def arguments(argument_owner, ctx = nil)
         | 
| 198 298 | 
             
                    @visible_arguments ||= read_through { |o|
         | 
| 199 299 | 
             
                      args = o.arguments(@context)
         | 
| 200 | 
            -
                      if args. | 
| 300 | 
            +
                      if !args.empty?
         | 
| 201 301 | 
             
                        args = args.values
         | 
| 202 302 | 
             
                        args.select! { |a| visible_argument?(a, @context) }
         | 
| 203 303 | 
             
                        args
         | 
| @@ -229,7 +329,7 @@ module GraphQL | |
| 229 329 | 
             
                  def interfaces(obj_type)
         | 
| 230 330 | 
             
                    @visible_interfaces ||= read_through { |t|
         | 
| 231 331 | 
             
                      ints = t.interfaces(@context)
         | 
| 232 | 
            -
                      if ints. | 
| 332 | 
            +
                      if !ints.empty?
         | 
| 233 333 | 
             
                        ints.select! { |i| visible_type?(i) }
         | 
| 234 334 | 
             
                      end
         | 
| 235 335 | 
             
                      ints
         | 
| @@ -289,9 +389,9 @@ module GraphQL | |
| 289 389 | 
             
                      next true if root_type?(type_defn) || type_defn.introspection?
         | 
| 290 390 |  | 
| 291 391 | 
             
                      if type_defn.kind.union?
         | 
| 292 | 
            -
                        possible_types(type_defn). | 
| 392 | 
            +
                        !possible_types(type_defn).empty? && (referenced?(type_defn) || orphan_type?(type_defn))
         | 
| 293 393 | 
             
                      elsif type_defn.kind.interface?
         | 
| 294 | 
            -
                        if possible_types(type_defn). | 
| 394 | 
            +
                        if !possible_types(type_defn).empty?
         | 
| 295 395 | 
             
                          true
         | 
| 296 396 | 
             
                        else
         | 
| 297 397 | 
             
                          if @context.respond_to?(:logger) && (logger = @context.logger)
         | 
| @@ -376,11 +476,61 @@ module GraphQL | |
| 376 476 | 
             
                  end
         | 
| 377 477 |  | 
| 378 478 | 
             
                  def read_through
         | 
| 379 | 
            -
                     | 
| 380 | 
            -
             | 
| 381 | 
            -
             | 
| 479 | 
            +
                    Hash.new { |h, k| h[k] = yield(k) }.compare_by_identity
         | 
| 480 | 
            +
                  end
         | 
| 481 | 
            +
             | 
| 482 | 
            +
                  def check_visible(schema, member)
         | 
| 483 | 
            +
                    if schema.visible?(member, @context)
         | 
| 484 | 
            +
                      true
         | 
| 485 | 
            +
                    elsif @skip_warning
         | 
| 486 | 
            +
                      false
         | 
| 487 | 
            +
                    else
         | 
| 488 | 
            +
                      member_s = member.respond_to?(:path) ? member.path : member.inspect
         | 
| 489 | 
            +
                      member_type = case member
         | 
| 490 | 
            +
                      when Module
         | 
| 491 | 
            +
                        if member.respond_to?(:kind)
         | 
| 492 | 
            +
                          member.kind.name.downcase
         | 
| 493 | 
            +
                        else
         | 
| 494 | 
            +
                          ""
         | 
| 495 | 
            +
                        end
         | 
| 496 | 
            +
                      when GraphQL::Schema::Field
         | 
| 497 | 
            +
                        "field"
         | 
| 498 | 
            +
                      when GraphQL::Schema::EnumValue
         | 
| 499 | 
            +
                        "enum value"
         | 
| 500 | 
            +
                      when GraphQL::Schema::Argument
         | 
| 501 | 
            +
                        "argument"
         | 
| 502 | 
            +
                      else
         | 
| 503 | 
            +
                        ""
         | 
| 504 | 
            +
                      end
         | 
| 505 | 
            +
             | 
| 506 | 
            +
                      schema_s = schema.name ? "#{schema.name}'s" : ""
         | 
| 507 | 
            +
                      schema_name = schema.name ? "#{schema.name}" : "your schema"
         | 
| 508 | 
            +
                      warn(ADD_WARDEN_WARNING % { schema_s: schema_s, schema_name: schema_name, member: member_s, member_type: member_type })
         | 
| 509 | 
            +
                      @skip_warning = true # only warn once per query
         | 
| 510 | 
            +
                      # If there's no schema name, add the backtrace for additional context:
         | 
| 511 | 
            +
                      if schema_s == ""
         | 
| 512 | 
            +
                        puts caller.map { |l| "    #{l}"}
         | 
| 513 | 
            +
                      end
         | 
| 514 | 
            +
                      false
         | 
| 515 | 
            +
                    end
         | 
| 382 516 | 
             
                  end
         | 
| 383 517 |  | 
| 518 | 
            +
                  ADD_WARDEN_WARNING = <<~WARNING
         | 
| 519 | 
            +
            DEPRECATION: %{schema_s} "%{member}" %{member_type} returned `false` for `.visible?` but `GraphQL::Schema::Visibility` isn't configured yet.
         | 
| 520 | 
            +
             | 
| 521 | 
            +
              Address this warning by adding:
         | 
| 522 | 
            +
             | 
| 523 | 
            +
                  use GraphQL::Schema::Visibility
         | 
| 524 | 
            +
             | 
| 525 | 
            +
              to the definition for %{schema_name}. (Future GraphQL-Ruby versions won't check `.visible?` methods by default.)
         | 
| 526 | 
            +
             | 
| 527 | 
            +
              Alternatively, for legacy behavior, add:
         | 
| 528 | 
            +
             | 
| 529 | 
            +
                  use GraphQL::Schema::Warden # legacy visibility behavior
         | 
| 530 | 
            +
             | 
| 531 | 
            +
              For more information see: https://graphql-ruby.org/authorization/visibility.html
         | 
| 532 | 
            +
                  WARNING
         | 
| 533 | 
            +
             | 
| 384 534 | 
             
                  def reachable_type_set
         | 
| 385 535 | 
             
                    return @reachable_type_set if @reachable_type_set
         | 
| 386 536 |  |