graphql 2.3.5 → 2.3.14

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.

Files changed (99) 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 +29 -25
  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/rubocop/graphql/field_type_in_block.rb +129 -0
  35. data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
  36. data/lib/graphql/rubocop.rb +2 -0
  37. data/lib/graphql/schema/addition.rb +1 -0
  38. data/lib/graphql/schema/always_visible.rb +1 -0
  39. data/lib/graphql/schema/argument.rb +19 -5
  40. data/lib/graphql/schema/build_from_definition.rb +8 -1
  41. data/lib/graphql/schema/directive/flagged.rb +1 -1
  42. data/lib/graphql/schema/directive.rb +2 -0
  43. data/lib/graphql/schema/enum.rb +51 -20
  44. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  45. data/lib/graphql/schema/field.rb +85 -39
  46. data/lib/graphql/schema/has_single_input_argument.rb +2 -1
  47. data/lib/graphql/schema/input_object.rb +8 -7
  48. data/lib/graphql/schema/interface.rb +20 -4
  49. data/lib/graphql/schema/introspection_system.rb +5 -16
  50. data/lib/graphql/schema/member/has_arguments.rb +14 -9
  51. data/lib/graphql/schema/member/has_fields.rb +8 -6
  52. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  53. data/lib/graphql/schema/resolver.rb +5 -5
  54. data/lib/graphql/schema/subset.rb +509 -0
  55. data/lib/graphql/schema/type_expression.rb +2 -2
  56. data/lib/graphql/schema/types_migration.rb +187 -0
  57. data/lib/graphql/schema/validator/all_validator.rb +62 -0
  58. data/lib/graphql/schema/validator.rb +2 -0
  59. data/lib/graphql/schema/warden.rb +89 -5
  60. data/lib/graphql/schema.rb +109 -53
  61. data/lib/graphql/static_validation/base_visitor.rb +6 -5
  62. data/lib/graphql/static_validation/literal_validator.rb +4 -4
  63. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  64. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  65. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  66. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  67. data/lib/graphql/static_validation/rules/fields_will_merge.rb +7 -7
  68. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  69. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  70. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  71. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  72. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  73. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -3
  74. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
  75. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  76. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  77. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  78. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  79. data/lib/graphql/static_validation/validation_context.rb +2 -2
  80. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  81. data/lib/graphql/subscriptions/event.rb +1 -1
  82. data/lib/graphql/subscriptions.rb +3 -3
  83. data/lib/graphql/testing/helpers.rb +8 -5
  84. data/lib/graphql/types/relay/connection_behaviors.rb +10 -0
  85. data/lib/graphql/types/relay/edge_behaviors.rb +10 -0
  86. data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
  87. data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
  88. data/lib/graphql/version.rb +1 -1
  89. data/lib/graphql.rb +3 -0
  90. metadata +31 -13
  91. data/lib/graphql/analysis/ast/analyzer.rb +0 -91
  92. data/lib/graphql/analysis/ast/field_usage.rb +0 -84
  93. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  94. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  95. data/lib/graphql/analysis/ast/query_complexity.rb +0 -185
  96. data/lib/graphql/analysis/ast/visitor.rb +0 -284
  97. data/lib/graphql/analysis/ast.rb +0 -94
  98. data/lib/graphql/language/token.rb +0 -34
  99. 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
@@ -208,22 +207,32 @@ module GraphQL
208
207
  finished_jobs = 0
209
208
  enqueued_jobs = gathered_selections.size
210
209
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
211
- @dataloader.append_job {
212
- evaluate_selection(
213
- result_name, field_ast_nodes_or_ast_node, selections_result
214
- )
215
- finished_jobs += 1
216
- if target_result && finished_jobs == enqueued_jobs
217
- selections_result.merge_into(target_result)
218
- end
219
- }
210
+
220
211
  # Field resolution may pause the fiber,
221
212
  # so it wouldn't get to the `Resolve` call that happens below.
222
213
  # So instead trigger a run from this outer context.
223
214
  if selections_result.graphql_is_eager
224
215
  @dataloader.clear_cache
225
- @dataloader.run
226
- @dataloader.clear_cache
216
+ @dataloader.run_isolated {
217
+ evaluate_selection(
218
+ result_name, field_ast_nodes_or_ast_node, selections_result
219
+ )
220
+ finished_jobs += 1
221
+ if target_result && finished_jobs == enqueued_jobs
222
+ selections_result.merge_into(target_result)
223
+ end
224
+ @dataloader.clear_cache
225
+ }
226
+ else
227
+ @dataloader.append_job {
228
+ evaluate_selection(
229
+ result_name, field_ast_nodes_or_ast_node, selections_result
230
+ )
231
+ finished_jobs += 1
232
+ if target_result && finished_jobs == enqueued_jobs
233
+ selections_result.merge_into(target_result)
234
+ end
235
+ }
227
236
  end
228
237
  end
229
238
  selections_result
@@ -245,7 +254,7 @@ module GraphQL
245
254
  end
246
255
  field_name = ast_node.name
247
256
  owner_type = selections_result.graphql_result_type
248
- field_defn = query.warden.get_field(owner_type, field_name)
257
+ field_defn = query.types.field(owner_type, field_name)
249
258
 
250
259
  # Set this before calling `run_with_directives`, so that the directive can have the latest path
251
260
  runtime_state = get_current_runtime_state
@@ -579,7 +588,7 @@ module GraphQL
579
588
  resolved_value = value
580
589
  end
581
590
 
582
- possible_types = query.possible_types(current_type)
591
+ possible_types = query.types.possible_types(current_type)
583
592
  if !possible_types.include?(resolved_type)
584
593
  parent_type = field.owner_type
585
594
  err_class = current_type::UnresolvedTypeError
@@ -727,12 +736,7 @@ module GraphQL
727
736
  end
728
737
 
729
738
  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
-
739
+ current_state = Thread.current[:__graphql_runtime_info] ||= {}.compare_by_identity
736
740
  current_state[@query] ||= CurrentState.new
737
741
  end
738
742
 
@@ -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