graphql 2.3.17 → 2.3.19

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 194930c79609165d98c3ee58c55d557ce5666860dccd4b4718666bf9080178f6
4
- data.tar.gz: c3bcc14c06fbcd90dcef7e046f96240775f903e479a8d76439cea01cfefe8373
3
+ metadata.gz: 10c07ea3257376b80dc347e8735c085cb1708ab2e10bef481030a9cafb395617
4
+ data.tar.gz: fa2c5a429ec7f5fcef99dad3784b118b4c59144d851fa8158a5c25fc52846404
5
5
  SHA512:
6
- metadata.gz: 1a6e461373942ea8ac603a0b7fb6cd6f2153280f9248d9bd0e6f5fae5c1c5e6e9dbf862114a7a518173ce46e5101a3a4e9ea5239d8f3dfcbd60752a6c086ddad
7
- data.tar.gz: 37e9dcc03c6fd735b6986efa3ea113702f53528bde840341b3d998bbd8a5a35eba11b4e38fded1bedaa8f84dca298e76fbf0d133299fbb4886dbdae36d0a8fe9
6
+ metadata.gz: 9fe9de7dafd4e61471821f13dd39300d70e69d8e64a264bb22e39482ea9d2eedea81470ee9bcb8d59cf2ba840ac8173547b9ce1bf01c45cc57446f92921c22de
7
+ data.tar.gz: 5a4343fb066b3b56129c18f9da25575db4d8eb58b569185a218bcfb069b20ecd51711988823fb25210b5239d0cb1dc03e0dc7c507ae90d6bd00301679d873f17
@@ -12,6 +12,7 @@ module GraphQL
12
12
  end
13
13
 
14
14
  def run
15
+ jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
15
16
  job_fibers = []
16
17
  next_job_fibers = []
17
18
  source_tasks = []
@@ -23,7 +24,7 @@ module GraphQL
23
24
  first_pass = false
24
25
  fiber_vars = get_fiber_variables
25
26
 
26
- while (f = (job_fibers.shift || spawn_job_fiber))
27
+ while (f = (job_fibers.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size) < jobs_fiber_limit) && spawn_job_fiber)))
27
28
  if f.alive?
28
29
  finished = run_fiber(f)
29
30
  if !finished
@@ -37,7 +38,7 @@ module GraphQL
37
38
  Sync do |root_task|
38
39
  set_fiber_variables(fiber_vars)
39
40
  while source_tasks.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
40
- while (task = source_tasks.shift || spawn_source_task(root_task, sources_condition))
41
+ while (task = (source_tasks.shift || (((job_fibers.size + next_job_fibers.size + source_tasks.size + next_source_tasks.size) < total_fiber_limit) && spawn_source_task(root_task, sources_condition))))
41
42
  if task.alive?
42
43
  root_task.yield # give the source task a chance to run
43
44
  next_source_tasks << task
@@ -98,7 +98,7 @@ module GraphQL
98
98
  while pending_result_keys.any? { |key| !@results.key?(key) }
99
99
  iterations += 1
100
100
  if iterations > MAX_ITERATIONS
101
- raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency."
101
+ raise "#{self.class}#sync tried #{MAX_ITERATIONS} times to load pending keys (#{pending_result_keys}), but they still weren't loaded. There is likely a circular dependency#{@dataloader.fiber_limit ? " or `fiber_limit: #{@dataloader.fiber_limit}` is set too low" : ""}."
102
102
  end
103
103
  @dataloader.yield
104
104
  end
@@ -24,18 +24,23 @@ module GraphQL
24
24
  #
25
25
  class Dataloader
26
26
  class << self
27
- attr_accessor :default_nonblocking
27
+ attr_accessor :default_nonblocking, :default_fiber_limit
28
28
  end
29
29
 
30
- NonblockingDataloader = Class.new(self) { self.default_nonblocking = true }
31
-
32
- def self.use(schema, nonblocking: nil)
33
- schema.dataloader_class = if nonblocking
30
+ def self.use(schema, nonblocking: nil, fiber_limit: nil)
31
+ dataloader_class = if nonblocking
34
32
  warn("`nonblocking: true` is deprecated from `GraphQL::Dataloader`, please use `GraphQL::Dataloader::AsyncDataloader` instead. Docs: https://graphql-ruby.org/dataloader/async_dataloader.")
35
- NonblockingDataloader
33
+ Class.new(self) { self.default_nonblocking = true }
36
34
  else
37
35
  self
38
36
  end
37
+
38
+ if fiber_limit
39
+ dataloader_class = Class.new(dataloader_class)
40
+ dataloader_class.default_fiber_limit = fiber_limit
41
+ end
42
+
43
+ schema.dataloader_class = dataloader_class
39
44
  end
40
45
 
41
46
  # Call the block with a Dataloader instance,
@@ -50,14 +55,18 @@ module GraphQL
50
55
  result
51
56
  end
52
57
 
53
- def initialize(nonblocking: self.class.default_nonblocking)
58
+ def initialize(nonblocking: self.class.default_nonblocking, fiber_limit: self.class.default_fiber_limit)
54
59
  @source_cache = Hash.new { |h, k| h[k] = {} }
55
60
  @pending_jobs = []
56
61
  if !nonblocking.nil?
57
62
  @nonblocking = nonblocking
58
63
  end
64
+ @fiber_limit = fiber_limit
59
65
  end
60
66
 
67
+ # @return [Integer, nil]
68
+ attr_reader :fiber_limit
69
+
61
70
  def nonblocking?
62
71
  @nonblocking
63
72
  end
@@ -178,6 +187,7 @@ module GraphQL
178
187
  end
179
188
 
180
189
  def run
190
+ jobs_fiber_limit, total_fiber_limit = calculate_fiber_limit
181
191
  job_fibers = []
182
192
  next_job_fibers = []
183
193
  source_fibers = []
@@ -187,7 +197,7 @@ module GraphQL
187
197
  while first_pass || job_fibers.any?
188
198
  first_pass = false
189
199
 
190
- while (f = (job_fibers.shift || spawn_job_fiber))
200
+ while (f = (job_fibers.shift || (((next_job_fibers.size + job_fibers.size) < jobs_fiber_limit) && spawn_job_fiber)))
191
201
  if f.alive?
192
202
  finished = run_fiber(f)
193
203
  if !finished
@@ -197,8 +207,8 @@ module GraphQL
197
207
  end
198
208
  join_queues(job_fibers, next_job_fibers)
199
209
 
200
- while source_fibers.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
201
- while (f = source_fibers.shift || spawn_source_fiber)
210
+ while (source_fibers.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) })
211
+ while (f = source_fibers.shift || (((job_fibers.size + source_fibers.size + next_source_fibers.size + next_job_fibers.size) < total_fiber_limit) && spawn_source_fiber))
202
212
  if f.alive?
203
213
  finished = run_fiber(f)
204
214
  if !finished
@@ -242,6 +252,17 @@ module GraphQL
242
252
 
243
253
  private
244
254
 
255
+ def calculate_fiber_limit
256
+ total_fiber_limit = @fiber_limit || Float::INFINITY
257
+ if total_fiber_limit < 4
258
+ raise ArgumentError, "Dataloader fiber limit is too low (#{total_fiber_limit}), it must be at least 4"
259
+ end
260
+ total_fiber_limit -= 1 # deduct one fiber for `manager`
261
+ # Deduct at least one fiber for sources
262
+ jobs_fiber_limit = total_fiber_limit - 2
263
+ return jobs_fiber_limit, total_fiber_limit
264
+ end
265
+
245
266
  def join_queues(prev_queue, new_queue)
246
267
  @nonblocking && Fiber.scheduler.run
247
268
  prev_queue.concat(new_queue)
@@ -28,7 +28,7 @@ module GraphQL
28
28
  end
29
29
 
30
30
  def types
31
- @types ||= GraphQL::Schema::Warden::SchemaSubset.new(@warden)
31
+ @types ||= Schema::Warden::VisibilityProfile.new(@warden)
32
32
  end
33
33
  end
34
34
  end
data/lib/graphql/query.rb CHANGED
@@ -95,21 +95,24 @@ module GraphQL
95
95
  # @param root_value [Object] the object used to resolve fields on the root type
96
96
  # @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
97
97
  # @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
98
- def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_schema_subset: nil)
98
+ # @param visibility_profile [Symbol]
99
+ def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
99
100
  # Even if `variables: nil` is passed, use an empty hash for simpler logic
100
101
  variables ||= {}
101
102
  @schema = schema
102
103
  @context = schema.context_class.new(query: self, values: context)
103
104
 
104
- if use_schema_subset.nil?
105
- use_schema_subset = warden ? false : schema.use_schema_visibility?
105
+ if use_visibility_profile.nil?
106
+ use_visibility_profile = warden ? false : schema.use_visibility_profile?
106
107
  end
107
108
 
108
- if use_schema_subset
109
- @schema_subset = @schema.subset_class.new(context: @context, schema: @schema)
109
+ @visibility_profile = visibility_profile
110
+
111
+ if use_visibility_profile
112
+ @visibility_profile = @schema.visibility.profile_for(@context, visibility_profile)
110
113
  @warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
111
114
  else
112
- @schema_subset = nil
115
+ @visibility_profile = nil
113
116
  @warden = warden
114
117
  end
115
118
 
@@ -187,6 +190,9 @@ module GraphQL
187
190
  @query_string ||= (document ? document.to_query_string : nil)
188
191
  end
189
192
 
193
+ # @return [Symbol, nil]
194
+ attr_reader :visibility_profile
195
+
190
196
  attr_accessor :multiplex
191
197
 
192
198
  # @return [GraphQL::Tracing::Trace]
@@ -203,15 +209,19 @@ module GraphQL
203
209
  def lookahead
204
210
  @lookahead ||= begin
205
211
  ast_node = selected_operation
206
- root_type = case ast_node.operation_type
207
- when nil, "query"
208
- types.query_root # rubocop:disable Development/ContextIsPassedCop
209
- when "mutation"
210
- types.mutation_root # rubocop:disable Development/ContextIsPassedCop
211
- when "subscription"
212
- types.subscription_root # rubocop:disable Development/ContextIsPassedCop
212
+ if ast_node.nil?
213
+ GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
214
+ else
215
+ root_type = case ast_node.operation_type
216
+ when nil, "query"
217
+ types.query_root # rubocop:disable Development/ContextIsPassedCop
218
+ when "mutation"
219
+ types.mutation_root # rubocop:disable Development/ContextIsPassedCop
220
+ when "subscription"
221
+ types.subscription_root # rubocop:disable Development/ContextIsPassedCop
222
+ end
223
+ GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
213
224
  end
214
- GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
215
225
  end
216
226
  end
217
227
 
@@ -343,10 +353,33 @@ module GraphQL
343
353
  with_prepared_ast { @warden }
344
354
  end
345
355
 
346
- def_delegators :warden, :get_type, :get_field, :possible_types, :root_type_for_operation
356
+ def get_type(type_name)
357
+ types.type(type_name) # rubocop:disable Development/ContextIsPassedCop
358
+ end
359
+
360
+ def get_field(owner, field_name)
361
+ types.field(owner, field_name) # rubocop:disable Development/ContextIsPassedCop
362
+ end
363
+
364
+ def possible_types(type)
365
+ types.possible_types(type) # rubocop:disable Development/ContextIsPassedCop
366
+ end
367
+
368
+ def root_type_for_operation(op_type)
369
+ case op_type
370
+ when "query"
371
+ types.query_root # rubocop:disable Development/ContextIsPassedCop
372
+ when "mutation"
373
+ types.mutation_root # rubocop:disable Development/ContextIsPassedCop
374
+ when "subscription"
375
+ types.subscription_root # rubocop:disable Development/ContextIsPassedCop
376
+ else
377
+ raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected 'query', 'mutation', or 'subscription'"
378
+ end
379
+ end
347
380
 
348
381
  def types
349
- @schema_subset || warden.schema_subset
382
+ @visibility_profile || warden.visibility_profile
350
383
  end
351
384
 
352
385
  # @param abstract_type [GraphQL::UnionType, GraphQL::InterfaceType]
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
3
  class Schema
4
- class AlwaysVisible
4
+ module AlwaysVisible
5
5
  def self.use(schema, **opts)
6
- schema.warden_class = GraphQL::Schema::Warden::NullWarden
7
- schema.subset_class = GraphQL::Schema::Warden::NullWarden::NullSubset
6
+ schema.extend(self)
7
+ end
8
+
9
+ def visible?(_member, _context)
10
+ true
8
11
  end
9
12
  end
10
13
  end
@@ -364,6 +364,7 @@ module GraphQL
364
364
 
365
365
  # @api private
366
366
  def validate_default_value
367
+ return unless default_value?
367
368
  coerced_default_value = begin
368
369
  # This is weird, but we should accept single-item default values for list-type arguments.
369
370
  # If we used `coerce_isolated_input` below, it would do this for us, but it's not really
@@ -188,6 +188,7 @@ module GraphQL
188
188
 
189
189
  def self.inherited(child_class)
190
190
  child_class.definition_default_resolve = self.definition_default_resolve
191
+ super
191
192
  end
192
193
  end
193
194
 
@@ -135,7 +135,7 @@ module GraphQL
135
135
 
136
136
  def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
137
137
  warden = Warden.from_context(context)
138
- skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Subset)
138
+ skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
139
139
  for ancestor in ancestors
140
140
  if ancestor.respond_to?(:own_arguments) &&
141
141
  (a = ancestor.own_arguments[argument_name]) &&
@@ -210,7 +210,7 @@ module GraphQL
210
210
  # @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
211
211
  def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
212
212
  warden = Warden.from_context(context)
213
- if (arg_config = own_arguments[argument_name]) && ((context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Subset)) || (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden)))
213
+ if (arg_config = own_arguments[argument_name]) && ((context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)) || (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden)))
214
214
  visible_arg || arg_config
215
215
  elsif defined?(@resolver_class) && @resolver_class
216
216
  @resolver_class.get_field_argument(argument_name, context)
@@ -99,7 +99,7 @@ module GraphQL
99
99
  module InterfaceMethods
100
100
  def get_field(field_name, context = GraphQL::Query::NullContext.instance)
101
101
  warden = Warden.from_context(context)
102
- skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Subset)
102
+ skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
103
103
  for ancestor in ancestors
104
104
  if ancestor.respond_to?(:own_fields) &&
105
105
  (f_entry = ancestor.own_fields[field_name]) &&
@@ -135,7 +135,7 @@ module GraphQL
135
135
  # Objects need to check that the interface implementation is visible, too
136
136
  warden = Warden.from_context(context)
137
137
  ancs = ancestors
138
- skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Subset)
138
+ skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
139
139
  i = 0
140
140
  while (ancestor = ancs[i])
141
141
  if ancestor.respond_to?(:own_fields) &&
@@ -35,9 +35,10 @@ module GraphQL
35
35
  # end
36
36
  #
37
37
  class RequiredValidator < Validator
38
- # @param one_of [Symbol, Array<Symbol>] An argument, or a list of arguments, that represents a valid set of inputs for this field
38
+ # @param one_of [Array<Symbol>] A list of arguments, exactly one of which is required for this field
39
+ # @param argument [Symbol] An argument that is required for this field
39
40
  # @param message [String]
40
- def initialize(one_of: nil, argument: nil, message: "%{validated} has the wrong arguments", **default_options)
41
+ def initialize(one_of: nil, argument: nil, message: nil, **default_options)
41
42
  @one_of = if one_of
42
43
  one_of
43
44
  elsif argument
@@ -49,7 +50,7 @@ module GraphQL
49
50
  super(**default_options)
50
51
  end
51
52
 
52
- def validate(_object, _context, value)
53
+ def validate(_object, context, value)
53
54
  matched_conditions = 0
54
55
 
55
56
  if !value.nil?
@@ -73,9 +74,32 @@ module GraphQL
73
74
  if matched_conditions == 1
74
75
  nil # OK
75
76
  else
76
- @message
77
+ @message || build_message(context)
77
78
  end
78
79
  end
80
+
81
+ def build_message(context)
82
+ argument_definitions = @validated.arguments(context).values
83
+ required_names = @one_of.map do |arg_keyword|
84
+ if arg_keyword.is_a?(Array)
85
+ names = arg_keyword.map { |arg| arg_keyword_to_grapqhl_name(argument_definitions, arg) }
86
+ "(" + names.join(" and ") + ")"
87
+ else
88
+ arg_keyword_to_grapqhl_name(argument_definitions, arg_keyword)
89
+ end
90
+ end
91
+
92
+ if required_names.size == 1
93
+ "%{validated} must include the following argument: #{required_names.first}."
94
+ else
95
+ "%{validated} must include exactly one of the following arguments: #{required_names.join(", ")}."
96
+ end
97
+ end
98
+
99
+ def arg_keyword_to_grapqhl_name(argument_definitions, arg_keyword)
100
+ argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword }
101
+ argument_definition.graphql_name
102
+ end
79
103
  end
80
104
  end
81
105
  end
@@ -2,7 +2,7 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class Visibility
5
- # You can use this to see how {GraphQL::Schema::Warden} and {GraphQL::Schema::Visibility::Subset}
5
+ # You can use this to see how {GraphQL::Schema::Warden} and {GraphQL::Schema::Visibility::Profile}
6
6
  # handle `.visible?` differently in your schema.
7
7
  #
8
8
  # It runs the same method on both implementations and raises an error when the results diverge.
@@ -15,28 +15,24 @@ module GraphQL
15
15
  # This plugin adds two keys to `context` when running:
16
16
  #
17
17
  # - `visibility_migration_running: true`
18
- # - For the {Warden} which it instantiates, it adds `visibility_migration_warden_running: true`.
18
+ # - For the {Schema::Warden} which it instantiates, it adds `visibility_migration_warden_running: true`.
19
19
  #
20
20
  # Use those keys to modify your `visible?` behavior as needed.
21
21
  #
22
22
  # Also, in a pinch, you can set `skip_visibility_migration_error: true` in context to turn off this behavior per-query.
23
- # (In that case, it uses {Subset} directly.)
23
+ # (In that case, it uses {Profile} directly.)
24
24
  #
25
25
  # @example Adding this plugin
26
26
  #
27
- # use GraphQL::Schema::Visibility::Migration
27
+ # use GraphQL::Schema::Visibility, migration_errors: true
28
28
  #
29
- class Migration < GraphQL::Schema::Visibility::Subset
30
- def self.use(schema)
31
- schema.subset_class = self
32
- end
33
-
29
+ class Migration < GraphQL::Schema::Visibility::Profile
34
30
  class RuntimeTypesMismatchError < GraphQL::Error
35
- def initialize(method_called, warden_result, subset_result, method_args)
31
+ def initialize(method_called, warden_result, profile_result, method_args)
36
32
  super(<<~ERR)
37
33
  Mismatch in types for `##{method_called}(#{method_args.map(&:inspect).join(", ")})`:
38
34
 
39
- #{compare_results(warden_result, subset_result)}
35
+ #{compare_results(warden_result, profile_result)}
40
36
 
41
37
  Update your `.visible?` implementation to make these implementations return the same value.
42
38
 
@@ -45,9 +41,9 @@ module GraphQL
45
41
  end
46
42
 
47
43
  private
48
- def compare_results(warden_result, subset_result)
49
- if warden_result.is_a?(Array) && subset_result.is_a?(Array)
50
- all_results = warden_result | subset_result
44
+ def compare_results(warden_result, profile_result)
45
+ if warden_result.is_a?(Array) && profile_result.is_a?(Array)
46
+ all_results = warden_result | profile_result
51
47
  all_results.sort_by!(&:graphql_name)
52
48
 
53
49
  entries_text = all_results.map { |entry| "#{entry.graphql_name} (#{entry})"}
@@ -55,13 +51,13 @@ module GraphQL
55
51
  yes = " ✔ "
56
52
  no = " "
57
53
  res = "".dup
58
- res << "#{"Result".center(width)} Warden Subset \n"
54
+ res << "#{"Result".center(width)} Warden Profile \n"
59
55
  all_results.each_with_index do |entry, idx|
60
- res << "#{entries_text[idx].ljust(width)}#{warden_result.include?(entry) ? yes : no}#{subset_result.include?(entry) ? yes : no}\n"
56
+ res << "#{entries_text[idx].ljust(width)}#{warden_result.include?(entry) ? yes : no}#{profile_result.include?(entry) ? yes : no}\n"
61
57
  end
62
58
  res << "\n"
63
59
  else
64
- "- Warden returned: #{humanize(warden_result)}\n\n- Subset returned: #{humanize(subset_result)}"
60
+ "- Warden returned: #{humanize(warden_result)}\n\n- Visibility::Profile returned: #{humanize(profile_result)}"
65
61
  end
66
62
  end
67
63
  def humanize(val)
@@ -80,38 +76,39 @@ module GraphQL
80
76
  end
81
77
  end
82
78
 
83
- def initialize(context:, schema:)
84
- @skip_error = context[:skip_visibility_migration_error]
85
- context[:visibility_migration_running] = true
86
- @subset_types = GraphQL::Schema::Visibility::Subset.new(context: context, schema: schema)
79
+ def initialize(context:, schema:, name: nil)
80
+ @name = name
81
+ @skip_error = context[:skip_visibility_migration_error] || context.is_a?(Query::NullContext) || context.is_a?(Hash)
82
+ @profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema)
87
83
  if !@skip_error
84
+ context[:visibility_migration_running] = true
88
85
  warden_ctx_vals = context.to_h.dup
89
86
  warden_ctx_vals[:visibility_migration_warden_running] = true
90
- if defined?(schema::WardenCompatSchema)
91
- warden_schema = schema::WardenCompatSchema
87
+ if schema.const_defined?(:WardenCompatSchema, false) # don't use a defn from a superclass
88
+ warden_schema = schema.const_get(:WardenCompatSchema, false)
92
89
  else
93
90
  warden_schema = Class.new(schema)
94
- warden_schema.use_schema_visibility = false
91
+ warden_schema.use_visibility_profile = false
95
92
  # TODO public API
96
93
  warden_schema.send(:add_type_and_traverse, [warden_schema.query, warden_schema.mutation, warden_schema.subscription].compact, root: true)
97
94
  warden_schema.send(:add_type_and_traverse, warden_schema.directives.values + warden_schema.orphan_types, root: false)
95
+ schema.const_set(:WardenCompatSchema, warden_schema)
98
96
  end
99
97
  warden_ctx = GraphQL::Query::Context.new(query: context.query, values: warden_ctx_vals)
100
- example_warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx)
101
- @warden_types = example_warden.schema_subset
102
- warden_ctx.warden = example_warden
103
- warden_ctx.types = @warden_types
98
+ warden_ctx.warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx)
99
+ warden_ctx.types = @warden_types = warden_ctx.warden.visibility_profile
104
100
  end
105
101
  end
106
102
 
107
103
  def loaded_types
108
- @subset_types.loaded_types
104
+ @profile_types.loaded_types
109
105
  end
110
106
 
111
- PUBLIC_SUBSET_METHODS = [
107
+ PUBLIC_PROFILE_METHODS = [
112
108
  :enum_values,
113
109
  :interfaces,
114
110
  :all_types,
111
+ :all_types_h,
115
112
  :fields,
116
113
  :loadable?,
117
114
  :type,
@@ -127,14 +124,14 @@ module GraphQL
127
124
  :reachable_type?
128
125
  ]
129
126
 
130
- PUBLIC_SUBSET_METHODS.each do |subset_method|
131
- define_method(subset_method) do |*args|
132
- call_method_and_compare(subset_method, args)
127
+ PUBLIC_PROFILE_METHODS.each do |profile_method|
128
+ define_method(profile_method) do |*args|
129
+ call_method_and_compare(profile_method, args)
133
130
  end
134
131
  end
135
132
 
136
133
  def call_method_and_compare(method, args)
137
- res_1 = @subset_types.public_send(method, *args)
134
+ res_1 = @profile_types.public_send(method, *args)
138
135
  if @skip_error
139
136
  return res_1
140
137
  end
@@ -11,26 +11,28 @@ module GraphQL
11
11
  # - It doesn't use {Schema}'s top-level caches (eg {Schema.references_to}, {Schema.possible_types}, {Schema.types})
12
12
  # - It doesn't hide Interface or Union types when all their possible types are hidden. (Instead, those types should implement `.visible?` to hide in that case.)
13
13
  # - It checks `.visible?` on root introspection types
14
- #
15
- # In the future, {Subset} will support lazy-loading types as needed during execution and multi-request caching of subsets.
16
- class Subset
17
- # @return [Schema::Visibility::Subset]
14
+ # - It can be used to cache profiles by name for re-use across queries
15
+ class Profile
16
+ # @return [Schema::Visibility::Profile]
18
17
  def self.from_context(ctx, schema)
19
18
  if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
20
19
  types
21
20
  else
22
- # TODO use a cached instance from the schema
23
- self.new(context: ctx, schema: schema)
21
+ schema.visibility.profile_for(ctx, nil)
24
22
  end
25
23
  end
26
24
 
27
25
  def self.pass_thru(context:, schema:)
28
- subset = self.new(context: context, schema: schema)
29
- subset.instance_variable_set(:@cached_visible, Hash.new { |h,k| h[k] = true })
30
- subset
26
+ profile = self.new(context: context, schema: schema)
27
+ profile.instance_variable_set(:@cached_visible, Hash.new { |h,k| h[k] = true })
28
+ profile
31
29
  end
32
30
 
33
- def initialize(context:, schema:)
31
+ # @return [Symbol, nil]
32
+ attr_reader :name
33
+
34
+ def initialize(name: nil, context:, schema:)
35
+ @name = name
34
36
  @context = context
35
37
  @schema = schema
36
38
  @all_types = {}
@@ -67,6 +69,7 @@ module GraphQL
67
69
  @cached_visible_arguments = Hash.new do |h, arg|
68
70
  h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
69
71
  add_type(arg_type, arg)
72
+ arg.validate_default_value
70
73
  true
71
74
  else
72
75
  false
@@ -403,8 +406,9 @@ module GraphQL
403
406
 
404
407
  @unfiltered_interface_type_memberships = Hash.new { |h, k| h[k] = [] }.compare_by_identity
405
408
  @add_possible_types = Set.new
409
+ @late_types = []
406
410
 
407
- while @unvisited_types.any?
411
+ while @unvisited_types.any? || @late_types.any?
408
412
  while t = @unvisited_types.pop
409
413
  # These have already been checked for `.visible?`
410
414
  visit_type(t)
@@ -418,6 +422,12 @@ module GraphQL
418
422
  end
419
423
  end
420
424
  @add_possible_types.clear
425
+
426
+ while (union_tm = @late_types.shift)
427
+ late_obj_t = union_tm.object_type
428
+ obj_t = @all_types[late_obj_t.graphql_name] || raise("Failed to resolve #{late_obj_t.graphql_name.inspect} from #{union_tm.inspect}")
429
+ union_tm.abstract_type.assign_type_membership_object_type(obj_t)
430
+ end
421
431
  end
422
432
 
423
433
  @all_types.delete_if { |type_name, type_defn| !referenced?(type_defn) }
@@ -470,12 +480,16 @@ module GraphQL
470
480
  type.type_memberships.each do |tm|
471
481
  if @cached_visible[tm]
472
482
  obj_t = tm.object_type
473
- if obj_t.is_a?(String)
474
- obj_t = Member::BuildType.constantize(obj_t)
475
- tm.object_type = obj_t
476
- end
477
- if @cached_visible[obj_t]
478
- add_type(obj_t, tm)
483
+ if obj_t.is_a?(GraphQL::Schema::LateBoundType)
484
+ @late_types << tm
485
+ else
486
+ if obj_t.is_a?(String)
487
+ obj_t = Member::BuildType.constantize(obj_t)
488
+ tm.object_type = obj_t
489
+ end
490
+ if @cached_visible[obj_t]
491
+ add_type(obj_t, tm)
492
+ end
479
493
  end
480
494
  end
481
495
  end
@@ -1,28 +1,73 @@
1
1
  # frozen_string_literal: true
2
- require "graphql/schema/visibility/subset"
2
+ require "graphql/schema/visibility/profile"
3
3
  require "graphql/schema/visibility/migration"
4
4
 
5
5
  module GraphQL
6
6
  class Schema
7
+ # Use this plugin to make some parts of your schema hidden from some viewers.
8
+ #
7
9
  class Visibility
8
- def self.use(schema, preload: nil, migration_errors: false)
9
- schema.visibility = self.new(schema, preload: preload)
10
- schema.use_schema_visibility = true
10
+ # @param schema [Class<GraphQL::Schema>]
11
+ # @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
12
+ # @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
13
+ # @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
14
+ def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails) ? Rails.env.production? : nil), migration_errors: false)
15
+ schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
16
+ end
17
+
18
+ def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
19
+ @schema = schema
20
+ schema.use_visibility_profile = true
11
21
  if migration_errors
12
- schema.subset_class = Migration
22
+ schema.visibility_profile_class = Migration
23
+ end
24
+ @profiles = profiles
25
+ @cached_profiles = {}
26
+ @dynamic = dynamic
27
+ @migration_errors = migration_errors
28
+ if preload
29
+ profiles.each do |profile_name, example_ctx|
30
+ example_ctx[:visibility_profile] = profile_name
31
+ prof = profile_for(example_ctx, profile_name)
32
+ prof.all_types # force loading
33
+ end
13
34
  end
14
35
  end
15
36
 
16
- def initialize(schema, preload:)
17
- @schema = schema
18
- @cached_subsets = {}
37
+ # Make another Visibility for `schema` based on this one
38
+ # @return [Visibility]
39
+ # @api private
40
+ def dup_for(other_schema)
41
+ self.class.new(
42
+ other_schema,
43
+ dynamic: @dynamic,
44
+ preload: @preload,
45
+ profiles: @profiles,
46
+ migration_errors: @migration_errors
47
+ )
48
+ end
19
49
 
20
- if preload.nil? && defined?(Rails) && Rails.env.production?
21
- preload = true
22
- end
50
+ def migration_errors?
51
+ @migration_errors
52
+ end
23
53
 
24
- if preload
54
+ attr_reader :cached_profiles
25
55
 
56
+ def profile_for(context, visibility_profile)
57
+ if @profiles.any?
58
+ if visibility_profile.nil?
59
+ if @dynamic
60
+ @schema.visibility_profile_class.new(context: context, schema: @schema)
61
+ elsif @profiles.any?
62
+ 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."
63
+ end
64
+ elsif !@profiles.include?(visibility_profile)
65
+ 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."
66
+ else
67
+ @cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: context, schema: @schema)
68
+ end
69
+ else
70
+ @schema.visibility_profile_class.new(context: context, schema: @schema)
26
71
  end
27
72
  end
28
73
  end
@@ -61,8 +61,8 @@ module GraphQL
61
61
  def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
62
62
  def arguments(owner, ctx); owner.arguments(ctx); end
63
63
  def loadable?(type, ctx); type.visible?(ctx); end
64
- def schema_subset
65
- @schema_subset ||= Warden::SchemaSubset.new(self)
64
+ def visibility_profile
65
+ @visibility_profile ||= Warden::VisibilityProfile.new(self)
66
66
  end
67
67
  end
68
68
  end
@@ -70,17 +70,17 @@ module GraphQL
70
70
  class NullWarden
71
71
  def initialize(_filter = nil, context:, schema:)
72
72
  @schema = schema
73
- @schema_subset = Warden::SchemaSubset.new(self)
73
+ @visibility_profile = Warden::VisibilityProfile.new(self)
74
74
  end
75
75
 
76
76
  # @api private
77
- module NullSubset
77
+ module NullVisibilityProfile
78
78
  def self.new(context:, schema:)
79
- NullWarden.new(context: context, schema: schema).schema_subset
79
+ NullWarden.new(context: context, schema: schema).visibility_profile
80
80
  end
81
81
  end
82
82
 
83
- attr_reader :schema_subset
83
+ attr_reader :visibility_profile
84
84
 
85
85
  def visible_field?(field_defn, _ctx = nil, owner = nil); true; end
86
86
  def visible_argument?(arg_defn, _ctx = nil); true; end
@@ -88,7 +88,7 @@ module GraphQL
88
88
  def visible_enum_value?(enum_value, _ctx = nil); true; end
89
89
  def visible_type_membership?(type_membership, _ctx = nil); true; end
90
90
  def interface_type_memberships(obj_type, _ctx = nil); obj_type.interface_type_memberships; end
91
- def get_type(type_name); @schema.get_type(type_name); end # rubocop:disable Development/ContextIsPassedCop
91
+ def get_type(type_name); @schema.get_type(type_name, Query::NullContext.instance, false); end # rubocop:disable Development/ContextIsPassedCop
92
92
  def arguments(argument_owner, ctx = nil); argument_owner.all_argument_definitions; end
93
93
  def enum_values(enum_defn); enum_defn.enum_values; end # rubocop:disable Development/ContextIsPassedCop
94
94
  def get_argument(parent_type, argument_name); parent_type.get_argument(argument_name); end # rubocop:disable Development/ContextIsPassedCop
@@ -100,15 +100,15 @@ module GraphQL
100
100
  def reachable_type?(type_name); true; end
101
101
  def loadable?(type, _ctx); true; end
102
102
  def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
103
- def possible_types(type_defn); @schema.possible_types(type_defn); end
103
+ def possible_types(type_defn); @schema.possible_types(type_defn, Query::NullContext.instance, false); end
104
104
  def interfaces(obj_type); obj_type.interfaces; end
105
105
  end
106
106
 
107
- def schema_subset
108
- @schema_subset ||= SchemaSubset.new(self)
107
+ def visibility_profile
108
+ @visibility_profile ||= VisibilityProfile.new(self)
109
109
  end
110
110
 
111
- class SchemaSubset
111
+ class VisibilityProfile
112
112
  def initialize(warden)
113
113
  @warden = warden
114
114
  end
@@ -193,7 +193,7 @@ module GraphQL
193
193
  @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
194
194
  @visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships =
195
195
  @visible_and_reachable_type = @unions = @unfiltered_interfaces =
196
- @reachable_type_set = @schema_subset =
196
+ @reachable_type_set = @visibility_profile =
197
197
  nil
198
198
  end
199
199
 
@@ -218,7 +218,7 @@ module GraphQL
218
218
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
219
219
  def get_type(type_name)
220
220
  @visible_types ||= read_through do |name|
221
- type_defn = @schema.get_type(name, @context)
221
+ type_defn = @schema.get_type(name, @context, false)
222
222
  if type_defn && visible_and_reachable_type?(type_defn)
223
223
  type_defn
224
224
  else
@@ -265,7 +265,7 @@ module GraphQL
265
265
  # @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
266
266
  def possible_types(type_defn)
267
267
  @visible_possible_types ||= read_through { |type_defn|
268
- pt = @schema.possible_types(type_defn, @context)
268
+ pt = @schema.possible_types(type_defn, @context, false)
269
269
  pt.select { |t| visible_and_reachable_type?(t) }
270
270
  }
271
271
  @visible_possible_types[type_defn]
@@ -162,6 +162,7 @@ module GraphQL
162
162
  # re-apply them here
163
163
  mods = trace_modules_for(:default)
164
164
  mods.each { |mod| new_class.include(mod) }
165
+ new_class.include(DefaultTraceClass)
165
166
  trace_mode(:default, new_class)
166
167
  backtrace_class = Class.new(new_class)
167
168
  backtrace_class.include(GraphQL::Backtrace::Trace)
@@ -316,6 +317,9 @@ module GraphQL
316
317
  GraphQL::StaticValidation::Validator.new(schema: self)
317
318
  end
318
319
 
320
+ # Add `plugin` to this schema
321
+ # @param plugin [#use] A Schema plugin
322
+ # @return void
319
323
  def use(plugin, **kwargs)
320
324
  if kwargs.any?
321
325
  plugin.use(self, **kwargs)
@@ -333,8 +337,9 @@ module GraphQL
333
337
  # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
334
338
  # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
335
339
  def types(context = GraphQL::Query::NullContext.instance)
336
- if use_schema_visibility?
337
- return Visibility::Subset.from_context(context, self).all_types_h
340
+ if use_visibility_profile?
341
+ types = Visibility::Profile.from_context(context, self)
342
+ return types.all_types_h
338
343
  end
339
344
  all_types = non_introspection_types.merge(introspection_system.types)
340
345
  visible_types = {}
@@ -361,17 +366,19 @@ module GraphQL
361
366
  end
362
367
 
363
368
  # @param type_name [String]
369
+ # @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
370
+ # @param use_visibility_profile Private, for migration to {Schema::Visibility}
364
371
  # @return [Module, nil] A type, or nil if there's no type called `type_name`
365
- def get_type(type_name, context = GraphQL::Query::NullContext.instance)
366
- if use_schema_visibility?
367
- return Visibility::Subset.from_context(context, self).type(type_name)
372
+ def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
373
+ if use_visibility_profile
374
+ return Visibility::Profile.from_context(context, self).type(type_name)
368
375
  end
369
376
  local_entry = own_types[type_name]
370
377
  type_defn = case local_entry
371
378
  when nil
372
379
  nil
373
380
  when Array
374
- if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Subset)
381
+ if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
375
382
  local_entry
376
383
  else
377
384
  visible_t = nil
@@ -397,7 +404,7 @@ module GraphQL
397
404
 
398
405
  type_defn ||
399
406
  introspection_system.types[type_name] || # todo context-specific introspection?
400
- (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
407
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context, use_visibility_profile) : nil)
401
408
  end
402
409
 
403
410
  # @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
@@ -429,7 +436,7 @@ module GraphQL
429
436
  if @query_object
430
437
  dup_defn = new_query_object || yield
431
438
  raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
432
- elsif use_schema_visibility?
439
+ elsif use_visibility_profile?
433
440
  @query_object = block_given? ? lazy_load_block : new_query_object
434
441
  else
435
442
  @query_object = new_query_object || lazy_load_block.call
@@ -448,7 +455,7 @@ module GraphQL
448
455
  if @mutation_object
449
456
  dup_defn = new_mutation_object || yield
450
457
  raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
451
- elsif use_schema_visibility?
458
+ elsif use_visibility_profile?
452
459
  @mutation_object = block_given? ? lazy_load_block : new_mutation_object
453
460
  else
454
461
  @mutation_object = new_mutation_object || lazy_load_block.call
@@ -467,7 +474,7 @@ module GraphQL
467
474
  if @subscription_object
468
475
  dup_defn = new_subscription_object || yield
469
476
  raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
470
- elsif use_schema_visibility?
477
+ elsif use_visibility_profile?
471
478
  @subscription_object = block_given? ? lazy_load_block : new_subscription_object
472
479
  add_subscription_extension_if_necessary
473
480
  else
@@ -501,7 +508,7 @@ module GraphQL
501
508
  end
502
509
 
503
510
  def root_types
504
- if use_schema_visibility?
511
+ if use_visibility_profile?
505
512
  [query, mutation, subscription].compact
506
513
  else
507
514
  @root_types
@@ -520,37 +527,43 @@ module GraphQL
520
527
 
521
528
  attr_writer :warden_class
522
529
 
523
- def subset_class
524
- if defined?(@subset_class)
525
- @subset_class
526
- elsif superclass.respond_to?(:subset_class)
527
- superclass.subset_class
530
+ # @api private
531
+ def visibility_profile_class
532
+ if defined?(@visibility_profile_class)
533
+ @visibility_profile_class
534
+ elsif superclass.respond_to?(:visibility_profile_class)
535
+ superclass.visibility_profile_class
528
536
  else
529
- GraphQL::Schema::Visibility::Subset
537
+ GraphQL::Schema::Visibility::Profile
530
538
  end
531
539
  end
532
540
 
533
- attr_writer :subset_class, :use_schema_visibility, :visibility
534
-
535
- def use_schema_visibility?
536
- if defined?(@use_schema_visibility)
537
- @use_schema_visibility
538
- elsif superclass.respond_to?(:use_schema_visibility?)
539
- superclass.use_schema_visibility?
541
+ # @api private
542
+ attr_writer :visibility_profile_class, :use_visibility_profile
543
+ # @api private
544
+ attr_accessor :visibility
545
+ # @api private
546
+ def use_visibility_profile?
547
+ if defined?(@use_visibility_profile)
548
+ @use_visibility_profile
549
+ elsif superclass.respond_to?(:use_visibility_profile?)
550
+ superclass.use_visibility_profile?
540
551
  else
541
552
  false
542
553
  end
543
554
  end
544
555
 
545
556
  # @param type [Module] The type definition whose possible types you want to see
557
+ # @param context [GraphQL::Query::Context] used for filtering visible possible types at runtime
558
+ # @param use_visibility_profile Private, for migration to {Schema::Visibility}
546
559
  # @return [Hash<String, Module>] All possible types, if no `type` is given.
547
560
  # @return [Array<Module>] Possible types for `type`, if it's given.
548
- def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
549
- if use_schema_visibility?
561
+ def possible_types(type = nil, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
562
+ if use_visibility_profile
550
563
  if type
551
- return Visibility::Subset.from_context(context, self).possible_types(type)
564
+ return Visibility::Profile.from_context(context, self).possible_types(type)
552
565
  else
553
- raise "Schema.possible_types is not implemented for `use_schema_visibility?`"
566
+ raise "Schema.possible_types is not implemented for `use_visibility_profile?`"
554
567
  end
555
568
  end
556
569
  if type
@@ -570,7 +583,7 @@ module GraphQL
570
583
  introspection_system.possible_types[type] ||
571
584
  (
572
585
  superclass.respond_to?(:possible_types) ?
573
- superclass.possible_types(type, context) :
586
+ superclass.possible_types(type, context, use_visibility_profile) :
574
587
  EMPTY_ARRAY
575
588
  )
576
589
  end
@@ -926,7 +939,7 @@ module GraphQL
926
939
  To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
927
940
  ERR
928
941
  end
929
- add_type_and_traverse(new_orphan_types, root: false) unless use_schema_visibility?
942
+ add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
930
943
  own_orphan_types.concat(new_orphan_types.flatten)
931
944
  end
932
945
 
@@ -1068,6 +1081,11 @@ module GraphQL
1068
1081
  child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
1069
1082
  end
1070
1083
  child_class.singleton_class.prepend(ResolveTypeWithType)
1084
+
1085
+ if use_visibility_profile?
1086
+ vis = self.visibility
1087
+ child_class.visibility = vis.dup_for(child_class)
1088
+ end
1071
1089
  super
1072
1090
  end
1073
1091
 
@@ -1185,7 +1203,7 @@ module GraphQL
1185
1203
  # @param new_directive [Class]
1186
1204
  # @return void
1187
1205
  def directive(new_directive)
1188
- if use_schema_visibility?
1206
+ if use_visibility_profile?
1189
1207
  own_directives[new_directive.graphql_name] = new_directive
1190
1208
  else
1191
1209
  add_type_and_traverse(new_directive, root: false)
@@ -212,6 +212,7 @@ module GraphQL
212
212
 
213
213
  def find_conflict(response_key, field1, field2, mutually_exclusive: false)
214
214
  return if @conflict_count >= context.max_errors
215
+ return if field1.definition.nil? || field2.definition.nil?
215
216
 
216
217
  node1 = field1.node
217
218
  node2 = field2.node
@@ -92,7 +92,7 @@ module GraphQL
92
92
  end
93
93
  graphql_result
94
94
  else
95
- unfiltered_type = Schema::Visibility::Subset.pass_thru(schema: schema, context: context).type(type_name)
95
+ unfiltered_type = Schema::Visibility::Profile.pass_thru(schema: schema, context: context).type(type_name)
96
96
  if unfiltered_type
97
97
  raise TypeNotVisibleError.new(type_name: type_name)
98
98
  else
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.3.17"
3
+ VERSION = "2.3.19"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.17
4
+ version: 2.3.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-04 00:00:00.000000000 Z
11
+ date: 2024-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -503,7 +503,7 @@ files:
503
503
  - lib/graphql/schema/validator/required_validator.rb
504
504
  - lib/graphql/schema/visibility.rb
505
505
  - lib/graphql/schema/visibility/migration.rb
506
- - lib/graphql/schema/visibility/subset.rb
506
+ - lib/graphql/schema/visibility/profile.rb
507
507
  - lib/graphql/schema/warden.rb
508
508
  - lib/graphql/schema/wrapper.rb
509
509
  - lib/graphql/static_validation.rb