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.
- 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
|