graphql 2.5.1 → 2.5.2

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: 9e469894226863a0c846e1f18e68fc7e0507f3d0c636e1c6416d545c55a7b83d
4
- data.tar.gz: 39fe2385b5be73699b243bbfb1c83459de350311a6bc130a06eccbfd912f9ec5
3
+ metadata.gz: cff992b51cd3efae382a362bc5708e8dce5060efb48cf1944560262a59276283
4
+ data.tar.gz: 206fd93c0504f9072f8af62a18557a9e0380664b1f8836acd38426ee44e9eaf7
5
5
  SHA512:
6
- metadata.gz: f8d17b486e8cb98240c64b35097d2cfdbbf9168ff07390b0a1c60f003879a726b6ce407f7a5019bbee33789a8b7a8f6e9ff3d5d5eb62bfa6c2aefc41c501234a
7
- data.tar.gz: 8f6401e9e5e19fe0979f907ec5180aa2b4b5c0caccab8ae8e28dfa98283144a98ab5fb396155bf1b85d2836ac847d5eed49e21099040a1cc4e2acc19ea2acd3a
6
+ metadata.gz: 293369e23e4baee72a07481b6a6015be003e2557469ab3b30e1ee82d79bfaf6004218a5fe0a85033de8e92b3ae475508bf445e5906ec2a81c4df53c2b25add49
7
+ data.tar.gz: fafe652c92d2655ad39a16fb61252b33b27eee4e10476a5cc40ab7fd3c1e81a56f9e67b81f6a9ba1da67ba76c7112e756973a236287c634bae9cf66fb7ed3e3e
@@ -44,8 +44,11 @@ module GraphQL
44
44
  def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
45
45
  super
46
46
  @graphql_result_data = {}
47
+ @ordered_result_keys = nil
47
48
  end
48
49
 
50
+ attr_accessor :ordered_result_keys
51
+
49
52
  include GraphQLResult
50
53
 
51
54
  attr_accessor :graphql_merged_into
@@ -63,7 +66,13 @@ module GraphQL
63
66
  t.set_leaf(key, value)
64
67
  end
65
68
 
69
+ before_size = @graphql_result_data.size
66
70
  @graphql_result_data[key] = value
71
+ after_size = @graphql_result_data.size
72
+ if after_size > before_size && @ordered_result_keys[before_size] != key
73
+ fix_result_order
74
+ end
75
+
67
76
  # keep this up-to-date if it's been initialized
68
77
  @graphql_metadata && @graphql_metadata[key] = value
69
78
 
@@ -74,7 +83,13 @@ module GraphQL
74
83
  if (t = @graphql_merged_into)
75
84
  t.set_child_result(key, value)
76
85
  end
86
+ before_size = @graphql_result_data.size
77
87
  @graphql_result_data[key] = value.graphql_result_data
88
+ after_size = @graphql_result_data.size
89
+ if after_size > before_size && @ordered_result_keys[before_size] != key
90
+ fix_result_order
91
+ end
92
+
78
93
  # If we encounter some part of this response that requires metadata tracking,
79
94
  # then create the metadata hash if necessary. It will be kept up-to-date after this.
80
95
  (@graphql_metadata ||= @graphql_result_data.dup)[key] = value
@@ -124,6 +139,14 @@ module GraphQL
124
139
  end
125
140
  @graphql_merged_into = into_result
126
141
  end
142
+
143
+ def fix_result_order
144
+ @ordered_result_keys.each do |k|
145
+ if @graphql_result_data.key?(k)
146
+ @graphql_result_data[k] = @graphql_result_data.delete(k)
147
+ end
148
+ end
149
+ end
127
150
  end
128
151
 
129
152
  class GraphQLResultArray
@@ -83,9 +83,11 @@ module GraphQL
83
83
  @response = nil
84
84
  else
85
85
  call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
86
- each_gathered_selections(@response) do |selections, is_selection_array|
86
+ each_gathered_selections(@response) do |selections, is_selection_array, ordered_result_keys|
87
+ @response.ordered_result_keys ||= ordered_result_keys
87
88
  if is_selection_array
88
89
  selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil)
90
+ selection_response.ordered_result_keys = ordered_result_keys
89
91
  final_response = @response
90
92
  else
91
93
  selection_response = @response
@@ -107,17 +109,19 @@ module GraphQL
107
109
  end
108
110
 
109
111
  def each_gathered_selections(response_hash)
110
- gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections)
112
+ ordered_result_keys = []
113
+ gathered_selections = gather_selections(response_hash.graphql_application_value, response_hash.graphql_result_type, response_hash.graphql_selections, nil, {}, ordered_result_keys)
114
+ ordered_result_keys.uniq!
111
115
  if gathered_selections.is_a?(Array)
112
116
  gathered_selections.each do |item|
113
- yield(item, true)
117
+ yield(item, true, ordered_result_keys)
114
118
  end
115
119
  else
116
- yield(gathered_selections, false)
120
+ yield(gathered_selections, false, ordered_result_keys)
117
121
  end
118
122
  end
119
123
 
120
- def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
124
+ def gather_selections(owner_object, owner_type, selections, selections_to_run, selections_by_name, ordered_result_keys)
121
125
  selections.each do |node|
122
126
  # Skip gathering this if the directive says so
123
127
  if !directives_include?(node, owner_object, owner_type)
@@ -126,6 +130,7 @@ module GraphQL
126
130
 
127
131
  if node.is_a?(GraphQL::Language::Nodes::Field)
128
132
  response_key = node.alias || node.name
133
+ ordered_result_keys << response_key
129
134
  selections = selections_by_name[response_key]
130
135
  # if there was already a selection of this field,
131
136
  # use an array to hold all selections,
@@ -162,14 +167,14 @@ module GraphQL
162
167
  type_defn = query.types.type(node.type.name)
163
168
 
164
169
  if query.types.possible_types(type_defn).include?(owner_type)
165
- result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
170
+ result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
166
171
  if !result.equal?(next_selections)
167
172
  selections_to_run = result
168
173
  end
169
174
  end
170
175
  else
171
176
  # it's an untyped fragment, definitely continue
172
- result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
177
+ result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections, ordered_result_keys)
173
178
  if !result.equal?(next_selections)
174
179
  selections_to_run = result
175
180
  end
@@ -178,7 +183,7 @@ module GraphQL
178
183
  fragment_def = query.fragments[node.name]
179
184
  type_defn = query.types.type(fragment_def.type.name)
180
185
  if query.types.possible_types(type_defn).include?(owner_type)
181
- result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
186
+ result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections, ordered_result_keys)
182
187
  if !result.equal?(next_selections)
183
188
  selections_to_run = result
184
189
  end
@@ -207,7 +212,6 @@ module GraphQL
207
212
  finished_jobs = 0
208
213
  enqueued_jobs = gathered_selections.size
209
214
  gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
210
-
211
215
  # Field resolution may pause the fiber,
212
216
  # so it wouldn't get to the `Resolve` call that happens below.
213
217
  # So instead trigger a run from this outer context.
@@ -629,9 +633,11 @@ module GraphQL
629
633
  if HALT != continue_value
630
634
  response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
631
635
  set_result(selection_result, result_name, response_hash, true, is_non_null)
632
- each_gathered_selections(response_hash) do |selections, is_selection_array|
636
+ each_gathered_selections(response_hash) do |selections, is_selection_array, ordered_result_keys|
637
+ response_hash.ordered_result_keys ||= ordered_result_keys
633
638
  if is_selection_array
634
639
  this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
640
+ this_result.ordered_result_keys = ordered_result_keys
635
641
  final_result = response_hash
636
642
  else
637
643
  this_result = response_hash
@@ -37,6 +37,8 @@ module GraphQL
37
37
 
38
38
  argument :by, [String], "Flags to check for this schema member"
39
39
 
40
+ repeatable(true)
41
+
40
42
  module VisibleByFlag
41
43
  def self.included(schema_class)
42
44
  schema_class.extend(self)
@@ -41,10 +41,24 @@ module GraphQL
41
41
  end
42
42
  end
43
43
 
44
+ # @return [String, nil]
45
+ def deprecation_reason
46
+ super || @resolver_class&.deprecation_reason
47
+ end
48
+
44
49
  def directives
45
50
  if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
46
51
  if !(own_dirs = super).empty?
47
- own_dirs + r_dirs
52
+ new_dirs = own_dirs.dup
53
+ r_dirs.each do |r_dir|
54
+ if r_dir.class.repeatable? ||
55
+ ( (r_dir_name = r_dir.graphql_name) &&
56
+ (!new_dirs.any? { |d| d.graphql_name == r_dir_name })
57
+ )
58
+ new_dirs << r_dir
59
+ end
60
+ end
61
+ new_dirs
48
62
  else
49
63
  r_dirs
50
64
  end
@@ -38,7 +38,7 @@ module GraphQL
38
38
  # Weirdly, procs are applied during coercion, but not methods.
39
39
  # Probably because these methods require a `self`.
40
40
  if arg_defn.prepare.is_a?(Symbol) || context.nil?
41
- prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
41
+ prepared_value = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key], context: context)
42
42
  overwrite_argument(ruby_kwargs_key, prepared_value)
43
43
  end
44
44
  end
@@ -18,6 +18,21 @@ module GraphQL
18
18
  directive(GraphQL::Schema::Directive::Deprecated, reason: text)
19
19
  end
20
20
  end
21
+
22
+ def self.extended(child_class)
23
+ super
24
+ child_class.extend(ClassMethods)
25
+ end
26
+
27
+ module ClassMethods
28
+ def deprecation_reason(new_reason = NOT_CONFIGURED)
29
+ if NOT_CONFIGURED.equal?(new_reason)
30
+ super()
31
+ else
32
+ self.deprecation_reason = new_reason
33
+ end
34
+ end
35
+ end
21
36
  end
22
37
  end
23
38
  end
@@ -28,6 +28,7 @@ module GraphQL
28
28
  extend Schema::Member::HasPath
29
29
  extend Schema::Member::HasDirectives
30
30
  include Schema::Member::HasDataloader
31
+ extend Schema::Member::HasDeprecationReason
31
32
 
32
33
  # @param object [Object] The application object that this field is being resolved on
33
34
  # @param context [GraphQL::Query::Context]
@@ -47,12 +47,21 @@ module GraphQL
47
47
  end.compare_by_identity
48
48
  }.compare_by_identity
49
49
 
50
- @cached_visible_arguments = Hash.new do |h, arg|
51
- h[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
52
- true
53
- else
54
- false
55
- end
50
+ @cached_visible_arguments = Hash.new do |h, owner|
51
+ h[owner] = Hash.new do |h2, arg|
52
+ h2[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
53
+ case owner
54
+ when GraphQL::Schema::Field
55
+ @cached_visible_fields[owner.owner][owner]
56
+ when Class
57
+ @cached_visible[owner]
58
+ else
59
+ raise "Unexpected argument owner for `#{arg.path}`: #{owner.inspect}"
60
+ end
61
+ else
62
+ false
63
+ end
64
+ end.compare_by_identity
56
65
  end.compare_by_identity
57
66
 
58
67
  @cached_parent_fields = Hash.new do |h, type|
@@ -82,7 +91,7 @@ module GraphQL
82
91
  end.compare_by_identity
83
92
 
84
93
  @cached_arguments = Hash.new do |h, owner|
85
- h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
94
+ h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments[owner])
86
95
  end.compare_by_identity
87
96
 
88
97
  @loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
@@ -180,7 +189,7 @@ module GraphQL
180
189
  if arg.is_a?(Array)
181
190
  visible_arg = nil
182
191
  arg.each do |arg_defn|
183
- if @cached_visible_arguments[arg_defn]
192
+ if @cached_visible_arguments[owner][arg_defn]
184
193
  if visible_arg.nil?
185
194
  visible_arg = arg_defn
186
195
  else
@@ -190,7 +199,7 @@ module GraphQL
190
199
  end
191
200
  visible_arg
192
201
  else
193
- if arg && @cached_visible_arguments[arg]
202
+ if arg && @cached_visible_arguments[owner][arg]
194
203
  arg
195
204
  else
196
205
  nil
@@ -292,7 +301,7 @@ module GraphQL
292
301
  @all_types_loaded = true
293
302
  visit = Visibility::Visit.new(@schema) do |member|
294
303
  if member.is_a?(Module) && member.respond_to?(:kind)
295
- if @cached_visible[member]
304
+ if @cached_visible[member] && referenced?(member)
296
305
  type_name = member.graphql_name
297
306
  if (prev_t = @all_types[type_name]) && !prev_t.equal?(member)
298
307
  raise_duplicate_definition(member, prev_t)
@@ -312,7 +321,18 @@ module GraphQL
312
321
  end
313
322
 
314
323
  def referenced?(type_defn)
315
- @schema.visibility.all_references[type_defn].any? { |r| r == true || @cached_visible[r] }
324
+ @schema.visibility.all_references[type_defn].any? do |ref|
325
+ case ref
326
+ when GraphQL::Schema::Argument
327
+ @cached_visible_arguments[ref.owner][ref]
328
+ when GraphQL::Schema::Field
329
+ @cached_visible_fields[ref.owner][ref]
330
+ when Module
331
+ @cached_visible[ref]
332
+ when true
333
+ true
334
+ end
335
+ end
316
336
  end
317
337
 
318
338
  def possible_types_for(type)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.5.1"
3
+ VERSION = "2.5.2"
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.5.1
4
+ version: 2.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-04-03 00:00:00.000000000 Z
10
+ date: 2025-04-08 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64