graphql 2.2.16 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 444884ff51b3699ea4b1b34cd89d3e686900ec53e59b502bd062ef295494271f
4
- data.tar.gz: d4bbddeb926a6106a38d029f7fbd23b63c2020ee371978aef8b0cf20313f47a1
3
+ metadata.gz: 881a55a1017c82563e75cf9898d44be453c1329c849f60b9538fdbd0f0d4b630
4
+ data.tar.gz: e99efcbffe7cab713e9d5fa7156c1f3bb56752b10ae35f9c5b23e705a27f90da
5
5
  SHA512:
6
- metadata.gz: c67c98945049adf941c6c9ed1819a9c8e85d3df9992d1186f1d6165a2b599ad8f51973bd34060e425c44edb58e542568810e7fa48acb1f726a1e837de423717b
7
- data.tar.gz: 78d57fe6de8cf176671088913d123d5d8986e98e37cc67b280d4ae9a3d98070cde2bd29cf2a4be777acd62653f2d0ce8204e303d6ee24b6d423fa538f23b6308
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
@@ -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
@@ -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
@@ -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
 
@@ -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)
@@ -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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.2.16"
3
+ VERSION = "2.3.0"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.16
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-17 00:00:00.000000000 Z
11
+ date: 2024-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -645,7 +645,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
645
645
  - !ruby/object:Gem::Version
646
646
  version: '0'
647
647
  requirements: []
648
- rubygems_version: 3.5.12
648
+ rubygems_version: 3.5.3
649
649
  signing_key:
650
650
  specification_version: 4
651
651
  summary: A GraphQL language and runtime for Ruby