graphql 2.2.5 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
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