graphql 2.2.5 → 2.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/templates/schema.erb +3 -0
- data/lib/graphql/analysis/ast/field_usage.rb +36 -9
- data/lib/graphql/analysis/ast/query_complexity.rb +3 -0
- data/lib/graphql/analysis/ast/visitor.rb +8 -0
- data/lib/graphql/analysis/ast.rb +10 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +6 -4
- data/lib/graphql/execution/interpreter/runtime.rb +93 -106
- data/lib/graphql/execution/interpreter.rb +90 -150
- data/lib/graphql/introspection/entry_points.rb +9 -3
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +2 -3
- data/lib/graphql/language/lexer.rb +48 -30
- data/lib/graphql/language/nodes.rb +11 -16
- data/lib/graphql/language/parser.rb +94 -45
- data/lib/graphql/language/printer.rb +4 -0
- data/lib/graphql/language.rb +60 -0
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/query/context.rb +30 -33
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +3 -3
- data/lib/graphql/schema/argument.rb +18 -2
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/field.rb +33 -30
- data/lib/graphql/schema/input_object.rb +1 -2
- data/lib/graphql/schema/interface.rb +5 -1
- data/lib/graphql/schema/loader.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/resolver.rb +19 -10
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema.rb +129 -29
- data/lib/graphql/static_validation/literal_validator.rb +1 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +0 -3
- data/lib/graphql/testing/helpers.rb +32 -6
- data/lib/graphql/tracing/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/prometheus_trace.rb +2 -2
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -2
- metadata +38 -23
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- 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,
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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
|
-
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
@
|
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)
|
data/lib/graphql/language.rb
CHANGED
@@ -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)-
|
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
|
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
|
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,
|
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
|