graphql 2.3.5 → 2.3.6

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.

@@ -0,0 +1,282 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Analysis
4
+ # Depth first traversal through a query AST, calling AST analyzers
5
+ # along the way.
6
+ #
7
+ # The visitor is a special case of GraphQL::Language::StaticVisitor, visiting
8
+ # only the selected operation, providing helpers for common use cases such
9
+ # as skipped fields and visiting fragment spreads.
10
+ #
11
+ # @see {GraphQL::Analysis::Analyzer} AST Analyzers for queries
12
+ class Visitor < GraphQL::Language::StaticVisitor
13
+ def initialize(query:, analyzers:)
14
+ @analyzers = analyzers
15
+ @path = []
16
+ @object_types = []
17
+ @directives = []
18
+ @field_definitions = []
19
+ @argument_definitions = []
20
+ @directive_definitions = []
21
+ @rescued_errors = []
22
+ @query = query
23
+ @schema = query.schema
24
+ @response_path = []
25
+ @skip_stack = [false]
26
+ super(query.selected_operation)
27
+ end
28
+
29
+ # @return [GraphQL::Query] the query being visited
30
+ attr_reader :query
31
+
32
+ # @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
33
+ attr_reader :object_types
34
+
35
+ # @return [Array<GraphQL::AnalysisError]
36
+ attr_reader :rescued_errors
37
+
38
+ def visit
39
+ return unless @document
40
+ super
41
+ end
42
+
43
+ # Visit Helpers
44
+
45
+ # @return [GraphQL::Execution::Interpreter::Arguments] Arguments for this node, merging default values, literal values and query variables
46
+ # @see {GraphQL::Query#arguments_for}
47
+ def arguments_for(ast_node, field_definition)
48
+ @query.arguments_for(ast_node, field_definition)
49
+ end
50
+
51
+ # @return [Boolean] If the visitor is currently inside a fragment definition
52
+ def visiting_fragment_definition?
53
+ @in_fragment_def
54
+ end
55
+
56
+ # @return [Boolean] If the current node should be skipped because of a skip or include directive
57
+ def skipping?
58
+ @skipping
59
+ end
60
+
61
+ # @return [Array<String>] The path to the response key for the current field
62
+ def response_path
63
+ @response_path.dup
64
+ end
65
+
66
+ # Visitor Hooks
67
+ [
68
+ :operation_definition, :fragment_definition,
69
+ :inline_fragment, :field, :directive, :argument, :fragment_spread
70
+ ].each do |node_type|
71
+ module_eval <<-RUBY, __FILE__, __LINE__
72
+ def call_on_enter_#{node_type}(node, parent)
73
+ @analyzers.each do |a|
74
+ begin
75
+ a.on_enter_#{node_type}(node, parent, self)
76
+ rescue AnalysisError => err
77
+ @rescued_errors << err
78
+ end
79
+ end
80
+ end
81
+
82
+ def call_on_leave_#{node_type}(node, parent)
83
+ @analyzers.each do |a|
84
+ begin
85
+ a.on_leave_#{node_type}(node, parent, self)
86
+ rescue AnalysisError => err
87
+ @rescued_errors << err
88
+ end
89
+ end
90
+ end
91
+
92
+ RUBY
93
+ end
94
+
95
+ def on_operation_definition(node, parent)
96
+ object_type = @schema.root_type_for_operation(node.operation_type)
97
+ @object_types.push(object_type)
98
+ @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
99
+ call_on_enter_operation_definition(node, parent)
100
+ super
101
+ call_on_leave_operation_definition(node, parent)
102
+ @object_types.pop
103
+ @path.pop
104
+ end
105
+
106
+ def on_fragment_definition(node, parent)
107
+ on_fragment_with_type(node) do
108
+ @path.push("fragment #{node.name}")
109
+ @in_fragment_def = false
110
+ call_on_enter_fragment_definition(node, parent)
111
+ super
112
+ @in_fragment_def = false
113
+ call_on_leave_fragment_definition(node, parent)
114
+ end
115
+ end
116
+
117
+ def on_inline_fragment(node, parent)
118
+ on_fragment_with_type(node) do
119
+ @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
120
+ @skipping = @skip_stack.last || skip?(node)
121
+ @skip_stack << @skipping
122
+
123
+ call_on_enter_inline_fragment(node, parent)
124
+ super
125
+ @skipping = @skip_stack.pop
126
+ call_on_leave_inline_fragment(node, parent)
127
+ end
128
+ end
129
+
130
+ def on_field(node, parent)
131
+ @response_path.push(node.alias || node.name)
132
+ parent_type = @object_types.last
133
+ # This could be nil if the previous field wasn't found:
134
+ field_definition = parent_type && @schema.get_field(parent_type, node.name, @query.context)
135
+ @field_definitions.push(field_definition)
136
+ if !field_definition.nil?
137
+ next_object_type = field_definition.type.unwrap
138
+ @object_types.push(next_object_type)
139
+ else
140
+ @object_types.push(nil)
141
+ end
142
+ @path.push(node.alias || node.name)
143
+
144
+ @skipping = @skip_stack.last || skip?(node)
145
+ @skip_stack << @skipping
146
+
147
+ call_on_enter_field(node, parent)
148
+ super
149
+ @skipping = @skip_stack.pop
150
+ call_on_leave_field(node, parent)
151
+ @response_path.pop
152
+ @field_definitions.pop
153
+ @object_types.pop
154
+ @path.pop
155
+ end
156
+
157
+ def on_directive(node, parent)
158
+ directive_defn = @schema.directives[node.name]
159
+ @directive_definitions.push(directive_defn)
160
+ call_on_enter_directive(node, parent)
161
+ super
162
+ call_on_leave_directive(node, parent)
163
+ @directive_definitions.pop
164
+ end
165
+
166
+ def on_argument(node, parent)
167
+ argument_defn = if (arg = @argument_definitions.last)
168
+ arg_type = arg.type.unwrap
169
+ if arg_type.kind.input_object?
170
+ arg_type.get_argument(node.name, @query.context)
171
+ else
172
+ nil
173
+ end
174
+ elsif (directive_defn = @directive_definitions.last)
175
+ directive_defn.get_argument(node.name, @query.context)
176
+ elsif (field_defn = @field_definitions.last)
177
+ field_defn.get_argument(node.name, @query.context)
178
+ else
179
+ nil
180
+ end
181
+
182
+ @argument_definitions.push(argument_defn)
183
+ @path.push(node.name)
184
+ call_on_enter_argument(node, parent)
185
+ super
186
+ call_on_leave_argument(node, parent)
187
+ @argument_definitions.pop
188
+ @path.pop
189
+ end
190
+
191
+ def on_fragment_spread(node, parent)
192
+ @path.push("... #{node.name}")
193
+ @skipping = @skip_stack.last || skip?(node)
194
+ @skip_stack << @skipping
195
+
196
+ call_on_enter_fragment_spread(node, parent)
197
+ enter_fragment_spread_inline(node)
198
+ super
199
+ @skipping = @skip_stack.pop
200
+ leave_fragment_spread_inline(node)
201
+ call_on_leave_fragment_spread(node, parent)
202
+ @path.pop
203
+ end
204
+
205
+ # @return [GraphQL::BaseType] The current object type
206
+ def type_definition
207
+ @object_types.last
208
+ end
209
+
210
+ # @return [GraphQL::BaseType] The type which the current type came from
211
+ def parent_type_definition
212
+ @object_types[-2]
213
+ end
214
+
215
+ # @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
216
+ def field_definition
217
+ @field_definitions.last
218
+ end
219
+
220
+ # @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to
221
+ def previous_field_definition
222
+ @field_definitions[-2]
223
+ end
224
+
225
+ # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
226
+ def directive_definition
227
+ @directive_definitions.last
228
+ end
229
+
230
+ # @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
231
+ def argument_definition
232
+ @argument_definitions.last
233
+ end
234
+
235
+ # @return [GraphQL::Argument, nil] The previous GraphQL argument
236
+ def previous_argument_definition
237
+ @argument_definitions[-2]
238
+ end
239
+
240
+ private
241
+
242
+ # Visit a fragment spread inline instead of visiting the definition
243
+ # by itself.
244
+ def enter_fragment_spread_inline(fragment_spread)
245
+ fragment_def = query.fragments[fragment_spread.name]
246
+
247
+ object_type = if fragment_def.type
248
+ @query.warden.get_type(fragment_def.type.name)
249
+ else
250
+ object_types.last
251
+ end
252
+
253
+ object_types << object_type
254
+
255
+ on_fragment_definition_children(fragment_def)
256
+ end
257
+
258
+ # Visit a fragment spread inline instead of visiting the definition
259
+ # by itself.
260
+ def leave_fragment_spread_inline(_fragment_spread)
261
+ object_types.pop
262
+ end
263
+
264
+ def skip?(ast_node)
265
+ dir = ast_node.directives
266
+ dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
267
+ end
268
+
269
+ def on_fragment_with_type(node)
270
+ object_type = if node.type
271
+ @query.warden.get_type(node.type.name)
272
+ else
273
+ @object_types.last
274
+ end
275
+ @object_types.push(object_type)
276
+ yield(node)
277
+ @object_types.pop
278
+ @path.pop
279
+ end
280
+ end
281
+ end
282
+ end
@@ -1,2 +1,93 @@
1
1
  # frozen_string_literal: true
2
- require "graphql/analysis/ast"
2
+ require "graphql/analysis/visitor"
3
+ require "graphql/analysis/analyzer"
4
+ require "graphql/analysis/field_usage"
5
+ require "graphql/analysis/query_complexity"
6
+ require "graphql/analysis/max_query_complexity"
7
+ require "graphql/analysis/query_depth"
8
+ require "graphql/analysis/max_query_depth"
9
+ require "timeout"
10
+
11
+ module GraphQL
12
+ module Analysis
13
+ AST = self
14
+ module_function
15
+ # Analyze a multiplex, and all queries within.
16
+ # Multiplex analyzers are ran for all queries, keeping state.
17
+ # Query analyzers are ran per query, without carrying state between queries.
18
+ #
19
+ # @param multiplex [GraphQL::Execution::Multiplex]
20
+ # @param analyzers [Array<GraphQL::Analysis::Analyzer>]
21
+ # @return [Array<Any>] Results from multiplex analyzers
22
+ def analyze_multiplex(multiplex, analyzers)
23
+ multiplex_analyzers = analyzers.map { |analyzer| analyzer.new(multiplex) }
24
+
25
+ multiplex.current_trace.analyze_multiplex(multiplex: multiplex) do
26
+ query_results = multiplex.queries.map do |query|
27
+ if query.valid?
28
+ analyze_query(
29
+ query,
30
+ query.analyzers,
31
+ multiplex_analyzers: multiplex_analyzers
32
+ )
33
+ else
34
+ []
35
+ end
36
+ end
37
+
38
+ multiplex_results = multiplex_analyzers.map(&:result)
39
+ multiplex_errors = analysis_errors(multiplex_results)
40
+
41
+ multiplex.queries.each_with_index do |query, idx|
42
+ query.analysis_errors = multiplex_errors + analysis_errors(query_results[idx])
43
+ end
44
+ multiplex_results
45
+ end
46
+ end
47
+
48
+ # @param query [GraphQL::Query]
49
+ # @param analyzers [Array<GraphQL::Analysis::Analyzer>]
50
+ # @return [Array<Any>] Results from those analyzers
51
+ def analyze_query(query, analyzers, multiplex_analyzers: [])
52
+ query.current_trace.analyze_query(query: query) do
53
+ query_analyzers = analyzers
54
+ .map { |analyzer| analyzer.new(query) }
55
+ .tap { _1.select!(&:analyze?) }
56
+
57
+ analyzers_to_run = query_analyzers + multiplex_analyzers
58
+ if analyzers_to_run.any?
59
+
60
+ analyzers_to_run.select!(&:visit?)
61
+ if analyzers_to_run.any?
62
+ visitor = GraphQL::Analysis::Visitor.new(
63
+ query: query,
64
+ analyzers: analyzers_to_run
65
+ )
66
+
67
+ # `nil` or `0` causes no timeout
68
+ Timeout::timeout(query.validate_timeout_remaining) do
69
+ visitor.visit
70
+ end
71
+
72
+ if visitor.rescued_errors.any?
73
+ return visitor.rescued_errors
74
+ end
75
+ end
76
+
77
+ query_analyzers.map(&:result)
78
+ else
79
+ []
80
+ end
81
+ end
82
+ rescue Timeout::Error
83
+ [GraphQL::AnalysisError.new("Timeout on validation of query")]
84
+ rescue GraphQL::UnauthorizedError
85
+ # This error was raised during analysis and will be returned the client before execution
86
+ []
87
+ end
88
+
89
+ def analysis_errors(results)
90
+ results.flatten.tap { _1.select! { |r| r.is_a?(GraphQL::AnalysisError) } }
91
+ end
92
+ end
93
+ end
@@ -21,6 +21,7 @@ module GraphQL
21
21
  manager = spawn_fiber do
22
22
  while first_pass || job_fibers.any?
23
23
  first_pass = false
24
+ fiber_vars = get_fiber_variables
24
25
 
25
26
  while (f = (job_fibers.shift || spawn_job_fiber))
26
27
  if f.alive?
@@ -34,6 +35,7 @@ module GraphQL
34
35
  next_job_fibers.clear
35
36
 
36
37
  Sync do |root_task|
38
+ set_fiber_variables(fiber_vars)
37
39
  while source_tasks.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
38
40
  while (task = source_tasks.shift || spawn_source_task(root_task, sources_condition))
39
41
  if task.alive?
@@ -40,7 +40,7 @@ module GraphQL
40
40
  lazies_at_depth = Hash.new { |h, k| h[k] = [] }
41
41
  multiplex_analyzers = schema.multiplex_analyzers
42
42
  if multiplex.max_complexity
43
- multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
43
+ multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
44
44
  end
45
45
 
46
46
  schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
@@ -100,10 +100,10 @@ module GraphQL
100
100
  # Depending on the analysis engine, we must use different analyzers
101
101
  # remove this once everything has switched over to AST analyzers
102
102
  if max_depth
103
- qa << GraphQL::Analysis::AST::MaxQueryDepth
103
+ qa << GraphQL::Analysis::MaxQueryDepth
104
104
  end
105
105
  if max_complexity
106
- qa << GraphQL::Analysis::AST::MaxQueryComplexity
106
+ qa << GraphQL::Analysis::MaxQueryComplexity
107
107
  end
108
108
  qa
109
109
  else
@@ -312,10 +312,15 @@ module GraphQL
312
312
  context.query.after_lazy(custom_loaded_value) do |custom_value|
313
313
  if loads
314
314
  if type.list?
315
- loaded_values = custom_value.each_with_index.map { |custom_val, idx|
316
- id = coerced_value[idx]
317
- load_method_owner.authorize_application_object(self, id, context, custom_val)
318
- }
315
+ loaded_values = []
316
+ context.dataloader.run_isolated do
317
+ custom_value.each_with_index.map { |custom_val, idx|
318
+ id = coerced_value[idx]
319
+ context.dataloader.append_job do
320
+ loaded_values[idx] = load_method_owner.authorize_application_object(self, id, context, custom_val)
321
+ end
322
+ }
323
+ end
319
324
  context.schema.after_any_lazies(loaded_values, &:itself)
320
325
  else
321
326
  load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value)
@@ -326,7 +331,16 @@ module GraphQL
326
331
  end
327
332
  elsif loads
328
333
  if type.list?
329
- loaded_values = coerced_value.map { |val| load_method_owner.load_and_authorize_application_object(self, val, context) }
334
+ loaded_values = []
335
+ # We want to run these list items all together,
336
+ # but we also need to wait for the result so we can return it :S
337
+ context.dataloader.run_isolated do
338
+ coerced_value.each_with_index { |val, idx|
339
+ context.dataloader.append_job do
340
+ loaded_values[idx] = load_method_owner.load_and_authorize_application_object(self, val, context)
341
+ end
342
+ }
343
+ end
330
344
  context.schema.after_any_lazies(loaded_values, &:itself)
331
345
  else
332
346
  load_method_owner.load_and_authorize_application_object(self, coerced_value, context)
@@ -188,6 +188,8 @@ module GraphQL
188
188
  assert_has_location(SCALAR)
189
189
  elsif @owner < GraphQL::Schema
190
190
  assert_has_location(SCHEMA)
191
+ elsif @owner < GraphQL::Schema::Resolver
192
+ assert_has_location(FIELD_DEFINITION)
191
193
  else
192
194
  raise "Unexpected directive owner class: #{@owner}"
193
195
  end
@@ -41,6 +41,14 @@ module GraphQL
41
41
  end
42
42
  end
43
43
 
44
+ def directives
45
+ if @resolver_class
46
+ @resolver_class.directives
47
+ else
48
+ super
49
+ end
50
+ end
51
+
44
52
  # @return [Class] The thing this field was defined on (type, mutation, resolver)
45
53
  attr_accessor :owner
46
54
 
@@ -25,6 +25,7 @@ module GraphQL
25
25
  extend GraphQL::Schema::Member::HasValidators
26
26
  include Schema::Member::HasPath
27
27
  extend Schema::Member::HasPath
28
+ extend Schema::Member::HasDirectives
28
29
 
29
30
  # @param object [Object] The application object that this field is being resolved on
30
31
  # @param context [GraphQL::Query::Context]
@@ -9,7 +9,7 @@ module GraphQL
9
9
  # Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]`
10
10
  # @api private
11
11
  # @see Subscriptions#broadcastable? for a public API
12
- class BroadcastAnalyzer < GraphQL::Analysis::AST::Analyzer
12
+ class BroadcastAnalyzer < GraphQL::Analysis::Analyzer
13
13
  def initialize(subject)
14
14
  super
15
15
  @default_broadcastable = subject.schema.subscriptions.default_broadcastable
@@ -235,7 +235,7 @@ module GraphQL
235
235
  if !query.valid?
236
236
  raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}"
237
237
  end
238
- GraphQL::Analysis::AST.analyze_query(query, @schema.query_analyzers)
238
+ GraphQL::Analysis.analyze_query(query, @schema.query_analyzers)
239
239
  query.context.namespace(:subscriptions)[:subscription_broadcastable]
240
240
  end
241
241
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.3.5"
3
+ VERSION = "2.3.6"
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.5
4
+ version: 2.3.6
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-06-13 00:00:00.000000000 Z
11
+ date: 2024-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -306,14 +306,13 @@ files:
306
306
  - lib/generators/graphql/union_generator.rb
307
307
  - lib/graphql.rb
308
308
  - lib/graphql/analysis.rb
309
- - lib/graphql/analysis/ast.rb
310
- - lib/graphql/analysis/ast/analyzer.rb
311
- - lib/graphql/analysis/ast/field_usage.rb
312
- - lib/graphql/analysis/ast/max_query_complexity.rb
313
- - lib/graphql/analysis/ast/max_query_depth.rb
314
- - lib/graphql/analysis/ast/query_complexity.rb
315
- - lib/graphql/analysis/ast/query_depth.rb
316
- - lib/graphql/analysis/ast/visitor.rb
309
+ - lib/graphql/analysis/analyzer.rb
310
+ - lib/graphql/analysis/field_usage.rb
311
+ - lib/graphql/analysis/max_query_complexity.rb
312
+ - lib/graphql/analysis/max_query_depth.rb
313
+ - lib/graphql/analysis/query_complexity.rb
314
+ - lib/graphql/analysis/query_depth.rb
315
+ - lib/graphql/analysis/visitor.rb
317
316
  - lib/graphql/analysis_error.rb
318
317
  - lib/graphql/backtrace.rb
319
318
  - lib/graphql/backtrace/inspect_result.rb
@@ -645,7 +644,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
645
644
  - !ruby/object:Gem::Version
646
645
  version: '0'
647
646
  requirements: []
648
- rubygems_version: 3.5.3
647
+ rubygems_version: 3.5.12
649
648
  signing_key:
650
649
  specification_version: 4
651
650
  summary: A GraphQL language and runtime for Ruby
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Analysis
4
- module AST
5
- # Query analyzer for query ASTs. Query analyzers respond to visitor style methods
6
- # but are prefixed by `enter` and `leave`.
7
- #
8
- # When an analyzer is initialized with a Multiplex, you can always get the current query from
9
- # `visitor.query` in the visit methods.
10
- #
11
- # @param [GraphQL::Query, GraphQL::Execution::Multiplex] The query or multiplex to analyze
12
- class Analyzer
13
- def initialize(subject)
14
- @subject = subject
15
-
16
- if subject.is_a?(GraphQL::Query)
17
- @query = subject
18
- @multiplex = nil
19
- else
20
- @multiplex = subject
21
- @query = nil
22
- end
23
- end
24
-
25
- # Analyzer hook to decide at analysis time whether a query should
26
- # be analyzed or not.
27
- # @return [Boolean] If the query should be analyzed or not
28
- def analyze?
29
- true
30
- end
31
-
32
- # Analyzer hook to decide at analysis time whether analysis
33
- # requires a visitor pass; can be disabled for precomputed results.
34
- # @return [Boolean] If analysis requires visitation or not
35
- def visit?
36
- true
37
- end
38
-
39
- # The result for this analyzer. Returning {GraphQL::AnalysisError} results
40
- # in a query error.
41
- # @return [Any] The analyzer result
42
- def result
43
- raise GraphQL::RequiredImplementationMissingError
44
- end
45
-
46
- class << self
47
- private
48
-
49
- def build_visitor_hooks(member_name)
50
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
51
- def on_enter_#{member_name}(node, parent, visitor)
52
- end
53
-
54
- def on_leave_#{member_name}(node, parent, visitor)
55
- end
56
- EOS
57
- end
58
- end
59
-
60
- build_visitor_hooks :argument
61
- build_visitor_hooks :directive
62
- build_visitor_hooks :document
63
- build_visitor_hooks :enum
64
- build_visitor_hooks :field
65
- build_visitor_hooks :fragment_spread
66
- build_visitor_hooks :inline_fragment
67
- build_visitor_hooks :input_object
68
- build_visitor_hooks :list_type
69
- build_visitor_hooks :non_null_type
70
- build_visitor_hooks :null_value
71
- build_visitor_hooks :operation_definition
72
- build_visitor_hooks :type_name
73
- build_visitor_hooks :variable_definition
74
- build_visitor_hooks :variable_identifier
75
- build_visitor_hooks :abstract_node
76
-
77
- protected
78
-
79
- # @return [GraphQL::Query, GraphQL::Execution::Multiplex] Whatever this analyzer is analyzing
80
- attr_reader :subject
81
-
82
- # @return [GraphQL::Query, nil] `nil` if this analyzer is visiting a multiplex
83
- # (When this is `nil`, use `visitor.query` inside visit methods to get the current query)
84
- attr_reader :query
85
-
86
- # @return [GraphQL::Execution::Multiplex, nil] `nil` if this analyzer is visiting a query
87
- attr_reader :multiplex
88
- end
89
- end
90
- end
91
- end