graphql 2.6.10 → 2.6.11

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6c191887acba06a4030f962822e9106e9d3aa9d5550c8cd25ea76f343927f1b
4
- data.tar.gz: b984ad9a61a391b4b8a42ade3bb9c63dc79ab2b3ee987845d655dc7038df1255
3
+ metadata.gz: f449cab829c8314cf4cd1b51e225e70603f5dc16dfcb221d3204aa39bd9fe2cd
4
+ data.tar.gz: 6fb74ee40392f0663b728c76e72239596fc82271d2222e2d9170d8ad06579f6c
5
5
  SHA512:
6
- metadata.gz: 7deac13ce0bbc579609e28575a309caa46086196f1381cfc5fbd6a5770b2059e123cb047cc835c573ad76be1df264a3b0d52d8692dbf161ad0d0248f8cfed2a3
7
- data.tar.gz: 1362c0867ad8bbbe086b1999c710f447de6ea16b53b8a05a83dc39cba6c7b7d8d5cccdc388a3dff2f42c1b5135ff59a4c78dd2d53badd34b28718a73aa331b92
6
+ metadata.gz: 2820887a516c9a015774633ac126924642ceb778b523d0563af9ba766f6a3bfa5fa89ad0420c9ea3ee5208fe8fcdd58101aff1777b44de98c8c12b01fd64be0d
7
+ data.tar.gz: 90856cb6b43ac1be362d3358820857c1c8cc038f19def34a119677a6924c7b1441eca628c9e26a2134ae52aaab7507cea3521e36cad2763614909c10d8a79577
@@ -262,11 +262,12 @@ module GraphQL
262
262
 
263
263
  def spawn_fiber
264
264
  fiber_vars = get_fiber_variables
265
- Fiber.new(blocking: !@nonblocking) {
265
+ Fiber.new(blocking: !@nonblocking) do
266
266
  set_fiber_variables(fiber_vars)
267
267
  yield
268
+ ensure
268
269
  cleanup_fiber
269
- }
270
+ end
270
271
  end
271
272
 
272
273
  # Pre-warm the Dataloader cache with ActiveRecord objects which were loaded elsewhere.
@@ -359,6 +360,7 @@ module GraphQL
359
360
  while job = @pending_jobs.shift
360
361
  job.call
361
362
  end
363
+ ensure
362
364
  trace&.dataloader_fiber_exit
363
365
  end
364
366
  end
@@ -392,6 +394,7 @@ module GraphQL
392
394
  source.run_pending_keys
393
395
  trace&.end_dataloader_source(source)
394
396
  end
397
+ ensure
395
398
  trace&.dataloader_fiber_exit
396
399
  end
397
400
  end
@@ -87,10 +87,10 @@ module GraphQL
87
87
  @schema.analysis_engine.analyze_multiplex(@multiplex, multiplex_analyzers)
88
88
  trace.end_analyze_multiplex(@multiplex, multiplex_analyzers)
89
89
 
90
- results = []
90
+ results = {}.compare_by_identity
91
91
  queries.each do |query|
92
92
  if query.validate && !query.valid?
93
- results << {
93
+ results[query] = {
94
94
  "errors" => query.static_errors.map(&:to_h)
95
95
  }
96
96
  next
@@ -125,8 +125,8 @@ module GraphQL
125
125
  end
126
126
  end
127
127
 
128
- queries.each_with_index.map do |query, idx|
129
- result = results[idx]
128
+ queries.map do |query|
129
+ result = results[query]
130
130
 
131
131
  fin_result = if (!@finalizers&.key?(query) && query.context.errors.empty?) || !query.valid?
132
132
  result
@@ -155,12 +155,16 @@ module GraphQL
155
155
  end
156
156
  query.result
157
157
  end
158
- end
159
- rescue SystemStackError => err
160
- @multiplex.queries.map do |query|
161
- @schema.query_stack_error(query, err)
162
- query.result_values ||= { "errors" => query.context.errors.map(&:to_h) }
163
- query.result
158
+ rescue SystemStackError => err
159
+ queries.map do |query|
160
+ @schema.query_stack_error(query, err)
161
+ query.result_values ||= { "errors" => query.context.errors.map(&:to_h) }
162
+ query.result
163
+ end
164
+ rescue Exception
165
+ # Assign values here so that the query's `@executed` becomes true
166
+ queries.map { |q| q.result_values ||= {} }
167
+ raise
164
168
  end
165
169
  ensure
166
170
  Fiber[:__graphql_current_multiplex] = previous_multiplex
@@ -262,12 +266,12 @@ module GraphQL
262
266
  end
263
267
 
264
268
  if !auth_check
265
- results << {}
269
+ results[query] = {}
266
270
  return
267
271
  end
268
272
  end
269
273
 
270
- results << { "data" => data }
274
+ results[query] = { "data" => data }
271
275
  objects = [root_value]
272
276
  query.current_trace.objects(root_type, objects, query.context)
273
277
 
@@ -361,7 +365,7 @@ module GraphQL
361
365
  objects = [root_value]
362
366
  query.current_trace.objects(resolved_type, objects, query.context)
363
367
  runtime_type_at[data] = resolved_type
364
- results << { "data" => data }
368
+ results[query] = { "data" => data }
365
369
  isolated_steps[0] << SelectionsStep.new(
366
370
  parent_type: resolved_type,
367
371
  field_resolve_step: nil,
@@ -376,10 +380,10 @@ module GraphQL
376
380
  inner_type = root_type.unwrap
377
381
  case inner_type.kind.name
378
382
  when "SCALAR", "ENUM"
379
- results << run_isolated_scalar(root_type, query)
383
+ results[query] = run_isolated_scalar(root_type, query)
380
384
  else
381
385
  list_result = Array.new(root_value.size) { Hash.new.compare_by_identity }
382
- results << { "data" => list_result }
386
+ results[query] = { "data" => list_result }
383
387
  isolated_steps[0] << SelectionsStep.new(
384
388
  parent_type: inner_type,
385
389
  field_resolve_step: nil,
@@ -392,7 +396,7 @@ module GraphQL
392
396
  )
393
397
  end
394
398
  when "SCALAR", "ENUM"
395
- results << run_isolated_scalar(root_type, query)
399
+ results[query] = run_isolated_scalar(root_type, query)
396
400
  else
397
401
  raise "Unhandled root type kind: #{root_type.kind.name.inspect}"
398
402
  end
@@ -425,7 +429,7 @@ module GraphQL
425
429
  key = dummy_path.pop
426
430
  is_from_array = key.is_a?(Integer)
427
431
 
428
- if lazy?(value)
432
+ if resolves_lazies && lazy?(value)
429
433
  value = @schema.sync_lazy(value)
430
434
  end
431
435
  selections = partial.ast_nodes
@@ -27,7 +27,11 @@ module GraphQL
27
27
  private
28
28
 
29
29
  def index_from_cursor(cursor)
30
- decode(cursor).to_i
30
+ index = Integer(decode(cursor), 10, exception: false)
31
+ if index.nil? || index <= 0
32
+ raise GraphQL::ExecutionError, "Invalid cursor: #{cursor.inspect}"
33
+ end
34
+ [index, items.length + 1].min
31
35
  end
32
36
 
33
37
  # Populate all the pagination info _once_,
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/pagination/connection"
3
+ require "monitor"
3
4
 
4
5
  module GraphQL
5
6
  module Pagination
@@ -178,6 +179,17 @@ module GraphQL
178
179
  # Apply `first` and `last` to `sliced_nodes`,
179
180
  # returning a new relation
180
181
  def limited_nodes
182
+ return @limited_nodes if @limited_nodes
183
+ if async_dataloader?
184
+ (@load_monitor ||= Monitor.new).synchronize do
185
+ build_limited_nodes
186
+ end
187
+ else
188
+ build_limited_nodes
189
+ end
190
+ end
191
+
192
+ def build_limited_nodes
181
193
  @limited_nodes ||= begin
182
194
  calculate_sliced_nodes_parameters
183
195
  if @sliced_nodes_null_relation
@@ -217,15 +229,19 @@ module GraphQL
217
229
  end
218
230
  end
219
231
 
232
+ def async_dataloader?
233
+ @context&.[](:dataloader).is_a?(GraphQL::Dataloader::AsyncDataloader)
234
+ end
235
+
220
236
  # Load nodes after applying first/last/before/after,
221
237
  # returns an array of nodes
222
238
  def load_nodes
223
239
  # Return an array so we can consistently use `.index(node)` on it
224
240
  return @nodes if @nodes
225
- if (@context[:dataloader].is_a?(GraphQL::Dataloader::AsyncDataloader))
241
+ if async_dataloader?
226
242
  # `AsyncDataloader` may resolve sibling fields (eg, `edges` and `pageInfo`)
227
243
  # in separate Fibers, so several callers can get here before `@nodes` is set.
228
- (@load_lock ||= Mutex.new).synchronize do
244
+ (@load_monitor ||= Monitor.new).synchronize do
229
245
  @nodes ||= limited_nodes.to_a
230
246
  end
231
247
  else
data/lib/graphql/query.rb CHANGED
@@ -287,7 +287,8 @@ module GraphQL
287
287
  # @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
288
288
  def result
289
289
  if !@executed
290
- Execution::Interpreter.run_all(@schema, [self], context: @context)
290
+ execution_engine = @schema.default_execution_next ? Execution::Next : Execution::Interpreter
291
+ execution_engine.run_all(@schema, [self], context: @context)
291
292
  end
292
293
  @result ||= Query::Result.new(query: self, values: @result_values)
293
294
  end
@@ -84,6 +84,11 @@ module GraphQL
84
84
  yield
85
85
  end
86
86
 
87
+ def resolve_field(...); end
88
+ def resolve_fragment_spread(...); end
89
+ def resolve_inline_fragment(...); end
90
+ def resolve_operation(...); end
91
+
87
92
  def validate!(arguments, context)
88
93
  Schema::Validator.validate!(validators, self, context, arguments)
89
94
  end
@@ -397,7 +397,7 @@ module GraphQL
397
397
  # This union/interface is used in `loads:` but not otherwise visible to this query
398
398
  context.types.loadable_possible_types(arg_loads_type, context).include?(application_object_type)
399
399
  else
400
- true
400
+ arg_loads_type == application_object_type
401
401
  end
402
402
  else
403
403
  context.types.possible_types(arg_loads_type).include?(application_object_type)
@@ -287,8 +287,12 @@ module GraphQL
287
287
  end
288
288
  elsif type_defn.kind.enum?
289
289
  enum_values(type_defn)
290
+ elsif type_defn.kind.union?
291
+ loadable_possible_types(type_defn, @context)
292
+ elsif type_defn.kind.object? || type_defn.kind.interface?
293
+ interfaces(type_defn)
290
294
  end
291
- # Lots more to do here
295
+ possible_types(type_defn)
292
296
  end
293
297
  if @schema.query
294
298
  @schema.introspection_system.entry_points.each do |f|
@@ -304,6 +308,11 @@ module GraphQL
304
308
  end
305
309
  end
306
310
 
311
+ directives.each do |directive|
312
+ arguments(directive).each do |directive_argument|
313
+ argument(directive, directive_argument.graphql_name)
314
+ end
315
+ end
307
316
  end
308
317
 
309
318
  private
@@ -13,6 +13,8 @@ module GraphQL
13
13
  TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%z" # eg '2020-01-01 23:59:59.123456789+05:00'
14
14
  TIMESTAMP_CLASS_NAMES = ["Date", "DateTime", "Time"].freeze
15
15
  OPEN_STRUCT_KEY = "__ostruct__"
16
+ HASH_KEY = "__graphql_hash__"
17
+ RESERVED_KEYS = [GLOBALID_KEY, SYMBOL_KEY, SYMBOL_KEYS_KEY, TIMESTAMP_KEY, OPEN_STRUCT_KEY, HASH_KEY].freeze
16
18
 
17
19
  module_function
18
20
 
@@ -57,7 +59,7 @@ module GraphQL
57
59
  # @return [Object] An object that load Global::Identification recursive
58
60
  def load_value(value)
59
61
  if value.is_a?(Array)
60
- is_gids = (v1 = value[0]).is_a?(Hash) && v1.size == 1 && v1[GLOBALID_KEY]
62
+ is_gids = !value.empty? && value.all? { |v| v.is_a?(Hash) && v.size == 1 && v[GLOBALID_KEY] }
61
63
  if is_gids
62
64
  # Assume it's an array of global IDs
63
65
  ids = value.map { |v| v[GLOBALID_KEY] }
@@ -91,6 +93,10 @@ module GraphQL
91
93
  when OPEN_STRUCT_KEY
92
94
  ostruct_values = load_value(value[OPEN_STRUCT_KEY])
93
95
  OpenStruct.new(ostruct_values)
96
+ when HASH_KEY
97
+ value[HASH_KEY].each_with_object({}) do |(k, v), loaded_h|
98
+ loaded_h[load_value(k)] = load_value(v)
99
+ end
94
100
  else
95
101
  key = value.keys.first
96
102
  { key => load_value(value[key]) }
@@ -98,15 +104,21 @@ module GraphQL
98
104
  else
99
105
  loaded_h = {}
100
106
  sym_keys = value.fetch(SYMBOL_KEYS_KEY, [])
107
+ symbol_key_values = sym_keys.is_a?(Hash) ? sym_keys : nil
101
108
  value.each do |k, v|
102
109
  if k == SYMBOL_KEYS_KEY
103
110
  next
104
111
  end
105
- if sym_keys.include?(k)
112
+ if !symbol_key_values && sym_keys.include?(k)
106
113
  k = k.to_sym
107
114
  end
108
115
  loaded_h[k] = load_value(v)
109
116
  end
117
+ if symbol_key_values
118
+ symbol_key_values.each do |k, v|
119
+ loaded_h[k.to_sym] = load_value(v)
120
+ end
121
+ end
110
122
  loaded_h
111
123
  end
112
124
  else
@@ -120,16 +132,31 @@ module GraphQL
120
132
  if obj.is_a?(Array)
121
133
  obj.map{|item| dump_value(item)}
122
134
  elsif obj.is_a?(Hash)
135
+ has_colliding_symbol_key = obj.any? { |k, _v| k.is_a?(Symbol) && obj.key?(k.to_s) }
136
+ if obj.any? { |k, _v| RESERVED_KEYS.include?(k.to_s) }
137
+ return {
138
+ HASH_KEY => obj.map { |k, v| [dump_value(k.is_a?(Symbol) ? k : k.to_s), dump_value(v)] },
139
+ }
140
+ end
123
141
  symbol_keys = nil
142
+ symbol_key_values = nil
124
143
  dumped_h = {}
125
144
  obj.each do |k, v|
126
- dumped_h[k.to_s] = dump_value(v)
127
- if k.is_a?(Symbol)
145
+ dumped_v = dump_value(v)
146
+ if has_colliding_symbol_key && k.is_a?(Symbol)
147
+ symbol_key_values ||= {}
148
+ symbol_key_values[k.to_s] = dumped_v
149
+ else
150
+ dumped_h[k.to_s] = dumped_v
151
+ end
152
+ if !has_colliding_symbol_key && k.is_a?(Symbol)
128
153
  symbol_keys ||= Set.new
129
154
  symbol_keys << k.to_s
130
155
  end
131
156
  end
132
- if symbol_keys
157
+ if symbol_key_values
158
+ dumped_h[SYMBOL_KEYS_KEY] = symbol_key_values
159
+ elsif symbol_keys
133
160
  dumped_h[SYMBOL_KEYS_KEY] = symbol_keys.to_a
134
161
  end
135
162
  dumped_h
@@ -37,13 +37,18 @@ module GraphQL
37
37
  scope.set_transaction_name(transaction_name(query))
38
38
  end
39
39
  end
40
- span.set_data('graphql.document', query.query_string)
40
+ if graphql_data_collection.nil? || graphql_data_collection.document
41
+ span.set_data('graphql.document', query.query_string)
42
+ end
41
43
  if query.selected_operation_name
42
44
  span.set_data('graphql.operation.name', query.selected_operation_name)
43
45
  end
44
46
  if query.selected_operation
45
47
  span.set_data('graphql.operation.type', query.selected_operation.operation_type)
46
48
  end
49
+ if graphql_data_collection&.variables && !query.provided_variables.empty?
50
+ span.set_data('graphql.variables', query.provided_variables)
51
+ end
47
52
  end
48
53
  end
49
54
 
@@ -55,6 +60,13 @@ module GraphQL
55
60
 
56
61
  private
57
62
 
63
+ def graphql_data_collection
64
+ @graphql_data_collection ||= if Sentry.respond_to?(:configuration)
65
+ configuration = Sentry.configuration
66
+ configuration.data_collection.graphql if configuration.respond_to?(:data_collection)
67
+ end
68
+ end
69
+
58
70
  def operation_name(query)
59
71
  selected_op = query.selected_operation
60
72
  if selected_op
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.6.10"
3
+ VERSION = "2.6.11"
4
4
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.10
4
+ version: 2.6.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-08-27 00:00:00.000000000 Z
10
+ date: 2026-09-21 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64