graphql 2.3.5 → 2.3.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +46 -0
  3. data/lib/graphql/analysis/analyzer.rb +89 -0
  4. data/lib/graphql/analysis/field_usage.rb +82 -0
  5. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  6. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  7. data/lib/graphql/analysis/query_complexity.rb +183 -0
  8. data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
  9. data/lib/graphql/analysis/visitor.rb +283 -0
  10. data/lib/graphql/analysis.rb +92 -1
  11. data/lib/graphql/current.rb +52 -0
  12. data/lib/graphql/dataloader/async_dataloader.rb +2 -0
  13. data/lib/graphql/dataloader/source.rb +5 -2
  14. data/lib/graphql/dataloader.rb +4 -1
  15. data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
  16. data/lib/graphql/execution/interpreter/runtime.rb +8 -14
  17. data/lib/graphql/execution/interpreter.rb +3 -1
  18. data/lib/graphql/execution/lookahead.rb +10 -10
  19. data/lib/graphql/introspection/directive_type.rb +1 -1
  20. data/lib/graphql/introspection/entry_points.rb +2 -2
  21. data/lib/graphql/introspection/field_type.rb +1 -1
  22. data/lib/graphql/introspection/schema_type.rb +6 -11
  23. data/lib/graphql/introspection/type_type.rb +5 -5
  24. data/lib/graphql/language/document_from_schema_definition.rb +19 -26
  25. data/lib/graphql/language/lexer.rb +0 -3
  26. data/lib/graphql/language/nodes.rb +2 -2
  27. data/lib/graphql/language/parser.rb +9 -1
  28. data/lib/graphql/language/sanitized_printer.rb +1 -1
  29. data/lib/graphql/language.rb +0 -1
  30. data/lib/graphql/query/context.rb +7 -1
  31. data/lib/graphql/query/null_context.rb +2 -2
  32. data/lib/graphql/query/validation_pipeline.rb +2 -2
  33. data/lib/graphql/query.rb +26 -7
  34. data/lib/graphql/schema/always_visible.rb +1 -0
  35. data/lib/graphql/schema/argument.rb +19 -5
  36. data/lib/graphql/schema/build_from_definition.rb +8 -1
  37. data/lib/graphql/schema/directive/flagged.rb +1 -1
  38. data/lib/graphql/schema/directive.rb +2 -0
  39. data/lib/graphql/schema/enum.rb +9 -5
  40. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  41. data/lib/graphql/schema/field.rb +13 -1
  42. data/lib/graphql/schema/has_single_input_argument.rb +2 -1
  43. data/lib/graphql/schema/input_object.rb +8 -7
  44. data/lib/graphql/schema/interface.rb +20 -4
  45. data/lib/graphql/schema/introspection_system.rb +5 -16
  46. data/lib/graphql/schema/member/has_arguments.rb +14 -9
  47. data/lib/graphql/schema/member/has_fields.rb +6 -4
  48. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  49. data/lib/graphql/schema/resolver.rb +5 -5
  50. data/lib/graphql/schema/subset.rb +510 -0
  51. data/lib/graphql/schema/type_expression.rb +2 -2
  52. data/lib/graphql/schema/types_migration.rb +185 -0
  53. data/lib/graphql/schema/validator/all_validator.rb +60 -0
  54. data/lib/graphql/schema/validator.rb +2 -0
  55. data/lib/graphql/schema/warden.rb +89 -5
  56. data/lib/graphql/schema.rb +74 -37
  57. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  58. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  59. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  60. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  61. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  62. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  63. data/lib/graphql/static_validation/rules/fields_will_merge.rb +7 -7
  64. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  65. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  66. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  67. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  68. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  69. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
  70. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  71. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  72. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  73. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  74. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  75. data/lib/graphql/static_validation/validation_context.rb +2 -2
  76. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  77. data/lib/graphql/subscriptions/event.rb +1 -1
  78. data/lib/graphql/subscriptions.rb +3 -3
  79. data/lib/graphql/testing/helpers.rb +8 -5
  80. data/lib/graphql/types/relay/connection_behaviors.rb +10 -0
  81. data/lib/graphql/types/relay/edge_behaviors.rb +10 -0
  82. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  83. data/lib/graphql/version.rb +1 -1
  84. data/lib/graphql.rb +1 -0
  85. metadata +14 -13
  86. data/lib/graphql/analysis/ast/analyzer.rb +0 -91
  87. data/lib/graphql/analysis/ast/field_usage.rb +0 -84
  88. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  89. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  90. data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
  91. data/lib/graphql/analysis/ast/visitor.rb +0 -284
  92. data/lib/graphql/analysis/ast.rb +0 -94
  93. data/lib/graphql/language/token.rb +0 -34
  94. data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -0,0 +1,283 @@
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
+ @types = query.types
25
+ @response_path = []
26
+ @skip_stack = [false]
27
+ super(query.selected_operation)
28
+ end
29
+
30
+ # @return [GraphQL::Query] the query being visited
31
+ attr_reader :query
32
+
33
+ # @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
34
+ attr_reader :object_types
35
+
36
+ # @return [Array<GraphQL::AnalysisError]
37
+ attr_reader :rescued_errors
38
+
39
+ def visit
40
+ return unless @document
41
+ super
42
+ end
43
+
44
+ # Visit Helpers
45
+
46
+ # @return [GraphQL::Execution::Interpreter::Arguments] Arguments for this node, merging default values, literal values and query variables
47
+ # @see {GraphQL::Query#arguments_for}
48
+ def arguments_for(ast_node, field_definition)
49
+ @query.arguments_for(ast_node, field_definition)
50
+ end
51
+
52
+ # @return [Boolean] If the visitor is currently inside a fragment definition
53
+ def visiting_fragment_definition?
54
+ @in_fragment_def
55
+ end
56
+
57
+ # @return [Boolean] If the current node should be skipped because of a skip or include directive
58
+ def skipping?
59
+ @skipping
60
+ end
61
+
62
+ # @return [Array<String>] The path to the response key for the current field
63
+ def response_path
64
+ @response_path.dup
65
+ end
66
+
67
+ # Visitor Hooks
68
+ [
69
+ :operation_definition, :fragment_definition,
70
+ :inline_fragment, :field, :directive, :argument, :fragment_spread
71
+ ].each do |node_type|
72
+ module_eval <<-RUBY, __FILE__, __LINE__
73
+ def call_on_enter_#{node_type}(node, parent)
74
+ @analyzers.each do |a|
75
+ begin
76
+ a.on_enter_#{node_type}(node, parent, self)
77
+ rescue AnalysisError => err
78
+ @rescued_errors << err
79
+ end
80
+ end
81
+ end
82
+
83
+ def call_on_leave_#{node_type}(node, parent)
84
+ @analyzers.each do |a|
85
+ begin
86
+ a.on_leave_#{node_type}(node, parent, self)
87
+ rescue AnalysisError => err
88
+ @rescued_errors << err
89
+ end
90
+ end
91
+ end
92
+
93
+ RUBY
94
+ end
95
+
96
+ def on_operation_definition(node, parent)
97
+ object_type = @schema.root_type_for_operation(node.operation_type)
98
+ @object_types.push(object_type)
99
+ @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
100
+ call_on_enter_operation_definition(node, parent)
101
+ super
102
+ call_on_leave_operation_definition(node, parent)
103
+ @object_types.pop
104
+ @path.pop
105
+ end
106
+
107
+ def on_fragment_definition(node, parent)
108
+ on_fragment_with_type(node) do
109
+ @path.push("fragment #{node.name}")
110
+ @in_fragment_def = false
111
+ call_on_enter_fragment_definition(node, parent)
112
+ super
113
+ @in_fragment_def = false
114
+ call_on_leave_fragment_definition(node, parent)
115
+ end
116
+ end
117
+
118
+ def on_inline_fragment(node, parent)
119
+ on_fragment_with_type(node) do
120
+ @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
121
+ @skipping = @skip_stack.last || skip?(node)
122
+ @skip_stack << @skipping
123
+
124
+ call_on_enter_inline_fragment(node, parent)
125
+ super
126
+ @skipping = @skip_stack.pop
127
+ call_on_leave_inline_fragment(node, parent)
128
+ end
129
+ end
130
+
131
+ def on_field(node, parent)
132
+ @response_path.push(node.alias || node.name)
133
+ parent_type = @object_types.last
134
+ # This could be nil if the previous field wasn't found:
135
+ field_definition = parent_type && @types.field(parent_type, node.name)
136
+ @field_definitions.push(field_definition)
137
+ if !field_definition.nil?
138
+ next_object_type = field_definition.type.unwrap
139
+ @object_types.push(next_object_type)
140
+ else
141
+ @object_types.push(nil)
142
+ end
143
+ @path.push(node.alias || node.name)
144
+
145
+ @skipping = @skip_stack.last || skip?(node)
146
+ @skip_stack << @skipping
147
+
148
+ call_on_enter_field(node, parent)
149
+ super
150
+ @skipping = @skip_stack.pop
151
+ call_on_leave_field(node, parent)
152
+ @response_path.pop
153
+ @field_definitions.pop
154
+ @object_types.pop
155
+ @path.pop
156
+ end
157
+
158
+ def on_directive(node, parent)
159
+ directive_defn = @schema.directives[node.name]
160
+ @directive_definitions.push(directive_defn)
161
+ call_on_enter_directive(node, parent)
162
+ super
163
+ call_on_leave_directive(node, parent)
164
+ @directive_definitions.pop
165
+ end
166
+
167
+ def on_argument(node, parent)
168
+ argument_defn = if (arg = @argument_definitions.last)
169
+ arg_type = arg.type.unwrap
170
+ if arg_type.kind.input_object?
171
+ @types.argument(arg_type, node.name)
172
+ else
173
+ nil
174
+ end
175
+ elsif (directive_defn = @directive_definitions.last)
176
+ @types.argument(directive_defn, node.name)
177
+ elsif (field_defn = @field_definitions.last)
178
+ @types.argument(field_defn, node.name)
179
+ else
180
+ nil
181
+ end
182
+
183
+ @argument_definitions.push(argument_defn)
184
+ @path.push(node.name)
185
+ call_on_enter_argument(node, parent)
186
+ super
187
+ call_on_leave_argument(node, parent)
188
+ @argument_definitions.pop
189
+ @path.pop
190
+ end
191
+
192
+ def on_fragment_spread(node, parent)
193
+ @path.push("... #{node.name}")
194
+ @skipping = @skip_stack.last || skip?(node)
195
+ @skip_stack << @skipping
196
+
197
+ call_on_enter_fragment_spread(node, parent)
198
+ enter_fragment_spread_inline(node)
199
+ super
200
+ @skipping = @skip_stack.pop
201
+ leave_fragment_spread_inline(node)
202
+ call_on_leave_fragment_spread(node, parent)
203
+ @path.pop
204
+ end
205
+
206
+ # @return [GraphQL::BaseType] The current object type
207
+ def type_definition
208
+ @object_types.last
209
+ end
210
+
211
+ # @return [GraphQL::BaseType] The type which the current type came from
212
+ def parent_type_definition
213
+ @object_types[-2]
214
+ end
215
+
216
+ # @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
217
+ def field_definition
218
+ @field_definitions.last
219
+ end
220
+
221
+ # @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to
222
+ def previous_field_definition
223
+ @field_definitions[-2]
224
+ end
225
+
226
+ # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
227
+ def directive_definition
228
+ @directive_definitions.last
229
+ end
230
+
231
+ # @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
232
+ def argument_definition
233
+ @argument_definitions.last
234
+ end
235
+
236
+ # @return [GraphQL::Argument, nil] The previous GraphQL argument
237
+ def previous_argument_definition
238
+ @argument_definitions[-2]
239
+ end
240
+
241
+ private
242
+
243
+ # Visit a fragment spread inline instead of visiting the definition
244
+ # by itself.
245
+ def enter_fragment_spread_inline(fragment_spread)
246
+ fragment_def = query.fragments[fragment_spread.name]
247
+
248
+ object_type = if fragment_def.type
249
+ @types.type(fragment_def.type.name)
250
+ else
251
+ object_types.last
252
+ end
253
+
254
+ object_types << object_type
255
+
256
+ on_fragment_definition_children(fragment_def)
257
+ end
258
+
259
+ # Visit a fragment spread inline instead of visiting the definition
260
+ # by itself.
261
+ def leave_fragment_spread_inline(_fragment_spread)
262
+ object_types.pop
263
+ end
264
+
265
+ def skip?(ast_node)
266
+ dir = ast_node.directives
267
+ dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
268
+ end
269
+
270
+ def on_fragment_with_type(node)
271
+ object_type = if node.type
272
+ @types.type(node.type.name)
273
+ else
274
+ @object_types.last
275
+ end
276
+ @object_types.push(object_type)
277
+ yield(node)
278
+ @object_types.pop
279
+ @path.pop
280
+ end
281
+ end
282
+ end
283
+ 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
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ # This module exposes Fiber-level runtime information.
5
+ #
6
+ # It won't work across unrelated fibers, although it will work in child Fibers.
7
+ #
8
+ # @example Setting Up ActiveRecord::QueryLogs
9
+ #
10
+ # config.active_record.query_log_tags = [
11
+ # :namespaced_controller,
12
+ # :action,
13
+ # :job,
14
+ # # ...
15
+ # {
16
+ # # GraphQL runtime info:
17
+ # current_graphql_operation: -> { GraphQL::Current.operation_name },
18
+ # current_graphql_field: -> { GraphQL::Current.field&.path },
19
+ # current_dataloader_source: -> { GraphQL::Current.dataloader_source_class },
20
+ # # ...
21
+ # },
22
+ # ]
23
+ #
24
+ module Current
25
+ # @return [String, nil] Comma-joined operation names for the currently-running {Multiplex}. `nil` if all operations are anonymous.
26
+ def self.operation_name
27
+ if (m = Fiber[:__graphql_current_multiplex])
28
+ m.context[:__graphql_current_operation_name] ||= begin
29
+ names = m.queries.map { |q| q.selected_operation_name }
30
+ if names.all?(&:nil?)
31
+ nil
32
+ else
33
+ names.join(",")
34
+ end
35
+ end
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ # @see GraphQL::Field#path for a string identifying this field
42
+ # @return [GraphQL::Field, nil] The currently-running field, if there is one.
43
+ def self.field
44
+ Thread.current[:__graphql_runtime_info]&.values&.first&.current_field
45
+ end
46
+
47
+ # @return [Class, nil] The currently-running {Dataloader::Source} class, if there is one.
48
+ def self.dataloader_source_class
49
+ Fiber[:__graphql_current_dataloader_source]&.class
50
+ end
51
+ end
52
+ 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?
@@ -186,8 +186,11 @@ This key should have been loaded already. This is a bug in GraphQL::Dataloader,
186
186
  ERR
187
187
  end
188
188
  result = @results[key]
189
-
190
- raise result if result.class <= StandardError
189
+ if result.is_a?(StandardError)
190
+ # Dup it because the rescuer may modify it.
191
+ # (This happens for GraphQL::ExecutionErrors, at least)
192
+ raise result.dup
193
+ end
191
194
 
192
195
  result
193
196
  end
@@ -271,7 +271,10 @@ module GraphQL
271
271
 
272
272
  if pending_sources
273
273
  spawn_fiber do
274
- pending_sources.each(&:run_pending_keys)
274
+ pending_sources.each do |source|
275
+ Fiber[:__graphql_current_dataloader_source] = source
276
+ source.run_pending_keys
277
+ end
275
278
  end
276
279
  end
277
280
  end
@@ -8,22 +8,17 @@ module GraphQL
8
8
  @query = query
9
9
  @dataloader = query.context.dataloader
10
10
  @storage = Hash.new do |h, argument_owner|
11
- args_by_parent = if argument_owner.arguments_statically_coercible?
11
+ h[argument_owner] = if argument_owner.arguments_statically_coercible?
12
12
  shared_values_cache = {}
13
13
  Hash.new do |h2, ignored_parent_object|
14
14
  h2[ignored_parent_object] = shared_values_cache
15
- end
15
+ end.compare_by_identity
16
16
  else
17
17
  Hash.new do |h2, parent_object|
18
- args_by_node = {}
19
- args_by_node.compare_by_identity
20
- h2[parent_object] = args_by_node
21
- end
18
+ h2[parent_object] = {}.compare_by_identity
19
+ end.compare_by_identity
22
20
  end
23
- args_by_parent.compare_by_identity
24
- h[argument_owner] = args_by_parent
25
- end
26
- @storage.compare_by_identity
21
+ end.compare_by_identity
27
22
  end
28
23
 
29
24
  def fetch(ast_node, argument_owner, parent_object)
@@ -53,8 +53,7 @@ module GraphQL
53
53
  end
54
54
  end
55
55
  # { Class => Boolean }
56
- @lazy_cache = {}
57
- @lazy_cache.compare_by_identity
56
+ @lazy_cache = {}.compare_by_identity
58
57
  end
59
58
 
60
59
  def final_result
@@ -160,9 +159,9 @@ module GraphQL
160
159
  case node
161
160
  when GraphQL::Language::Nodes::InlineFragment
162
161
  if node.type
163
- type_defn = schema.get_type(node.type.name, context)
162
+ type_defn = query.types.type(node.type.name)
164
163
 
165
- if query.warden.possible_types(type_defn).include?(owner_type)
164
+ if query.types.possible_types(type_defn).include?(owner_type)
166
165
  result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
167
166
  if !result.equal?(next_selections)
168
167
  selections_to_run = result
@@ -177,8 +176,8 @@ module GraphQL
177
176
  end
178
177
  when GraphQL::Language::Nodes::FragmentSpread
179
178
  fragment_def = query.fragments[node.name]
180
- type_defn = query.get_type(fragment_def.type.name)
181
- if query.warden.possible_types(type_defn).include?(owner_type)
179
+ type_defn = query.types.type(fragment_def.type.name)
180
+ if query.types.possible_types(type_defn).include?(owner_type)
182
181
  result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
183
182
  if !result.equal?(next_selections)
184
183
  selections_to_run = result
@@ -245,7 +244,7 @@ module GraphQL
245
244
  end
246
245
  field_name = ast_node.name
247
246
  owner_type = selections_result.graphql_result_type
248
- field_defn = query.warden.get_field(owner_type, field_name)
247
+ field_defn = query.types.field(owner_type, field_name)
249
248
 
250
249
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
251
250
  runtime_state = get_current_runtime_state
@@ -579,7 +578,7 @@ module GraphQL
579
578
  resolved_value = value
580
579
  end
581
580
 
582
- possible_types = query.possible_types(current_type)
581
+ possible_types = query.types.possible_types(current_type)
583
582
  if !possible_types.include?(resolved_type)
584
583
  parent_type = field.owner_type
585
584
  err_class = current_type::UnresolvedTypeError
@@ -727,12 +726,7 @@ module GraphQL
727
726
  end
728
727
 
729
728
  def get_current_runtime_state
730
- current_state = Thread.current[:__graphql_runtime_info] ||= begin
731
- per_query_state = {}
732
- per_query_state.compare_by_identity
733
- per_query_state
734
- end
735
-
729
+ current_state = Thread.current[:__graphql_runtime_info] ||= {}.compare_by_identity
736
730
  current_state[@query] ||= CurrentState.new
737
731
  end
738
732
 
@@ -34,13 +34,14 @@ module GraphQL
34
34
  end
35
35
 
36
36
  multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
37
+ Fiber[:__graphql_current_multiplex] = multiplex
37
38
  multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
38
39
  schema = multiplex.schema
39
40
  queries = multiplex.queries
40
41
  lazies_at_depth = Hash.new { |h, k| h[k] = [] }
41
42
  multiplex_analyzers = schema.multiplex_analyzers
42
43
  if multiplex.max_complexity
43
- multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
44
+ multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
44
45
  end
45
46
 
46
47
  schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
@@ -136,6 +137,7 @@ module GraphQL
136
137
  queries.map { |q| q.result_values ||= {} }
137
138
  raise
138
139
  ensure
140
+ Fiber[:__graphql_current_multiplex] = nil
139
141
  queries.map { |query|
140
142
  runtime = query.context.namespace(:interpreter_runtime)[:runtime]
141
143
  if runtime
@@ -108,16 +108,16 @@ module GraphQL
108
108
  def selection(field_name, selected_type: @selected_type, arguments: nil)
109
109
  next_field_defn = case field_name
110
110
  when String
111
- @query.get_field(selected_type, field_name)
111
+ @query.types.field(selected_type, field_name)
112
112
  when Symbol
113
113
  # Try to avoid the `.to_s` below, if possible
114
114
  all_fields = if selected_type.kind.fields?
115
- @query.warden.fields(selected_type)
115
+ @query.types.fields(selected_type)
116
116
  else
117
117
  # Handle unions by checking possible
118
- @query.warden
118
+ @query.types
119
119
  .possible_types(selected_type)
120
- .map { |t| @query.warden.fields(t) }
120
+ .map { |t| @query.types.fields(t) }
121
121
  .tap(&:flatten!)
122
122
  end
123
123
 
@@ -128,7 +128,7 @@ module GraphQL
128
128
  # Symbol#name is only present on 3.0+
129
129
  sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s
130
130
  guessed_name = Schema::Member::BuildType.camelize(sym_s)
131
- @query.get_field(selected_type, guessed_name)
131
+ @query.types.field(selected_type, guessed_name)
132
132
  end
133
133
  end
134
134
  lookahead_for_selection(next_field_defn, selected_type, arguments)
@@ -144,7 +144,7 @@ module GraphQL
144
144
  alias_node = lookup_alias_node(ast_nodes, alias_name)
145
145
  return NULL_LOOKAHEAD unless alias_node
146
146
 
147
- next_field_defn = @query.get_field(selected_type, alias_node.name)
147
+ next_field_defn = @query.types.field(selected_type, alias_node.name)
148
148
 
149
149
  alias_arguments = @query.arguments_for(alias_node, next_field_defn)
150
150
  if alias_arguments.is_a?(::GraphQL::Execution::Interpreter::Arguments)
@@ -183,7 +183,7 @@ module GraphQL
183
183
 
184
184
  subselections_by_type.each do |type, ast_nodes_by_response_key|
185
185
  ast_nodes_by_response_key.each do |response_key, ast_nodes|
186
- field_defn = @query.get_field(type, ast_nodes.first.name)
186
+ field_defn = @query.types.field(type, ast_nodes.first.name)
187
187
  lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
188
188
  subselections.push(lookahead)
189
189
  end
@@ -266,7 +266,7 @@ module GraphQL
266
266
  elsif arguments.nil? || arguments.empty?
267
267
  selections_on_type[response_key] = [ast_selection]
268
268
  else
269
- field_defn = @query.get_field(selected_type, ast_selection.name)
269
+ field_defn = @query.types.field(selected_type, ast_selection.name)
270
270
  if arguments_match?(arguments, field_defn, ast_selection)
271
271
  selections_on_type[response_key] = [ast_selection]
272
272
  end
@@ -276,14 +276,14 @@ module GraphQL
276
276
  subselections_on_type = selections_on_type
277
277
  if (t = ast_selection.type)
278
278
  # Assuming this is valid, that `t` will be found.
279
- on_type = @query.get_type(t.name)
279
+ on_type = @query.types.type(t.name)
280
280
  subselections_on_type = subselections_by_type[on_type] ||= {}
281
281
  end
282
282
  find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
283
283
  when GraphQL::Language::Nodes::FragmentSpread
284
284
  frag_defn = lookup_fragment(ast_selection)
285
285
  # Again, assuming a valid AST
286
- on_type = @query.get_type(frag_defn.type.name)
286
+ on_type = @query.types.type(frag_defn.type.name)
287
287
  subselections_on_type = subselections_by_type[on_type] ||= {}
288
288
  find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
289
289
  else
@@ -22,7 +22,7 @@ module GraphQL
22
22
  field :is_repeatable, Boolean, method: :repeatable?
23
23
 
24
24
  def args(include_deprecated:)
25
- args = @context.warden.arguments(@object)
25
+ args = @context.types.arguments(@object)
26
26
  args = args.reject(&:deprecation_reason) unless include_deprecated
27
27
  args
28
28
  end
@@ -15,8 +15,8 @@ module GraphQL
15
15
  end
16
16
 
17
17
  def __type(name:)
18
- if context.warden.reachable_type?(name)
19
- context.warden.get_type(name)
18
+ if context.types.reachable_type?(name) && (type = context.types.type(name))
19
+ type
20
20
  elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
21
21
  type
22
22
  else