graphql 2.5.23 → 2.6.3

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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/query_complexity.rb +29 -13
  3. data/lib/graphql/analysis.rb +20 -13
  4. data/lib/graphql/backtrace/table.rb +10 -1
  5. data/lib/graphql/current.rb +7 -1
  6. data/lib/graphql/dataloader.rb +1 -1
  7. data/lib/graphql/execution/directive_checks.rb +2 -0
  8. data/lib/graphql/execution/field_resolve_step.rb +744 -0
  9. data/lib/graphql/execution/finalize.rb +230 -0
  10. data/lib/graphql/execution/input_values.rb +333 -0
  11. data/lib/graphql/execution/interpreter/arguments_cache.rb +3 -0
  12. data/lib/graphql/execution/interpreter/handles_raw_value.rb +6 -0
  13. data/lib/graphql/execution/interpreter/runtime.rb +36 -15
  14. data/lib/graphql/execution/load_argument_step.rb +102 -0
  15. data/lib/graphql/execution/next.rb +42 -16
  16. data/lib/graphql/execution/prepare_object_step.rb +147 -0
  17. data/lib/graphql/execution/resolve_type_step.rb +27 -0
  18. data/lib/graphql/execution/runner.rb +445 -0
  19. data/lib/graphql/execution/selections_step.rb +91 -0
  20. data/lib/graphql/execution.rb +10 -3
  21. data/lib/graphql/execution_error.rb +7 -13
  22. data/lib/graphql/introspection/entry_points.rb +2 -2
  23. data/lib/graphql/introspection/schema_type.rb +6 -2
  24. data/lib/graphql/language/lexer.rb +12 -8
  25. data/lib/graphql/language/parser.rb +1 -1
  26. data/lib/graphql/language.rb +8 -2
  27. data/lib/graphql/pagination/connections.rb +1 -3
  28. data/lib/graphql/query/context.rb +6 -0
  29. data/lib/graphql/query/partial.rb +18 -3
  30. data/lib/graphql/query.rb +12 -3
  31. data/lib/graphql/runtime_error.rb +6 -0
  32. data/lib/graphql/schema/argument.rb +3 -3
  33. data/lib/graphql/schema/build_from_definition.rb +10 -0
  34. data/lib/graphql/schema/directive/feature.rb +4 -0
  35. data/lib/graphql/schema/directive/transform.rb +20 -0
  36. data/lib/graphql/schema/directive.rb +23 -9
  37. data/lib/graphql/schema/field/connection_extension.rb +2 -15
  38. data/lib/graphql/schema/field/scope_extension.rb +0 -4
  39. data/lib/graphql/schema/field.rb +20 -20
  40. data/lib/graphql/schema/field_extension.rb +11 -41
  41. data/lib/graphql/schema/has_single_input_argument.rb +24 -13
  42. data/lib/graphql/schema/input_object.rb +4 -0
  43. data/lib/graphql/schema/interface.rb +26 -0
  44. data/lib/graphql/schema/introspection_system.rb +6 -21
  45. data/lib/graphql/schema/list.rb +4 -0
  46. data/lib/graphql/schema/member/base_dsl_methods.rb +0 -10
  47. data/lib/graphql/schema/printer.rb +1 -1
  48. data/lib/graphql/schema/ractor_shareable.rb +1 -0
  49. data/lib/graphql/schema/relay_classic_mutation.rb +16 -2
  50. data/lib/graphql/schema/resolver.rb +30 -14
  51. data/lib/graphql/schema/subscription.rb +53 -8
  52. data/lib/graphql/schema/timeout.rb +2 -2
  53. data/lib/graphql/schema/validator/allow_blank_validator.rb +3 -3
  54. data/lib/graphql/schema/validator/allow_null_validator.rb +3 -3
  55. data/lib/graphql/schema/validator/exclusion_validator.rb +2 -2
  56. data/lib/graphql/schema/validator/format_validator.rb +3 -3
  57. data/lib/graphql/schema/validator/inclusion_validator.rb +2 -2
  58. data/lib/graphql/schema/validator/length_validator.rb +6 -6
  59. data/lib/graphql/schema/validator/numericality_validator.rb +19 -19
  60. data/lib/graphql/schema/validator/required_validator.rb +6 -4
  61. data/lib/graphql/schema/validator.rb +9 -0
  62. data/lib/graphql/schema/visibility/profile.rb +6 -4
  63. data/lib/graphql/schema/visibility/visit.rb +1 -1
  64. data/lib/graphql/schema/visibility.rb +30 -22
  65. data/lib/graphql/schema.rb +43 -20
  66. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +31 -25
  67. data/lib/graphql/subscriptions/event.rb +0 -1
  68. data/lib/graphql/subscriptions.rb +15 -0
  69. data/lib/graphql/tracing/perfetto_trace.rb +5 -3
  70. data/lib/graphql/tracing/trace.rb +6 -0
  71. data/lib/graphql/unauthorized_error.rb +1 -1
  72. data/lib/graphql/version.rb +1 -1
  73. data/lib/graphql.rb +1 -3
  74. metadata +11 -7
  75. data/lib/graphql/execution/next/field_resolve_step.rb +0 -743
  76. data/lib/graphql/execution/next/load_argument_step.rb +0 -64
  77. data/lib/graphql/execution/next/prepare_object_step.rb +0 -129
  78. data/lib/graphql/execution/next/runner.rb +0 -411
  79. data/lib/graphql/execution/next/selections_step.rb +0 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 724e526bb33dee5a8b39cb04b4d42f2e969e45aafe73581c5bdd02177cfa1347
4
- data.tar.gz: f211ca87d49c5077b0ae4744ce5956795835a6405a07e8782970cde14d172d7e
3
+ metadata.gz: 16abc9c2a5eda0da251dbe7e14433241bc7d038d2527926832ceb29336fb857a
4
+ data.tar.gz: c89bd2a4b340ca30bfa7b823f51e4671972355d54bd56fdaed598c13a415c3b3
5
5
  SHA512:
6
- metadata.gz: 5d9c249f5d7bbe3ef06070cf52b04586eedf2df06f7c1fc95c6ab47bdc8182218fff2281d76a3f2a18bef4f1ad9c508901e6c7a3f882d28390afdd2f4dca6fd0
7
- data.tar.gz: 5179a34bc5c34769021fab1dff6384c22f73f776dc994a82f9bce635ddf6ead05ec0a3d2ff37bfcedbc892d87d2d7a82b08eab4e4cf319ee390e2c5a34db76fa
6
+ metadata.gz: 6452d2a517c502b4a8934582d060885a5f6462de244a1f331433f1076dc57a879cdd081e147f70925064a99607e0794fcf6eadad7a3f4a9111a5ab979c552554
7
+ data.tar.gz: b6a0219606d905fc885ab2192596d1acb1d92bce407defc751bfafb1a7199e9c32275c1d3202f3246251e72b80554698b1f54920f76178f1ddc81b41d628bacd
@@ -9,6 +9,8 @@ module GraphQL
9
9
  super
10
10
  @skip_introspection_fields = !query.schema.max_complexity_count_introspection_fields
11
11
  @complexities_on_type_by_query = {}
12
+ @intersect_cache = Hash.new { |h, k| h[k] = {}.compare_by_identity }.compare_by_identity
13
+ @possible_types_cache = {}.compare_by_identity
12
14
  end
13
15
 
14
16
  # Override this method to use the complexity result
@@ -159,8 +161,22 @@ module GraphQL
159
161
 
160
162
  def types_intersect?(query, a, b)
161
163
  return true if a == b
162
- a_types = query.types.possible_types(a)
163
- query.types.possible_types(b).any? { |t| a_types.include?(t) }
164
+
165
+ if a.object_id < b.object_id
166
+ first_cache = @intersect_cache[a]
167
+ second_key = b
168
+ else
169
+ first_cache = @intersect_cache[b]
170
+ second_key = a
171
+ end
172
+
173
+ if first_cache.key?(second_key)
174
+ first_cache[second_key]
175
+ else
176
+ a_types = @possible_types_cache[a] ||= query.types.possible_types(a).to_set
177
+ b_types = @possible_types_cache[b] ||= query.types.possible_types(b).to_set
178
+ first_cache[second_key] = a_types.intersect?(b_types)
179
+ end
164
180
  end
165
181
 
166
182
  # A hook which is called whenever a field's max complexity is calculated.
@@ -175,18 +191,16 @@ module GraphQL
175
191
  # @param inner_selections [Array<Hash<String, ScopedTypeComplexity>>] Field selections for a scope
176
192
  # @return [Integer] Total complexity value for all these selections in the parent scope
177
193
  def merged_max_complexity(query, inner_selections)
178
- # Aggregate a set of all unique field selection keys across all scopes.
179
- # Use a hash, but ignore the values; it's just a fast way to work with the keys.
180
- unique_field_keys = inner_selections.each_with_object({}) do |inner_selection, memo|
181
- memo.merge!(inner_selection)
194
+ child_scopes_by_key = {}
195
+ inner_selections.each do |inner_selection|
196
+ inner_selection.each do |k, v|
197
+ scopes = child_scopes_by_key[k] ||= []
198
+ scopes << v
199
+ end
182
200
  end
183
-
184
201
  # Add up the total cost for each unique field name's coalesced selections
185
- unique_field_keys.each_key.reduce(0) do |total, field_key|
186
- # Collect all child scopes for this field key;
187
- # all keys come with at least one scope.
188
- child_scopes = inner_selections.filter_map { _1[field_key] }
189
-
202
+ total = 0
203
+ child_scopes_by_key.each do |field_key, child_scopes|
190
204
  # Compute maximum possible cost of child selections;
191
205
  # composites merge their maximums, while leaf scopes are always zero.
192
206
  # FieldsWillMerge validation assures all scopes are uniformly composite or leaf.
@@ -214,8 +228,10 @@ module GraphQL
214
228
  child_complexity: maximum_children_cost,
215
229
  )
216
230
 
217
- total + maximum_cost
231
+ total += maximum_cost
218
232
  end
233
+
234
+ total
219
235
  end
220
236
 
221
237
  def legacy_merged_max_complexity(query, inner_selections)
@@ -40,13 +40,13 @@ module GraphQL
40
40
  end
41
41
  end
42
42
 
43
- multiplex_results = multiplex_analyzers.map(&:result)
44
- multiplex_errors = analysis_errors(multiplex_results)
45
43
 
44
+ multiplex_analyzers.map!(&:result)
45
+ multiplex_errors = analysis_errors(EmptyObjects::EMPTY_ARRAY, multiplex_analyzers)
46
46
  multiplex.queries.each_with_index do |query, idx|
47
- query.analysis_errors = multiplex_errors + analysis_errors(query_results[idx])
47
+ query.analysis_errors = analysis_errors(multiplex_errors, query_results[idx])
48
48
  end
49
- multiplex_results
49
+ multiplex_analyzers
50
50
  end
51
51
  end
52
52
 
@@ -55,13 +55,11 @@ module GraphQL
55
55
  # @return [Array<Any>] Results from those analyzers
56
56
  def analyze_query(query, analyzers, multiplex_analyzers: [])
57
57
  query.current_trace.analyze_query(query: query) do
58
- query_analyzers = analyzers
59
- .map { |analyzer| analyzer.new(query) }
60
- .tap { _1.select!(&:analyze?) }
61
-
58
+ query_analyzers = analyzers.map { |analyzer| analyzer.new(query) }
59
+ query_analyzers.select!(&:analyze?)
62
60
  analyzers_to_run = query_analyzers + multiplex_analyzers
63
- if !analyzers_to_run.empty?
64
61
 
62
+ if !analyzers_to_run.empty?
65
63
  analyzers_to_run.select!(&:visit?)
66
64
  if !analyzers_to_run.empty?
67
65
  visitor = GraphQL::Analysis::Visitor.new(
@@ -79,18 +77,27 @@ module GraphQL
79
77
 
80
78
  query_analyzers.map(&:result)
81
79
  else
82
- []
80
+ EmptyObjects::EMPTY_ARRAY
83
81
  end
84
82
  end
85
83
  rescue TimeoutError => err
86
84
  [err]
87
85
  rescue GraphQL::UnauthorizedError, GraphQL::ExecutionError
88
86
  # This error was raised during analysis and will be returned the client before execution
89
- []
87
+ EmptyObjects::EMPTY_ARRAY
90
88
  end
91
89
 
92
- def analysis_errors(results)
93
- results.flatten.tap { _1.select! { |r| r.is_a?(GraphQL::AnalysisError) } }
90
+ def analysis_errors(parent_errors, results)
91
+ if !results.empty?
92
+ results = results.flatten
93
+ results.select! { |r| r.is_a?(GraphQL::AnalysisError) }
94
+ end
95
+
96
+ if parent_errors.empty?
97
+ results
98
+ else
99
+ parent_errors + results
100
+ end
94
101
  end
95
102
  end
96
103
  end
@@ -90,7 +90,16 @@ module GraphQL
90
90
 
91
91
  if ast_node
92
92
  field_defn = query.get_field(result.graphql_result_type, ast_node.name)
93
- args = query.arguments_for(ast_node, field_defn).to_h
93
+ args = begin
94
+ if (cached_args = query.arguments_cache.cached_arguments_for(ast_node, field_defn))
95
+ cached_args.to_h
96
+ else
97
+ EmptyObjects::EMPTY_HASH
98
+ end
99
+ rescue StandardError => err
100
+ "Failed to load arguments, #{err.class}: #{err.message}"
101
+ end
102
+
94
103
  field_path = field_defn.path
95
104
  if ast_node.alias
96
105
  field_path += " as #{ast_node.alias}"
@@ -41,7 +41,13 @@ module GraphQL
41
41
  # @see GraphQL::Field#path for a string identifying this field
42
42
  # @return [GraphQL::Field, nil] The currently-running field, if there is one.
43
43
  def self.field
44
- Fiber[:__graphql_runtime_info]&.values&.first&.current_field
44
+ if (interpreter_info = Fiber[:__graphql_runtime_info])
45
+ interpreter_info.values&.first&.current_field
46
+ elsif (field = Fiber[:__graphql_current_field])
47
+ field
48
+ else
49
+ nil
50
+ end
45
51
  end
46
52
 
47
53
  # @return [Class, nil] The currently-running {Dataloader::Source} class, if there is one.
@@ -108,7 +108,7 @@ module GraphQL
108
108
  # @param batch_parameters [Array<Object>]
109
109
  # @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
110
110
  # and cached for the lifetime of this {Multiplex}.
111
- if RUBY_VERSION < "3" || RUBY_ENGINE != "ruby" # truffle-ruby wasn't doing well with the implementation below
111
+ if (RUBY_ENGINE == "ruby" && RUBY_ENGINE < "3") || RUBY_ENGINE == "truffleruby" # truffle-ruby wasn't doing well with the implementation below
112
112
  def with(source_class, *batch_args)
113
113
  batch_key = source_class.batch_key_for(*batch_args)
114
114
  @source_cache[source_class][batch_key] ||= begin
@@ -18,11 +18,13 @@ module GraphQL
18
18
  case name
19
19
  when SKIP
20
20
  args = query.arguments_for(directive_ast_node, directive_defn)
21
+ next if args.is_a?(GraphQL::ExecutionError)
21
22
  if args[:if] == true
22
23
  return false
23
24
  end
24
25
  when INCLUDE
25
26
  args = query.arguments_for(directive_ast_node, directive_defn)
27
+ next if args.is_a?(GraphQL::ExecutionError)
26
28
  if args[:if] == false
27
29
  return false
28
30
  end