graphql 2.2.17 → 2.3.0

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/ast/visitor.rb +8 -0
  3. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  4. data/lib/graphql/dataloader/source.rb +1 -5
  5. data/lib/graphql/execution/interpreter/runtime.rb +4 -12
  6. data/lib/graphql/language/document_from_schema_definition.rb +1 -1
  7. data/lib/graphql/language/lexer.rb +27 -28
  8. data/lib/graphql/language/nodes.rb +0 -3
  9. data/lib/graphql/language/static_visitor.rb +33 -37
  10. data/lib/graphql/language/visitor.rb +55 -59
  11. data/lib/graphql/language.rb +37 -0
  12. data/lib/graphql/query/context.rb +30 -33
  13. data/lib/graphql/query.rb +1 -1
  14. data/lib/graphql/schema/argument.rb +5 -3
  15. data/lib/graphql/schema/build_from_definition.rb +10 -9
  16. data/lib/graphql/schema/directive.rb +1 -1
  17. data/lib/graphql/schema/enum_value.rb +1 -1
  18. data/lib/graphql/schema/field.rb +1 -1
  19. data/lib/graphql/schema/interface.rb +1 -1
  20. data/lib/graphql/schema/loader.rb +2 -1
  21. data/lib/graphql/schema/member/has_directives.rb +1 -1
  22. data/lib/graphql/schema/member/has_fields.rb +1 -1
  23. data/lib/graphql/schema/member/has_interfaces.rb +1 -1
  24. data/lib/graphql/schema/member/scoped.rb +1 -1
  25. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  26. data/lib/graphql/schema.rb +16 -1
  27. data/lib/graphql/tracing/appoptics_trace.rb +0 -4
  28. data/lib/graphql/tracing/appsignal_trace.rb +0 -4
  29. data/lib/graphql/tracing/data_dog_trace.rb +0 -4
  30. data/lib/graphql/tracing/notifications_trace.rb +0 -4
  31. data/lib/graphql/tracing/platform_trace.rb +0 -5
  32. data/lib/graphql/tracing/platform_tracing.rb +1 -1
  33. data/lib/graphql/tracing/prometheus_trace.rb +0 -4
  34. data/lib/graphql/tracing/scout_trace.rb +0 -3
  35. data/lib/graphql/tracing/sentry_trace.rb +0 -4
  36. data/lib/graphql/tracing/statsd_trace.rb +0 -4
  37. data/lib/graphql/types/relay/connection_behaviors.rb +1 -1
  38. data/lib/graphql/types/relay/edge_behaviors.rb +1 -1
  39. data/lib/graphql/version.rb +1 -1
  40. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 543f8d4c101b53b2dce0d9b01d694a1ed0be3f8d22663cc33ddf40cce35da066
4
- data.tar.gz: 51c773a99b528550dde8b0bfa845fbd2871198a329903aa44e96319d805d3ef4
3
+ metadata.gz: 881a55a1017c82563e75cf9898d44be453c1329c849f60b9538fdbd0f0d4b630
4
+ data.tar.gz: e99efcbffe7cab713e9d5fa7156c1f3bb56752b10ae35f9c5b23e705a27f90da
5
5
  SHA512:
6
- metadata.gz: f1aed999ba68c4020e5c63e3616d02a7b46b8626680182a67222a0e35636b12c1b1b024f99e84f4ac61bad06757c2599f331918240ffc6cac0cd060877bb4f7c
7
- data.tar.gz: 6180c9f78064cb20bc6b9f8fe15b3961371abe0f0462c4d4e306e830ed0864c0e2e4996aecc1cd1504a3341c969770265fa9497948f9f53e2a36f402a3ded93f
6
+ metadata.gz: c82107ac040dd40a8bfcf09f0abf23d5a4a40ef8ed68b3e6ff0918e3b4ff02c6ae7f9637fed578bbd78032bde8d61622b65ab8efeb3bd4e2538fc809e626a92a
7
+ data.tar.gz: e0eddc7d0562f9637ecb1707d690b7d4579373c6d8f030e09565a150a60c4272ebb4e5f06cefc5165f6babfde01e5fb8930b4f678d6c1e146df56999634cbc1f
@@ -118,8 +118,12 @@ module GraphQL
118
118
  def on_inline_fragment(node, parent)
119
119
  on_fragment_with_type(node) do
120
120
  @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
121
+ @skipping = @skip_stack.last || skip?(node)
122
+ @skip_stack << @skipping
123
+
121
124
  call_on_enter_inline_fragment(node, parent)
122
125
  super
126
+ @skipping = @skip_stack.pop
123
127
  call_on_leave_inline_fragment(node, parent)
124
128
  end
125
129
  end
@@ -187,9 +191,13 @@ module GraphQL
187
191
 
188
192
  def on_fragment_spread(node, parent)
189
193
  @path.push("... #{node.name}")
194
+ @skipping = @skip_stack.last || skip?(node)
195
+ @skip_stack << @skipping
196
+
190
197
  call_on_enter_fragment_spread(node, parent)
191
198
  enter_fragment_spread_inline(node)
192
199
  super
200
+ @skipping = @skip_stack.pop
193
201
  leave_fragment_spread_inline(node)
194
202
  call_on_leave_fragment_spread(node, parent)
195
203
  @path.pop
@@ -16,12 +16,6 @@ module GraphQL
16
16
  "[" +
17
17
  obj.map { |v| inspect_truncated(v) }.join(", ") +
18
18
  "]"
19
- when Query::Context::SharedMethods
20
- if obj.invalid_null?
21
- "nil"
22
- else
23
- inspect_truncated(obj.value)
24
- end
25
19
  else
26
20
  inspect_truncated(obj)
27
21
  end
@@ -33,12 +27,6 @@ module GraphQL
33
27
  "{...}"
34
28
  when Array
35
29
  "[...]"
36
- when Query::Context::SharedMethods
37
- if obj.invalid_null?
38
- "nil"
39
- else
40
- inspect_truncated(obj.value)
41
- end
42
30
  when GraphQL::Execution::Lazy
43
31
  "(unresolved)"
44
32
  else
@@ -187,11 +187,7 @@ ERR
187
187
  end
188
188
  result = @results[key]
189
189
 
190
- if result.is_a?(StandardError)
191
- # Dup it because the rescuer may modify it.
192
- # (This happens for GraphQL::ExecutionErrors, at least)
193
- raise result.dup
194
- end
190
+ raise result if result.class <= StandardError
195
191
 
196
192
  result
197
193
  end
@@ -137,6 +137,7 @@ module GraphQL
137
137
  end
138
138
 
139
139
  def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = {})
140
+
140
141
  selections.each do |node|
141
142
  # Skip gathering this if the directive says so
142
143
  if !directives_include?(node, owner_object, owner_type)
@@ -181,26 +182,17 @@ module GraphQL
181
182
  type_defn = schema.get_type(node.type.name, context)
182
183
 
183
184
  if query.warden.possible_types(type_defn).include?(owner_type)
184
- result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
185
- if !result.equal?(next_selections)
186
- selections_to_run = result
187
- end
185
+ gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
188
186
  end
189
187
  else
190
188
  # it's an untyped fragment, definitely continue
191
- result = gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
192
- if !result.equal?(next_selections)
193
- selections_to_run = result
194
- end
189
+ gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
195
190
  end
196
191
  when GraphQL::Language::Nodes::FragmentSpread
197
192
  fragment_def = query.fragments[node.name]
198
193
  type_defn = query.get_type(fragment_def.type.name)
199
194
  if query.warden.possible_types(type_defn).include?(owner_type)
200
- result = gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
201
- if !result.equal?(next_selections)
202
- selections_to_run = result
203
- end
195
+ gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
204
196
  end
205
197
  else
206
198
  raise "Invariant: unexpected selection class: #{node.class}"
@@ -24,7 +24,7 @@ module GraphQL
24
24
  @include_built_in_directives = include_built_in_directives
25
25
  @include_one_of = false
26
26
 
27
- schema_context = schema.context_class.new(query: nil, object: nil, schema: schema, values: context)
27
+ schema_context = schema.context_class.new(query: nil, schema: schema, values: context)
28
28
 
29
29
 
30
30
  @warden = @schema.warden_class.new(
@@ -109,29 +109,27 @@ module GraphQL
109
109
  }
110
110
  UTF_8 = /\\u(?:([\dAa-f]{4})|\{([\da-f]{4,})\})(?:\\u([\dAa-f]{4}))?/i
111
111
  VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{UTF_8})*\z/o
112
+ ESCAPED = /(?:#{ESCAPES}|#{UTF_8})/o
112
113
 
113
114
  def string_value
114
115
  str = token_value
115
116
  is_block = str.start_with?('"""')
116
117
  if is_block
117
118
  str.gsub!(/\A"""|"""\z/, '')
119
+ return Language::BlockString.trim_whitespace(str)
118
120
  else
119
121
  str.gsub!(/\A"|"\z/, '')
120
- end
121
-
122
- if is_block
123
- str = Language::BlockString.trim_whitespace(str)
124
- end
125
-
126
- if !str.valid_encoding? || !str.match?(VALID_STRING)
127
- raise_parse_error("Bad unicode escape in #{str.inspect}")
128
- else
129
- Lexer.replace_escaped_characters_in_place(str)
130
122
 
131
- if !str.valid_encoding?
123
+ if !str.valid_encoding? || !str.match?(VALID_STRING)
132
124
  raise_parse_error("Bad unicode escape in #{str.inspect}")
133
125
  else
134
- str
126
+ Lexer.replace_escaped_characters_in_place(str)
127
+
128
+ if !str.valid_encoding?
129
+ raise_parse_error("Bad unicode escape in #{str.inspect}")
130
+ else
131
+ str
132
+ end
135
133
  end
136
134
  end
137
135
  end
@@ -256,7 +254,7 @@ module GraphQL
256
254
  STRING_ESCAPE = %r{[\\][\\/bfnrt]}
257
255
  BLOCK_QUOTE = '"""'
258
256
  ESCAPED_QUOTE = /\\"/;
259
- STRING_CHAR = /#{ESCAPED_QUOTE}|[^"\\]|#{UNICODE_ESCAPE}|#{STRING_ESCAPE}/
257
+ STRING_CHAR = /#{ESCAPED_QUOTE}|[^"\\\n\r]|#{UNICODE_ESCAPE}|#{STRING_ESCAPE}/
260
258
  QUOTED_STRING_REGEXP = %r{#{QUOTE} (?:#{STRING_CHAR})* #{QUOTE}}x
261
259
  BLOCK_STRING_REGEXP = %r{
262
260
  #{BLOCK_QUOTE}
@@ -301,24 +299,25 @@ module GraphQL
301
299
  # Replace any escaped unicode or whitespace with the _actual_ characters
302
300
  # To avoid allocating more strings, this modifies the string passed into it
303
301
  def self.replace_escaped_characters_in_place(raw_string)
304
- raw_string.gsub!(ESCAPES, ESCAPES_REPLACE)
305
- raw_string.gsub!(UTF_8) do |_matched_str|
306
- codepoint_1 = ($1 || $2).to_i(16)
307
- codepoint_2 = $3
308
-
309
- if codepoint_2
310
- codepoint_2 = codepoint_2.to_i(16)
311
- if (codepoint_1 >= 0xD800 && codepoint_1 <= 0xDBFF) && # leading surrogate
312
- (codepoint_2 >= 0xDC00 && codepoint_2 <= 0xDFFF) # trailing surrogate
313
- # A surrogate pair
314
- combined = ((codepoint_1 - 0xD800) * 0x400) + (codepoint_2 - 0xDC00) + 0x10000
315
- [combined].pack('U'.freeze)
302
+ raw_string.gsub!(ESCAPED) do |matched_str|
303
+ if (point_str_1 = $1 || $2)
304
+ codepoint_1 = point_str_1.to_i(16)
305
+ if (codepoint_2 = $3)
306
+ codepoint_2 = codepoint_2.to_i(16)
307
+ if (codepoint_1 >= 0xD800 && codepoint_1 <= 0xDBFF) && # leading surrogate
308
+ (codepoint_2 >= 0xDC00 && codepoint_2 <= 0xDFFF) # trailing surrogate
309
+ # A surrogate pair
310
+ combined = ((codepoint_1 - 0xD800) * 0x400) + (codepoint_2 - 0xDC00) + 0x10000
311
+ [combined].pack('U'.freeze)
312
+ else
313
+ # Two separate code points
314
+ [codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze)
315
+ end
316
316
  else
317
- # Two separate code points
318
- [codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze)
317
+ [codepoint_1].pack('U'.freeze)
319
318
  end
320
319
  else
321
- [codepoint_1].pack('U'.freeze)
320
+ ESCAPES_REPLACE[matched_str]
322
321
  end
323
322
  end
324
323
  nil
@@ -138,8 +138,6 @@ module GraphQL
138
138
  end
139
139
 
140
140
  class << self
141
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
142
-
143
141
  # Add a default `#visit_method` and `#children_method_name` using the class name
144
142
  def inherited(child_class)
145
143
  super
@@ -318,7 +316,6 @@ module GraphQL
318
316
  RUBY
319
317
  end
320
318
  end
321
- # rubocop:enable Development/NoEvalCop
322
319
  end
323
320
  end
324
321
 
@@ -22,6 +22,39 @@ module GraphQL
22
22
  end
23
23
  end
24
24
 
25
+ # We don't use `alias` here because it breaks `super`
26
+ def self.make_visit_methods(ast_node_class)
27
+ node_method = ast_node_class.visit_method
28
+ children_of_type = ast_node_class.children_of_type
29
+ child_visit_method = :"#{node_method}_children"
30
+
31
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
32
+ # The default implementation for visiting an AST node.
33
+ # It doesn't _do_ anything, but it continues to visiting the node's children.
34
+ # To customize this hook, override one of its make_visit_methods (or the base method?)
35
+ # in your subclasses.
36
+ #
37
+ # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
38
+ # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
39
+ # @return [void]
40
+ def #{node_method}(node, parent)
41
+ #{
42
+ if method_defined?(child_visit_method)
43
+ "#{child_visit_method}(node)"
44
+ elsif children_of_type
45
+ children_of_type.map do |child_accessor, child_class|
46
+ "node.#{child_accessor}.each do |child_node|
47
+ #{child_class.visit_method}(child_node, node)
48
+ end"
49
+ end.join("\n")
50
+ else
51
+ ""
52
+ end
53
+ }
54
+ end
55
+ RUBY
56
+ end
57
+
25
58
  def on_document_children(document_node)
26
59
  document_node.children.each do |child_node|
27
60
  visit_method = child_node.visit_method
@@ -90,41 +123,6 @@ module GraphQL
90
123
  end
91
124
  end
92
125
 
93
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
94
-
95
- # We don't use `alias` here because it breaks `super`
96
- def self.make_visit_methods(ast_node_class)
97
- node_method = ast_node_class.visit_method
98
- children_of_type = ast_node_class.children_of_type
99
- child_visit_method = :"#{node_method}_children"
100
-
101
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
102
- # The default implementation for visiting an AST node.
103
- # It doesn't _do_ anything, but it continues to visiting the node's children.
104
- # To customize this hook, override one of its make_visit_methods (or the base method?)
105
- # in your subclasses.
106
- #
107
- # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
108
- # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
109
- # @return [void]
110
- def #{node_method}(node, parent)
111
- #{
112
- if method_defined?(child_visit_method)
113
- "#{child_visit_method}(node)"
114
- elsif children_of_type
115
- children_of_type.map do |child_accessor, child_class|
116
- "node.#{child_accessor}.each do |child_node|
117
- #{child_class.visit_method}(child_node, node)
118
- end"
119
- end.join("\n")
120
- else
121
- ""
122
- end
123
- }
124
- end
125
- RUBY
126
- end
127
-
128
126
  [
129
127
  Language::Nodes::Argument,
130
128
  Language::Nodes::Directive,
@@ -164,8 +162,6 @@ module GraphQL
164
162
  ].each do |ast_node_class|
165
163
  make_visit_methods(ast_node_class)
166
164
  end
167
-
168
- # rubocop:disable Development/NoEvalCop
169
165
  end
170
166
  end
171
167
  end
@@ -61,6 +61,61 @@ module GraphQL
61
61
  end
62
62
  end
63
63
 
64
+ # We don't use `alias` here because it breaks `super`
65
+ def self.make_visit_methods(ast_node_class)
66
+ node_method = ast_node_class.visit_method
67
+ children_of_type = ast_node_class.children_of_type
68
+ child_visit_method = :"#{node_method}_children"
69
+
70
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
71
+ # The default implementation for visiting an AST node.
72
+ # It doesn't _do_ anything, but it continues to visiting the node's children.
73
+ # To customize this hook, override one of its make_visit_methods (or the base method?)
74
+ # in your subclasses.
75
+ #
76
+ # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
77
+ # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
78
+ # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
79
+ def #{node_method}(node, parent)
80
+ if node.equal?(DELETE_NODE)
81
+ # This might be passed to `super(DELETE_NODE, ...)`
82
+ # by a user hook, don't want to keep visiting in that case.
83
+ [node, parent]
84
+ else
85
+ new_node = node
86
+ #{
87
+ if method_defined?(child_visit_method)
88
+ "new_node = #{child_visit_method}(new_node)"
89
+ elsif children_of_type
90
+ children_of_type.map do |child_accessor, child_class|
91
+ "node.#{child_accessor}.each do |child_node|
92
+ new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
93
+ # Reassign `node` in case the child hook makes a modification
94
+ if new_child_and_node.is_a?(Array)
95
+ new_node = new_child_and_node[1]
96
+ end
97
+ end"
98
+ end.join("\n")
99
+ else
100
+ ""
101
+ end
102
+ }
103
+
104
+ if new_node.equal?(node)
105
+ [node, parent]
106
+ else
107
+ [new_node, parent]
108
+ end
109
+ end
110
+ end
111
+
112
+ def #{node_method}_with_modifications(node, parent)
113
+ new_node_and_new_parent = #{node_method}(node, parent)
114
+ apply_modifications(node, parent, new_node_and_new_parent)
115
+ end
116
+ RUBY
117
+ end
118
+
64
119
  def on_document_children(document_node)
65
120
  new_node = document_node
66
121
  document_node.children.each do |child_node|
@@ -161,63 +216,6 @@ module GraphQL
161
216
  new_node
162
217
  end
163
218
 
164
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
165
-
166
- # We don't use `alias` here because it breaks `super`
167
- def self.make_visit_methods(ast_node_class)
168
- node_method = ast_node_class.visit_method
169
- children_of_type = ast_node_class.children_of_type
170
- child_visit_method = :"#{node_method}_children"
171
-
172
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
173
- # The default implementation for visiting an AST node.
174
- # It doesn't _do_ anything, but it continues to visiting the node's children.
175
- # To customize this hook, override one of its make_visit_methods (or the base method?)
176
- # in your subclasses.
177
- #
178
- # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
179
- # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
180
- # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
181
- def #{node_method}(node, parent)
182
- if node.equal?(DELETE_NODE)
183
- # This might be passed to `super(DELETE_NODE, ...)`
184
- # by a user hook, don't want to keep visiting in that case.
185
- [node, parent]
186
- else
187
- new_node = node
188
- #{
189
- if method_defined?(child_visit_method)
190
- "new_node = #{child_visit_method}(new_node)"
191
- elsif children_of_type
192
- children_of_type.map do |child_accessor, child_class|
193
- "node.#{child_accessor}.each do |child_node|
194
- new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
195
- # Reassign `node` in case the child hook makes a modification
196
- if new_child_and_node.is_a?(Array)
197
- new_node = new_child_and_node[1]
198
- end
199
- end"
200
- end.join("\n")
201
- else
202
- ""
203
- end
204
- }
205
-
206
- if new_node.equal?(node)
207
- [node, parent]
208
- else
209
- [new_node, parent]
210
- end
211
- end
212
- end
213
-
214
- def #{node_method}_with_modifications(node, parent)
215
- new_node_and_new_parent = #{node_method}(node, parent)
216
- apply_modifications(node, parent, new_node_and_new_parent)
217
- end
218
- RUBY
219
- end
220
-
221
219
  [
222
220
  Language::Nodes::Argument,
223
221
  Language::Nodes::Directive,
@@ -258,8 +256,6 @@ module GraphQL
258
256
  make_visit_methods(ast_node_class)
259
257
  end
260
258
 
261
- # rubocop:enable Development/NoEvalCop
262
-
263
259
  private
264
260
 
265
261
  def apply_modifications(node, parent, new_node_and_new_parent)
@@ -12,6 +12,7 @@ require "graphql/language/static_visitor"
12
12
  require "graphql/language/token"
13
13
  require "graphql/language/visitor"
14
14
  require "graphql/language/definition_slice"
15
+ require "strscan"
15
16
 
16
17
  module GraphQL
17
18
  module Language
@@ -33,5 +34,41 @@ module GraphQL
33
34
  JSON.generate(value, quirks_mode: true)
34
35
  end
35
36
  end
37
+
38
+ # Returns a new string if any single-quoted newlines were escaped.
39
+ # Otherwise, returns `query_str` unchanged.
40
+ # @return [String]
41
+ def self.escape_single_quoted_newlines(query_str)
42
+ scanner = StringScanner.new(query_str)
43
+ inside_single_quoted_string = false
44
+ new_query_str = nil
45
+ while !scanner.eos?
46
+ if (match = scanner.scan(/(?:\\"|[^"\n\r]|""")+/m)) && new_query_str
47
+ new_query_str << match
48
+ elsif scanner.scan('"')
49
+ new_query_str && (new_query_str << '"')
50
+ inside_single_quoted_string = !inside_single_quoted_string
51
+ elsif scanner.scan("\n")
52
+ if inside_single_quoted_string
53
+ new_query_str ||= query_str[0, scanner.pos - 1]
54
+ new_query_str << '\\n'
55
+ else
56
+ new_query_str && (new_query_str << "\n")
57
+ end
58
+ elsif scanner.scan("\r")
59
+ if inside_single_quoted_string
60
+ new_query_str ||= query_str[0, scanner.pos - 1]
61
+ new_query_str << '\\r'
62
+ else
63
+ new_query_str && (new_query_str << "\r")
64
+ end
65
+ elsif scanner.eos?
66
+ break
67
+ else
68
+ raise ArgumentError, "Unmatchable string scanner segment: #{scanner.rest.inspect}"
69
+ end
70
+ end
71
+ new_query_str || query_str
72
+ end
36
73
  end
37
74
  end
@@ -6,36 +6,6 @@ module GraphQL
6
6
  # Expose some query-specific info to field resolve functions.
7
7
  # It delegates `[]` to the hash that's passed to `GraphQL::Query#initialize`.
8
8
  class Context
9
- module SharedMethods
10
- # Return this value to tell the runtime
11
- # to exclude this field from the response altogether
12
- def skip
13
- GraphQL::Execution::SKIP
14
- end
15
-
16
- # Add error at query-level.
17
- # @param error [GraphQL::ExecutionError] an execution error
18
- # @return [void]
19
- def add_error(error)
20
- if !error.is_a?(ExecutionError)
21
- raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
22
- end
23
- errors << error
24
- nil
25
- end
26
-
27
- # @example Print the GraphQL backtrace during field resolution
28
- # puts ctx.backtrace
29
- #
30
- # @return [GraphQL::Backtrace] The backtrace for this point in query execution
31
- def backtrace
32
- GraphQL::Backtrace.new(self)
33
- end
34
-
35
- def execution_errors
36
- @execution_errors ||= ExecutionErrors.new(self)
37
- end
38
- end
39
9
 
40
10
  class ExecutionErrors
41
11
  def initialize(ctx)
@@ -59,7 +29,6 @@ module GraphQL
59
29
  alias :push :add
60
30
  end
61
31
 
62
- include SharedMethods
63
32
  extend Forwardable
64
33
 
65
34
  # @return [Array<GraphQL::ExecutionError>] errors returned during execution
@@ -77,11 +46,10 @@ module GraphQL
77
46
  # Make a new context which delegates key lookup to `values`
78
47
  # @param query [GraphQL::Query] the query who owns this context
79
48
  # @param values [Hash] A hash of arbitrary values which will be accessible at query-time
80
- def initialize(query:, schema: query.schema, values:, object:)
49
+ def initialize(query:, schema: query.schema, values:)
81
50
  @query = query
82
51
  @schema = schema
83
52
  @provided_values = values || {}
84
- @object = object
85
53
  # Namespaced storage, where user-provided values are in `nil` namespace:
86
54
  @storage = Hash.new { |h, k| h[k] = {} }
87
55
  @storage[nil] = @provided_values
@@ -140,6 +108,35 @@ module GraphQL
140
108
  end
141
109
  end
142
110
 
111
+ # Return this value to tell the runtime
112
+ # to exclude this field from the response altogether
113
+ def skip
114
+ GraphQL::Execution::SKIP
115
+ end
116
+
117
+ # Add error at query-level.
118
+ # @param error [GraphQL::ExecutionError] an execution error
119
+ # @return [void]
120
+ def add_error(error)
121
+ if !error.is_a?(ExecutionError)
122
+ raise TypeError, "expected error to be a ExecutionError, but was #{error.class}"
123
+ end
124
+ errors << error
125
+ nil
126
+ end
127
+
128
+ # @example Print the GraphQL backtrace during field resolution
129
+ # puts ctx.backtrace
130
+ #
131
+ # @return [GraphQL::Backtrace] The backtrace for this point in query execution
132
+ def backtrace
133
+ GraphQL::Backtrace.new(self)
134
+ end
135
+
136
+ def execution_errors
137
+ @execution_errors ||= ExecutionErrors.new(self)
138
+ end
139
+
143
140
  def current_path
144
141
  current_runtime_state = Thread.current[:__graphql_runtime_info]
145
142
  query_runtime_state = current_runtime_state && current_runtime_state[@query]
data/lib/graphql/query.rb CHANGED
@@ -99,7 +99,7 @@ module GraphQL
99
99
  # Even if `variables: nil` is passed, use an empty hash for simpler logic
100
100
  variables ||= {}
101
101
  @schema = schema
102
- @context = schema.context_class.new(query: self, object: root_value, values: context)
102
+ @context = schema.context_class.new(query: self, values: context)
103
103
  @warden = warden
104
104
  @subscription_topic = subscription_topic
105
105
  @root_value = root_value
@@ -53,7 +53,6 @@ module GraphQL
53
53
  def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block)
54
54
  arg_name ||= name
55
55
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
56
- NameValidator.validate!(@name)
57
56
  @type_expr = type_expr || type
58
57
  @description = desc || description
59
58
  @null = required != true
@@ -89,8 +88,11 @@ module GraphQL
89
88
  end
90
89
 
91
90
  if definition_block
92
- # `self` will still be self, it will also be the first argument to the block:
93
- instance_exec(self, &definition_block)
91
+ if definition_block.arity == 1
92
+ instance_exec(self, &definition_block)
93
+ else
94
+ instance_eval(&definition_block)
95
+ end
94
96
  end
95
97
  end
96
98
 
@@ -120,10 +120,12 @@ module GraphQL
120
120
 
121
121
  builder = self
122
122
 
123
+ found_types = types.values
123
124
  schema_class = Class.new(schema_superclass) do
124
125
  begin
125
126
  # Add these first so that there's some chance of resolving late-bound types
126
- orphan_types types.values
127
+ add_type_and_traverse(found_types, root: false)
128
+ orphan_types(found_types.select { |t| t.respond_to?(:kind) && t.kind.object? })
127
129
  query query_root_type
128
130
  mutation mutation_root_type
129
131
  subscription subscription_root_type
@@ -451,18 +453,17 @@ module GraphQL
451
453
 
452
454
  # Don't do this for interfaces
453
455
  if default_resolve
454
- define_field_resolve_method(owner, resolve_method_name, field_definition.name)
456
+ owner.class_eval <<-RUBY, __FILE__, __LINE__
457
+ # frozen_string_literal: true
458
+ def #{resolve_method_name}(**args)
459
+ field_instance = self.class.get_field("#{field_definition.name}")
460
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
461
+ end
462
+ RUBY
455
463
  end
456
464
  end
457
465
  end
458
466
 
459
- def define_field_resolve_method(owner, method_name, field_name)
460
- owner.define_method(method_name) { |**args|
461
- field_instance = self.class.get_field(field_name)
462
- context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
463
- }
464
- end
465
-
466
467
  def build_resolve_type(lookup_hash, directives, missing_type_handler)
467
468
  resolve_type_proc = nil
468
469
  resolve_type_proc = ->(ast_node) {
@@ -99,7 +99,7 @@ module GraphQL
99
99
 
100
100
  def inherited(subclass)
101
101
  super
102
- subclass.class_exec do
102
+ subclass.class_eval do
103
103
  @default_graphql_name ||= nil
104
104
  end
105
105
  end
@@ -47,7 +47,7 @@ module GraphQL
47
47
  end
48
48
 
49
49
  if block_given?
50
- instance_exec(self, &block)
50
+ instance_eval(&block)
51
51
  end
52
52
  end
53
53
 
@@ -233,7 +233,7 @@ module GraphQL
233
233
 
234
234
  @underscored_name = -Member::BuildType.underscore(name_s)
235
235
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
236
- NameValidator.validate!(@name)
236
+
237
237
  @description = description
238
238
  @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
239
239
 
@@ -29,7 +29,7 @@ module GraphQL
29
29
  const_set(:DefinitionMethods, defn_methods_module)
30
30
  extend(self::DefinitionMethods)
31
31
  end
32
- self::DefinitionMethods.module_exec(&block)
32
+ self::DefinitionMethods.module_eval(&block)
33
33
  end
34
34
 
35
35
  # @see {Schema::Warden} hides interfaces without visible implementations
@@ -32,7 +32,8 @@ module GraphQL
32
32
  end
33
33
 
34
34
  Class.new(GraphQL::Schema) do
35
- orphan_types(types.values)
35
+ add_type_and_traverse(types.values, root: false)
36
+ orphan_types(types.values.select { |t| t.kind.object? })
36
37
  directives(directives)
37
38
  description(schema["description"])
38
39
 
@@ -6,7 +6,7 @@ module GraphQL
6
6
  module HasDirectives
7
7
  def self.extended(child_cls)
8
8
  super
9
- child_cls.module_exec { self.own_directives = nil }
9
+ child_cls.module_eval { self.own_directives = nil }
10
10
  end
11
11
 
12
12
  def inherited(child_cls)
@@ -183,7 +183,7 @@ module GraphQL
183
183
 
184
184
  def inherited(subclass)
185
185
  super
186
- subclass.class_exec do
186
+ subclass.class_eval do
187
187
  @own_fields ||= nil
188
188
  @field_class ||= nil
189
189
  end
@@ -133,7 +133,7 @@ module GraphQL
133
133
 
134
134
  def inherited(subclass)
135
135
  super
136
- subclass.class_exec do
136
+ subclass.class_eval do
137
137
  @own_interface_type_memberships ||= nil
138
138
  end
139
139
  end
@@ -30,7 +30,7 @@ module GraphQL
30
30
 
31
31
  def inherited(subclass)
32
32
  super
33
- subclass.class_exec do
33
+ subclass.class_eval do
34
34
  @reauthorize_scoped_objects = nil
35
35
  end
36
36
  end
@@ -43,7 +43,7 @@ module GraphQL
43
43
  private
44
44
 
45
45
  def inherited(subclass)
46
- subclass.class_exec do
46
+ subclass.class_eval do
47
47
  @to_non_null_type ||= nil
48
48
  @to_list_type ||= nil
49
49
  end
@@ -861,6 +861,17 @@ module GraphQL
861
861
  def orphan_types(*new_orphan_types)
862
862
  if new_orphan_types.any?
863
863
  new_orphan_types = new_orphan_types.flatten
864
+ non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
865
+ if non_object_types.any?
866
+ raise ArgumentError, <<~ERR
867
+ Only object type classes should be added as `orphan_types(...)`.
868
+
869
+ - Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
870
+ - See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
871
+
872
+ To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
873
+ ERR
874
+ end
864
875
  add_type_and_traverse(new_orphan_types, root: false)
865
876
  own_orphan_types.concat(new_orphan_types.flatten)
866
877
  end
@@ -1129,7 +1140,11 @@ module GraphQL
1129
1140
  }.freeze
1130
1141
  end
1131
1142
 
1132
- def tracer(new_tracer)
1143
+ def tracer(new_tracer, silence_deprecation_warning: false)
1144
+ if !silence_deprecation_warning
1145
+ warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
1146
+ warn " #{caller(1, 1).first}"
1147
+ end
1133
1148
  default_trace = trace_class_for(:default, build: true)
1134
1149
  if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
1135
1150
  trace_with(GraphQL::Tracing::CallLegacyTracers)
@@ -28,8 +28,6 @@ module GraphQL
28
28
  Gem::Version.new('1.0.0')
29
29
  end
30
30
 
31
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
32
-
33
31
  [
34
32
  'lex',
35
33
  'parse',
@@ -57,8 +55,6 @@ module GraphQL
57
55
  RUBY
58
56
  end
59
57
 
60
- # rubocop:enable Development/NoEvalCop
61
-
62
58
  def execute_field(query:, field:, ast_node:, arguments:, object:)
63
59
  return_type = field.type.unwrap
64
60
  trace_field = if return_type.kind.scalar? || return_type.kind.enum?
@@ -13,8 +13,6 @@ module GraphQL
13
13
  super
14
14
  end
15
15
 
16
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
17
-
18
16
  {
19
17
  "lex" => "lex.graphql",
20
18
  "parse" => "parse.graphql",
@@ -45,8 +43,6 @@ module GraphQL
45
43
  RUBY
46
44
  end
47
45
 
48
- # rubocop:enable Development/NoEvalCop
49
-
50
46
  def platform_execute_field(platform_key)
51
47
  Appsignal.instrument(platform_key) do
52
48
  yield
@@ -20,8 +20,6 @@ module GraphQL
20
20
  super
21
21
  end
22
22
 
23
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
24
-
25
23
  {
26
24
  'lex' => 'lex.graphql',
27
25
  'parse' => 'parse.graphql',
@@ -71,8 +69,6 @@ module GraphQL
71
69
  RUBY
72
70
  end
73
71
 
74
- # rubocop:enable Development/NoEvalCop
75
-
76
72
  def execute_field_span(span_key, query, field, ast_node, arguments, object)
77
73
  return_type = field.type.unwrap
78
74
  trace_field = if return_type.kind.scalar? || return_type.kind.enum?
@@ -16,8 +16,6 @@ module GraphQL
16
16
  super
17
17
  end
18
18
 
19
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
20
-
21
19
  {
22
20
  "lex" => "lex.graphql",
23
21
  "parse" => "parse.graphql",
@@ -41,8 +39,6 @@ module GraphQL
41
39
  RUBY
42
40
  end
43
41
 
44
- # rubocop:enable Development/NoEvalCop
45
-
46
42
  include PlatformTrace
47
43
  end
48
44
  end
@@ -39,9 +39,6 @@ module GraphQL
39
39
  include(BaseKeyCache)
40
40
  }
41
41
  child_class.const_set(:KeyCache, key_methods_class)
42
-
43
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
44
-
45
42
  [:execute_field, :execute_field_lazy].each do |field_trace_method|
46
43
  if !child_class.method_defined?(field_trace_method)
47
44
  child_class.module_eval <<-RUBY, __FILE__, __LINE__
@@ -94,8 +91,6 @@ module GraphQL
94
91
  end
95
92
  RUBY
96
93
  end
97
-
98
- # rubocop:enable Development/NoEvalCop
99
94
  end
100
95
  end
101
96
 
@@ -86,7 +86,7 @@ module GraphQL
86
86
  else
87
87
  warn("`use(#{self.name})` and `Tracing::PlatformTracing` are deprecated. Use a `trace_with(...)` module instead. More info: https://graphql-ruby.org/queries/tracing.html. Please open an issue on the GraphQL-Ruby repo if you want to discuss further!")
88
88
  tracer = self.new(**options)
89
- schema_defn.tracer(tracer)
89
+ schema_defn.tracer(tracer, silence_deprecation_warning: true)
90
90
  end
91
91
  end
92
92
  end
@@ -13,8 +13,6 @@ module GraphQL
13
13
  super(**rest)
14
14
  end
15
15
 
16
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
17
-
18
16
  {
19
17
  'lex' => "graphql.lex",
20
18
  'parse' => "graphql.parse",
@@ -32,8 +30,6 @@ module GraphQL
32
30
  RUBY
33
31
  end
34
32
 
35
- # rubocop:enable Development/NoEvalCop
36
-
37
33
  def platform_execute_field(platform_key, &block)
38
34
  instrument_execution(platform_key, "execute_field", &block)
39
35
  end
@@ -16,8 +16,6 @@ module GraphQL
16
16
  super
17
17
  end
18
18
 
19
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
20
-
21
19
  {
22
20
  "lex" => "lex.graphql",
23
21
  "parse" => "parse.graphql",
@@ -47,7 +45,6 @@ module GraphQL
47
45
  end
48
46
  RUBY
49
47
  end
50
- # rubocop:enable Development/NoEvalCop
51
48
 
52
49
  def platform_execute_field(platform_key, &block)
53
50
  self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
@@ -23,8 +23,6 @@ module GraphQL
23
23
  instrument_execution("graphql.execute", "execute_query", data) { super }
24
24
  end
25
25
 
26
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
27
-
28
26
  {
29
27
  "lex" => "graphql.lex",
30
28
  "parse" => "graphql.parse",
@@ -41,8 +39,6 @@ module GraphQL
41
39
  RUBY
42
40
  end
43
41
 
44
- # rubocop:enable Development/NoEvalCop
45
-
46
42
  def platform_execute_field(platform_key, &block)
47
43
  instrument_execution(platform_key, "execute_field", &block)
48
44
  end
@@ -11,8 +11,6 @@ module GraphQL
11
11
  super(**rest)
12
12
  end
13
13
 
14
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
15
-
16
14
  {
17
15
  'lex' => "graphql.lex",
18
16
  'parse' => "graphql.parse",
@@ -32,8 +30,6 @@ module GraphQL
32
30
  RUBY
33
31
  end
34
32
 
35
- # rubocop:enable Development/NoEvalCop
36
-
37
33
  def platform_execute_field(platform_key, &block)
38
34
  @statsd.time(platform_key, &block)
39
35
  end
@@ -13,7 +13,7 @@ module GraphQL
13
13
  child_class.node_nullable(true)
14
14
  child_class.edges_nullable(true)
15
15
  child_class.edge_nullable(true)
16
- child_class.module_exec {
16
+ child_class.module_eval {
17
17
  self.edge_type = nil
18
18
  self.node_type = nil
19
19
  self.edge_class = nil
@@ -8,7 +8,7 @@ module GraphQL
8
8
  child_class.description("An edge in a connection.")
9
9
  child_class.field(:cursor, String, null: false, description: "A cursor for use in pagination.")
10
10
  child_class.extend(ClassMethods)
11
- child_class.class_exec { self.node_type = nil }
11
+ child_class.class_eval { self.node_type = nil }
12
12
  child_class.node_nullable(true)
13
13
  end
14
14
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.2.17"
3
+ VERSION = "2.3.0"
4
4
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.17
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2025-03-12 00:00:00.000000000 Z
11
+ date: 2024-03-20 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: base64
@@ -629,6 +630,7 @@ metadata:
629
630
  bug_tracker_uri: https://github.com/rmosolgo/graphql-ruby/issues
630
631
  mailing_list_uri: https://buttondown.email/graphql-ruby
631
632
  rubygems_mfa_required: 'true'
633
+ post_install_message:
632
634
  rdoc_options: []
633
635
  require_paths:
634
636
  - lib
@@ -643,7 +645,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
643
645
  - !ruby/object:Gem::Version
644
646
  version: '0'
645
647
  requirements: []
646
- rubygems_version: 3.6.3
648
+ rubygems_version: 3.5.3
649
+ signing_key:
647
650
  specification_version: 4
648
651
  summary: A GraphQL language and runtime for Ruby
649
652
  test_files: []