graphql 2.2.14 → 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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f35b5aadaa5d2666f3f41f3347a0dec2a3799e747e0f70c4075ab9dd21e6c965
4
- data.tar.gz: b5f5b6ed45a287fdb8a7e29b0e45b13fc3093c9040a85a5c139db3c9df1ca41e
3
+ metadata.gz: 881a55a1017c82563e75cf9898d44be453c1329c849f60b9538fdbd0f0d4b630
4
+ data.tar.gz: e99efcbffe7cab713e9d5fa7156c1f3bb56752b10ae35f9c5b23e705a27f90da
5
5
  SHA512:
6
- metadata.gz: 3bb546944fd6a331ffaeba21130ce6253d372300b3ff3b7252d7d8da934a5ae7b4f95503128855e19fa5f860ef4e9868103f4212e068c556f0f9908ed051f0dc
7
- data.tar.gz: 0d2f779dbfd416d92ec46774178f7556dfdac7e5c3e70b54a816de32af5f849493da8771e679e7555209e9ea2ab62702e574bc1a9416a328e9afbc74ac8a4306
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
@@ -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.14"
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.14
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-03-18 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