graphql 2.2.5 → 2.3.3

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/templates/schema.erb +3 -0
  3. data/lib/graphql/analysis/ast/field_usage.rb +36 -9
  4. data/lib/graphql/analysis/ast/query_complexity.rb +3 -0
  5. data/lib/graphql/analysis/ast/visitor.rb +8 -0
  6. data/lib/graphql/analysis/ast.rb +10 -1
  7. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  8. data/lib/graphql/coercion_error.rb +1 -9
  9. data/lib/graphql/dataloader/request.rb +5 -0
  10. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  11. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +6 -4
  12. data/lib/graphql/execution/interpreter/runtime.rb +93 -106
  13. data/lib/graphql/execution/interpreter.rb +90 -150
  14. data/lib/graphql/introspection/entry_points.rb +9 -3
  15. data/lib/graphql/introspection/schema_type.rb +3 -1
  16. data/lib/graphql/language/document_from_schema_definition.rb +2 -3
  17. data/lib/graphql/language/lexer.rb +48 -30
  18. data/lib/graphql/language/nodes.rb +11 -16
  19. data/lib/graphql/language/parser.rb +94 -45
  20. data/lib/graphql/language/printer.rb +4 -0
  21. data/lib/graphql/language.rb +60 -0
  22. data/lib/graphql/pagination/array_connection.rb +6 -6
  23. data/lib/graphql/query/context.rb +30 -33
  24. data/lib/graphql/query/validation_pipeline.rb +2 -2
  25. data/lib/graphql/query/variables.rb +3 -3
  26. data/lib/graphql/query.rb +3 -3
  27. data/lib/graphql/schema/argument.rb +18 -2
  28. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  29. data/lib/graphql/schema/build_from_definition.rb +9 -1
  30. data/lib/graphql/schema/field.rb +33 -30
  31. data/lib/graphql/schema/input_object.rb +1 -2
  32. data/lib/graphql/schema/interface.rb +5 -1
  33. data/lib/graphql/schema/loader.rb +2 -1
  34. data/lib/graphql/schema/member/has_arguments.rb +2 -2
  35. data/lib/graphql/schema/mutation.rb +7 -0
  36. data/lib/graphql/schema/resolver.rb +19 -10
  37. data/lib/graphql/schema/unique_within_type.rb +1 -1
  38. data/lib/graphql/schema.rb +129 -29
  39. data/lib/graphql/static_validation/literal_validator.rb +1 -2
  40. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  41. data/lib/graphql/static_validation/validator.rb +3 -0
  42. data/lib/graphql/subscriptions/serialize.rb +2 -0
  43. data/lib/graphql/subscriptions.rb +0 -3
  44. data/lib/graphql/testing/helpers.rb +32 -6
  45. data/lib/graphql/tracing/data_dog_trace.rb +21 -34
  46. data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
  47. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  48. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  49. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  50. data/lib/graphql/tracing/prometheus_trace.rb +2 -2
  51. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  52. data/lib/graphql/tracing.rb +3 -1
  53. data/lib/graphql/version.rb +1 -1
  54. data/lib/graphql.rb +10 -2
  55. metadata +38 -23
  56. data/lib/graphql/schema/base_64_bp.rb +0 -26
  57. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -12,8 +12,8 @@ module GraphQL
12
12
  class << self
13
13
  attr_accessor :cache
14
14
 
15
- def parse(graphql_str, filename: nil, trace: Tracing::NullTrace)
16
- self.new(graphql_str, filename: filename, trace: trace).parse
15
+ def parse(graphql_str, filename: nil, trace: Tracing::NullTrace, max_tokens: nil)
16
+ self.new(graphql_str, filename: filename, trace: trace, max_tokens: max_tokens).parse
17
17
  end
18
18
 
19
19
  def parse_file(filename, trace: Tracing::NullTrace)
@@ -27,14 +27,16 @@ module GraphQL
27
27
  end
28
28
  end
29
29
 
30
- def initialize(graphql_str, filename: nil, trace: Tracing::NullTrace)
30
+ def initialize(graphql_str, filename: nil, trace: Tracing::NullTrace, max_tokens: nil)
31
31
  if graphql_str.nil?
32
32
  raise GraphQL::ParseError.new("No query string was present", nil, nil, nil)
33
33
  end
34
- @lexer = Lexer.new(graphql_str, filename: filename)
34
+ @lexer = Lexer.new(graphql_str, filename: filename, max_tokens: max_tokens)
35
35
  @graphql_str = graphql_str
36
36
  @filename = filename
37
37
  @trace = trace
38
+ @dedup_identifiers = false
39
+ @lines_at = nil
38
40
  end
39
41
 
40
42
  def parse
@@ -47,9 +49,43 @@ module GraphQL
47
49
  end
48
50
  end
49
51
 
52
+ def line_at(pos)
53
+ line = lines_at.bsearch_index { |l| l >= pos }
54
+ if line.nil?
55
+ @lines_at.size + 1
56
+ else
57
+ line + 1
58
+ end
59
+ end
60
+
61
+ def column_at(pos)
62
+ next_line_idx = lines_at.bsearch_index { |l| l >= pos } || 0
63
+ if next_line_idx > 0
64
+ line_pos = @lines_at[next_line_idx - 1]
65
+ pos - line_pos
66
+ else
67
+ pos + 1
68
+ end
69
+ end
50
70
 
51
71
  private
52
72
 
73
+ # @return [Array<Integer>] Positions of each line break in the original string
74
+ def lines_at
75
+ @lines_at ||= begin
76
+ la = []
77
+ idx = 0
78
+ while idx
79
+ idx = @graphql_str.index("\n", idx)
80
+ if idx
81
+ la << idx
82
+ idx += 1
83
+ end
84
+ end
85
+ la
86
+ end
87
+ end
88
+
53
89
  attr_reader :token_name
54
90
 
55
91
  def advance_token
@@ -72,7 +108,7 @@ module GraphQL
72
108
  while !@lexer.eos?
73
109
  defns << definition
74
110
  end
75
- Document.new(pos: 0, definitions: defns, filename: @filename, source_string: @graphql_str)
111
+ Document.new(pos: 0, definitions: defns, filename: @filename, source: self)
76
112
  end
77
113
 
78
114
  def definition
@@ -94,7 +130,7 @@ module GraphQL
94
130
  directives: directives,
95
131
  selections: selections,
96
132
  filename: @filename,
97
- source_string: @graphql_str
133
+ source: self
98
134
  )
99
135
  when :QUERY, :MUTATION, :SUBSCRIPTION, :LCURLY
100
136
  op_loc = pos
@@ -121,7 +157,17 @@ module GraphQL
121
157
  value
122
158
  end
123
159
 
124
- defs << Nodes::VariableDefinition.new(pos: loc, name: var_name, type: var_type, default_value: default_value, filename: @filename, source_string: @graphql_str)
160
+ directives = parse_directives
161
+
162
+ defs << Nodes::VariableDefinition.new(
163
+ pos: loc,
164
+ name: var_name,
165
+ type: var_type,
166
+ default_value: default_value,
167
+ directives: directives,
168
+ filename: @filename,
169
+ source: self
170
+ )
125
171
  end
126
172
  expect_token(:RPAREN)
127
173
  defs
@@ -139,7 +185,7 @@ module GraphQL
139
185
  directives: directives,
140
186
  selections: selection_set,
141
187
  filename: @filename,
142
- source_string: @graphql_str
188
+ source: self
143
189
  )
144
190
  when :EXTEND
145
191
  loc = pos
@@ -149,7 +195,7 @@ module GraphQL
149
195
  advance_token
150
196
  name = parse_name
151
197
  directives = parse_directives
152
- ScalarTypeExtension.new(pos: loc, name: name, directives: directives, filename: @filename, source_string: @graphql_str)
198
+ ScalarTypeExtension.new(pos: loc, name: name, directives: directives, filename: @filename, source: self)
153
199
  when :TYPE
154
200
  advance_token
155
201
  name = parse_name
@@ -157,32 +203,32 @@ module GraphQL
157
203
  directives = parse_directives
158
204
  field_defns = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY
159
205
 
160
- ObjectTypeExtension.new(pos: loc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename, source_string: @graphql_str)
206
+ ObjectTypeExtension.new(pos: loc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename, source: self)
161
207
  when :INTERFACE
162
208
  advance_token
163
209
  name = parse_name
164
210
  directives = parse_directives
165
211
  interfaces = parse_implements
166
212
  fields_definition = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY
167
- InterfaceTypeExtension.new(pos: loc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename, source_string: @graphql_str)
213
+ InterfaceTypeExtension.new(pos: loc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename, source: self)
168
214
  when :UNION
169
215
  advance_token
170
216
  name = parse_name
171
217
  directives = parse_directives
172
218
  union_member_types = parse_union_members
173
- UnionTypeExtension.new(pos: loc, name: name, directives: directives, types: union_member_types, filename: @filename, source_string: @graphql_str)
219
+ UnionTypeExtension.new(pos: loc, name: name, directives: directives, types: union_member_types, filename: @filename, source: self)
174
220
  when :ENUM
175
221
  advance_token
176
222
  name = parse_name
177
223
  directives = parse_directives
178
224
  enum_values_definition = parse_enum_value_definitions
179
- Nodes::EnumTypeExtension.new(pos: loc, name: name, directives: directives, values: enum_values_definition, filename: @filename, source_string: @graphql_str)
225
+ Nodes::EnumTypeExtension.new(pos: loc, name: name, directives: directives, values: enum_values_definition, filename: @filename, source: self)
180
226
  when :INPUT
181
227
  advance_token
182
228
  name = parse_name
183
229
  directives = parse_directives
184
230
  input_fields_definition = parse_input_object_field_definitions
185
- InputObjectTypeExtension.new(pos: loc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source_string: @graphql_str)
231
+ InputObjectTypeExtension.new(pos: loc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source: self)
186
232
  when :SCHEMA
187
233
  advance_token
188
234
  directives = parse_directives
@@ -215,7 +261,7 @@ module GraphQL
215
261
  directives: directives,
216
262
  pos: loc,
217
263
  filename: @filename,
218
- source_string: @graphql_str,
264
+ source: self,
219
265
  )
220
266
  else
221
267
  expect_one_of([:SCHEMA, :SCALAR, :TYPE, :ENUM, :INPUT, :UNION, :INTERFACE])
@@ -248,7 +294,7 @@ module GraphQL
248
294
  end
249
295
  end
250
296
  expect_token :RCURLY
251
- SchemaDefinition.new(pos: loc, definition_pos: defn_loc, query: query, mutation: mutation, subscription: subscription, directives: directives, filename: @filename, source_string: @graphql_str)
297
+ SchemaDefinition.new(pos: loc, definition_pos: defn_loc, query: query, mutation: mutation, subscription: subscription, directives: directives, filename: @filename, source: self)
252
298
  when :DIRECTIVE
253
299
  advance_token
254
300
  expect_token :DIR_SIGN
@@ -261,12 +307,12 @@ module GraphQL
261
307
  false
262
308
  end
263
309
  expect_token :ON
264
- directive_locations = [DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename, source_string: @graphql_str)]
310
+ directive_locations = [DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename, source: self)]
265
311
  while at?(:PIPE)
266
312
  advance_token
267
- directive_locations << DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename, source_string: @graphql_str)
313
+ directive_locations << DirectiveLocation.new(pos: pos, name: parse_name, filename: @filename, source: self)
268
314
  end
269
- DirectiveDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, arguments: arguments_definition, locations: directive_locations, repeatable: repeatable, filename: @filename, source_string: @graphql_str)
315
+ DirectiveDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, arguments: arguments_definition, locations: directive_locations, repeatable: repeatable, filename: @filename, source: self)
270
316
  when :TYPE
271
317
  advance_token
272
318
  name = parse_name
@@ -274,37 +320,37 @@ module GraphQL
274
320
  directives = parse_directives
275
321
  field_defns = at?(:LCURLY) ? parse_field_definitions : EMPTY_ARRAY
276
322
 
277
- ObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename, source_string: @graphql_str)
323
+ ObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, interfaces: implements_interfaces, directives: directives, fields: field_defns, filename: @filename, source: self)
278
324
  when :INTERFACE
279
325
  advance_token
280
326
  name = parse_name
281
327
  interfaces = parse_implements
282
328
  directives = parse_directives
283
329
  fields_definition = parse_field_definitions
284
- InterfaceTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename, source_string: @graphql_str)
330
+ InterfaceTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: fields_definition, interfaces: interfaces, filename: @filename, source: self)
285
331
  when :UNION
286
332
  advance_token
287
333
  name = parse_name
288
334
  directives = parse_directives
289
335
  union_member_types = parse_union_members
290
- UnionTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, types: union_member_types, filename: @filename, source_string: @graphql_str)
336
+ UnionTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, types: union_member_types, filename: @filename, source: self)
291
337
  when :SCALAR
292
338
  advance_token
293
339
  name = parse_name
294
340
  directives = parse_directives
295
- ScalarTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, filename: @filename, source_string: @graphql_str)
341
+ ScalarTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, filename: @filename, source: self)
296
342
  when :ENUM
297
343
  advance_token
298
344
  name = parse_name
299
345
  directives = parse_directives
300
346
  enum_values_definition = parse_enum_value_definitions
301
- Nodes::EnumTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, values: enum_values_definition, filename: @filename, source_string: @graphql_str)
347
+ Nodes::EnumTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, values: enum_values_definition, filename: @filename, source: self)
302
348
  when :INPUT
303
349
  advance_token
304
350
  name = parse_name
305
351
  directives = parse_directives
306
352
  input_fields_definition = parse_input_object_field_definitions
307
- InputObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source_string: @graphql_str)
353
+ InputObjectTypeDefinition.new(pos: loc, definition_pos: defn_loc, description: desc, name: name, directives: directives, fields: input_fields_definition, filename: @filename, source: self)
308
354
  else
309
355
  expect_one_of([:SCHEMA, :SCALAR, :TYPE, :ENUM, :INPUT, :UNION, :INTERFACE])
310
356
  end
@@ -335,7 +381,7 @@ module GraphQL
335
381
  defn_loc = pos
336
382
  enum_value = parse_enum_name
337
383
  v_directives = parse_directives
338
- list << EnumValueDefinition.new(pos: v_loc, definition_pos: defn_loc, description: description, name: enum_value, directives: v_directives, filename: @filename, source_string: @graphql_str)
384
+ list << EnumValueDefinition.new(pos: v_loc, definition_pos: defn_loc, description: description, name: enum_value, directives: v_directives, filename: @filename, source: self)
339
385
  end
340
386
  expect_token :RCURLY
341
387
  list
@@ -386,7 +432,7 @@ module GraphQL
386
432
  type = self.type
387
433
  directives = parse_directives
388
434
 
389
- list << FieldDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, arguments: arguments_definition, type: type, directives: directives, filename: @filename, source_string: @graphql_str)
435
+ list << FieldDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, arguments: arguments_definition, type: type, directives: directives, filename: @filename, source: self)
390
436
  end
391
437
  expect_token :RCURLY
392
438
  list
@@ -420,7 +466,7 @@ module GraphQL
420
466
  nil
421
467
  end
422
468
  directives = parse_directives
423
- InputValueDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, type: type, default_value: default_value, directives: directives, filename: @filename, source_string: @graphql_str)
469
+ InputValueDefinition.new(pos: loc, definition_pos: defn_loc, description: description, name: name, type: type, default_value: default_value, directives: directives, filename: @filename, source: self)
424
470
  end
425
471
 
426
472
  def type
@@ -478,7 +524,7 @@ module GraphQL
478
524
 
479
525
  directives = parse_directives
480
526
 
481
- Nodes::InlineFragment.new(pos: loc, type: if_type, directives: directives, selections: selection_set, filename: @filename, source_string: @graphql_str)
527
+ Nodes::InlineFragment.new(pos: loc, type: if_type, directives: directives, selections: selection_set, filename: @filename, source: self)
482
528
  else
483
529
  name = parse_name_without_on
484
530
  directives = parse_directives
@@ -486,7 +532,7 @@ module GraphQL
486
532
  # Can this ever happen?
487
533
  # expect_token(:IDENTIFIER) if at?(:ON)
488
534
 
489
- FragmentSpread.new(pos: loc, name: name, directives: directives, filename: @filename, source_string: @graphql_str)
535
+ FragmentSpread.new(pos: loc, name: name, directives: directives, filename: @filename, source: self)
490
536
  end
491
537
  else
492
538
  loc = pos
@@ -504,7 +550,7 @@ module GraphQL
504
550
  directives = at?(:DIR_SIGN) ? parse_directives : nil
505
551
  selection_set = at?(:LCURLY) ? self.selection_set : nil
506
552
 
507
- Nodes::Field.new(pos: loc, field_alias: field_alias, name: name, arguments: arguments, directives: directives, selections: selection_set, filename: @filename, source_string: @graphql_str)
553
+ Nodes::Field.new(pos: loc, field_alias: field_alias, name: name, arguments: arguments, directives: directives, selections: selection_set, filename: @filename, source: self)
508
554
  end
509
555
  end
510
556
  expect_token(:RCURLY)
@@ -598,7 +644,7 @@ module GraphQL
598
644
  end
599
645
 
600
646
  def parse_type_name
601
- TypeName.new(pos: pos, name: parse_name, filename: @filename, source_string: @graphql_str)
647
+ TypeName.new(pos: pos, name: parse_name, filename: @filename, source: self)
602
648
  end
603
649
 
604
650
  def parse_directives
@@ -610,7 +656,7 @@ module GraphQL
610
656
  name = parse_name
611
657
  arguments = parse_arguments
612
658
 
613
- dirs << Nodes::Directive.new(pos: loc, name: name, arguments: arguments, filename: @filename, source_string: @graphql_str)
659
+ dirs << Nodes::Directive.new(pos: loc, name: name, arguments: arguments, filename: @filename, source: self)
614
660
  end
615
661
  dirs
616
662
  else
@@ -626,7 +672,7 @@ module GraphQL
626
672
  loc = pos
627
673
  name = parse_name
628
674
  expect_token(:COLON)
629
- args << Nodes::Argument.new(pos: loc, name: name, value: value, filename: @filename, source_string: @graphql_str)
675
+ args << Nodes::Argument.new(pos: loc, name: name, value: value, filename: @filename, source: self)
630
676
  end
631
677
  if args.empty?
632
678
  expect_token(:ARGUMENT_NAME) # At least one argument is required
@@ -660,9 +706,9 @@ module GraphQL
660
706
  false
661
707
  when :NULL
662
708
  advance_token
663
- NullValue.new(pos: pos, name: "null", filename: @filename, source_string: @graphql_str)
709
+ NullValue.new(pos: pos, name: "null", filename: @filename, source: self)
664
710
  when :IDENTIFIER
665
- Nodes::Enum.new(pos: pos, name: expect_token_value(:IDENTIFIER), filename: @filename, source_string: @graphql_str)
711
+ Nodes::Enum.new(pos: pos, name: expect_token_value(:IDENTIFIER), filename: @filename, source: self)
666
712
  when :LBRACKET
667
713
  advance_token
668
714
  list = []
@@ -679,14 +725,14 @@ module GraphQL
679
725
  loc = pos
680
726
  n = parse_name
681
727
  expect_token(:COLON)
682
- args << Argument.new(pos: loc, name: n, value: value, filename: @filename, source_string: @graphql_str)
728
+ args << Argument.new(pos: loc, name: n, value: value, filename: @filename, source: self)
683
729
  end
684
730
  expect_token(:RCURLY)
685
- InputObject.new(pos: start, arguments: args, filename: @filename, source_string: @graphql_str)
731
+ InputObject.new(pos: start, arguments: args, filename: @filename, source: self)
686
732
  when :VAR_SIGN
687
733
  loc = pos
688
734
  advance_token
689
- VariableIdentifier.new(pos: loc, name: parse_name, filename: @filename, source_string: @graphql_str)
735
+ VariableIdentifier.new(pos: loc, name: parse_name, filename: @filename, source: self)
690
736
  else
691
737
  expect_token(:VALUE)
692
738
  end
@@ -722,6 +768,9 @@ module GraphQL
722
768
  # Only use when we care about the expected token's value
723
769
  def expect_token_value(tok)
724
770
  token_value = @lexer.token_value
771
+ if @dedup_identifiers
772
+ token_value = -token_value
773
+ end
725
774
  expect_token(tok)
726
775
  token_value
727
776
  end
@@ -729,12 +778,12 @@ module GraphQL
729
778
  # token_value works for when the scanner matched something
730
779
  # which is usually fine and it's good for it to be fast at that.
731
780
  def debug_token_value
732
- if token_name && Lexer::Punctuation.const_defined?(token_name)
733
- Lexer::Punctuation.const_get(token_name)
734
- elsif token_name == :ELLIPSIS
735
- "..."
736
- else
737
- @lexer.token_value
781
+ @lexer.debug_token_value(token_name)
782
+ end
783
+ class SchemaParser < Parser
784
+ def initialize(*args, **kwargs)
785
+ super
786
+ @dedup_identifiers = true
738
787
  end
739
788
  end
740
789
  end
@@ -208,6 +208,10 @@ module GraphQL
208
208
  print_string(" = ")
209
209
  print_node(variable_definition.default_value)
210
210
  end
211
+ variable_definition.directives.each do |dir|
212
+ print_string(" ")
213
+ print_directive(dir)
214
+ end
211
215
  end
212
216
 
213
217
  def print_variable_identifier(variable_identifier)
@@ -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
@@ -32,6 +33,65 @@ module GraphQL
32
33
  else
33
34
  JSON.generate(value, quirks_mode: true)
34
35
  end
36
+ rescue JSON::GeneratorError
37
+ if Float::INFINITY == value
38
+ "Infinity"
39
+ else
40
+ raise
41
+ end
42
+ end
43
+
44
+ # Returns a new string if any single-quoted newlines were escaped.
45
+ # Otherwise, returns `query_str` unchanged.
46
+ # @return [String]
47
+ def self.escape_single_quoted_newlines(query_str)
48
+ scanner = StringScanner.new(query_str)
49
+ inside_single_quoted_string = false
50
+ new_query_str = nil
51
+ while !scanner.eos?
52
+ if (match = scanner.scan(/(?:\\"|[^"\n\r]|""")+/m)) && new_query_str
53
+ new_query_str << match
54
+ elsif scanner.scan('"')
55
+ new_query_str && (new_query_str << '"')
56
+ inside_single_quoted_string = !inside_single_quoted_string
57
+ elsif scanner.scan("\n")
58
+ if inside_single_quoted_string
59
+ new_query_str ||= query_str[0, scanner.pos - 1]
60
+ new_query_str << '\\n'
61
+ else
62
+ new_query_str && (new_query_str << "\n")
63
+ end
64
+ elsif scanner.scan("\r")
65
+ if inside_single_quoted_string
66
+ new_query_str ||= query_str[0, scanner.pos - 1]
67
+ new_query_str << '\\r'
68
+ else
69
+ new_query_str && (new_query_str << "\r")
70
+ end
71
+ elsif scanner.eos?
72
+ break
73
+ else
74
+ raise ArgumentError, "Unmatchable string scanner segment: #{scanner.rest.inspect}"
75
+ end
76
+ end
77
+ new_query_str || query_str
78
+ end
79
+
80
+ INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP = %r{
81
+ (
82
+ ((?<num>#{Lexer::INT_REGEXP}(#{Lexer::FLOAT_EXP_REGEXP})?)(?<name>#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:)
83
+ |
84
+ ((?<num>#{Lexer::INT_REGEXP}#{Lexer::FLOAT_DECIMAL_REGEXP}#{Lexer::FLOAT_EXP_REGEXP})(?<name>#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:)
85
+ |
86
+ ((?<num>#{Lexer::INT_REGEXP}#{Lexer::FLOAT_DECIMAL_REGEXP})(?<name>#{Lexer::IDENTIFIER_REGEXP})#{Lexer::IGNORE_REGEXP}:)
87
+ )}x
88
+
89
+ def self.add_space_between_numbers_and_names(query_str)
90
+ if query_str.match?(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP)
91
+ query_str.gsub(INVALID_NUMBER_FOLLOWED_BY_NAME_REGEXP, "\\k<num> \\k<name>:")
92
+ else
93
+ query_str
94
+ end
35
95
  end
36
96
  end
37
97
  end
@@ -35,10 +35,10 @@ module GraphQL
35
35
  def load_nodes
36
36
  @nodes ||= begin
37
37
  sliced_nodes = if before && after
38
- end_idx = index_from_cursor(before)-1
38
+ end_idx = index_from_cursor(before) - 2
39
39
  end_idx < 0 ? [] : items[index_from_cursor(after)..end_idx] || []
40
40
  elsif before
41
- end_idx = index_from_cursor(before)-2
41
+ end_idx = index_from_cursor(before) - 2
42
42
  end_idx < 0 ? [] : items[0..end_idx] || []
43
43
  elsif after
44
44
  items[index_from_cursor(after)..-1] || []
@@ -56,12 +56,12 @@ module GraphQL
56
56
  false
57
57
  end
58
58
 
59
- @has_next_page = if first
60
- # There are more items after these items
61
- sliced_nodes.count > first
62
- elsif before
59
+ @has_next_page = if before
63
60
  # The original array is longer than the `before` index
64
61
  index_from_cursor(before) < items.length + 1
62
+ elsif first
63
+ # There are more items after these items
64
+ sliced_nodes.count > first
65
65
  else
66
66
  false
67
67
  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]
@@ -14,7 +14,7 @@ module GraphQL
14
14
  #
15
15
  # @api private
16
16
  class ValidationPipeline
17
- attr_reader :max_depth, :max_complexity
17
+ attr_reader :max_depth, :max_complexity, :validate_timeout_remaining
18
18
 
19
19
  def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
20
20
  @validation_errors = []
@@ -71,7 +71,7 @@ module GraphQL
71
71
  validator = @query.static_validator || @schema.static_validator
72
72
  validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
73
73
  @validation_errors.concat(validation_result[:errors])
74
-
74
+ @validate_timeout_remaining = validation_result[:remaining_timeout]
75
75
  if @validation_errors.empty?
76
76
  @validation_errors.concat(@query.variables.errors)
77
77
  end
@@ -26,7 +26,7 @@ module GraphQL
26
26
  # - Then, fall back to the default value from the query string
27
27
  # If it's still nil, raise an error if it's required.
28
28
  variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
29
- if variable_type.nil?
29
+ if variable_type.nil? || !variable_type.unwrap.kind.input?
30
30
  # Pass -- it will get handled by a validator
31
31
  else
32
32
  variable_name = ast_variable.name
@@ -80,12 +80,12 @@ module GraphQL
80
80
  else
81
81
  val
82
82
  end
83
- end
83
+ end
84
84
 
85
85
  def add_max_errors_reached_message
86
86
  message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
87
87
  validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
88
- errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
88
+ errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
89
89
  end
90
90
  end
91
91
  end
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
@@ -317,7 +317,7 @@ module GraphQL
317
317
  end
318
318
 
319
319
  def_delegators :validation_pipeline, :validation_errors,
320
- :analyzers, :ast_analyzers, :max_depth, :max_complexity
320
+ :analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining
321
321
 
322
322
  attr_accessor :analysis_errors
323
323
  def valid?
@@ -395,7 +395,7 @@ module GraphQL
395
395
  parse_error = nil
396
396
  @document ||= begin
397
397
  if query_string
398
- GraphQL.parse(query_string, trace: self.current_trace)
398
+ GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens)
399
399
  end
400
400
  rescue GraphQL::ParseError => err
401
401
  parse_error = err