graphql 1.7.8 → 1.7.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/base_type.rb +6 -5
- data/lib/graphql/compatibility/query_parser_specification.rb +7 -0
- data/lib/graphql/field.rb +3 -3
- data/lib/graphql/internal_representation/node.rb +32 -13
- data/lib/graphql/internal_representation/visit.rb +3 -6
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/language/block_string.rb +47 -0
- data/lib/graphql/language/lexer.rb +129 -68
- data/lib/graphql/language/lexer.rl +13 -4
- data/lib/graphql/language/nodes.rb +6 -3
- data/lib/graphql/language/printer.rb +1 -1
- data/lib/graphql/language/token.rb +1 -1
- data/lib/graphql/query.rb +1 -1
- data/lib/graphql/schema/build_from_definition.rb +2 -0
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +15 -8
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +11 -1
- data/lib/graphql/tracing/data_dog_tracing.rb +13 -9
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/backtrace_spec.rb +10 -0
- data/spec/graphql/directive_spec.rb +3 -1
- data/spec/graphql/language/block_string_spec.rb +70 -0
- data/spec/graphql/language/lexer_spec.rb +9 -0
- data/spec/graphql/query_spec.rb +1 -1
- data/spec/graphql/schema/printer_spec.rb +48 -5
- metadata +19 -2
@@ -34,6 +34,9 @@
|
|
34
34
|
RBRACKET = ']';
|
35
35
|
COLON = ':';
|
36
36
|
QUOTE = '"';
|
37
|
+
BLOCK_QUOTE = '"""';
|
38
|
+
ESCAPED_BLOCK_QUOTE = '\\"""';
|
39
|
+
BLOCK_STRING_CHAR = (ESCAPED_BLOCK_QUOTE | ^'"""');
|
37
40
|
ESCAPED_QUOTE = '\\"';
|
38
41
|
STRING_CHAR = (ESCAPED_QUOTE | ^'"');
|
39
42
|
VAR_SIGN = '$';
|
@@ -44,7 +47,7 @@
|
|
44
47
|
PIPE = '|';
|
45
48
|
|
46
49
|
QUOTED_STRING = QUOTE STRING_CHAR* QUOTE;
|
47
|
-
|
50
|
+
BLOCK_STRING = BLOCK_QUOTE BLOCK_STRING_CHAR* BLOCK_QUOTE;
|
48
51
|
# catch-all for anything else. must be at the bottom for precedence.
|
49
52
|
UNKNOWN_CHAR = /./;
|
50
53
|
|
@@ -75,7 +78,8 @@
|
|
75
78
|
RBRACKET => { emit(:RBRACKET, ts, te, meta) };
|
76
79
|
LBRACKET => { emit(:LBRACKET, ts, te, meta) };
|
77
80
|
COLON => { emit(:COLON, ts, te, meta) };
|
78
|
-
QUOTED_STRING => { emit_string(ts
|
81
|
+
QUOTED_STRING => { emit_string(ts, te, meta, block: false) };
|
82
|
+
BLOCK_STRING => { emit_string(ts, te, meta, block: true) };
|
79
83
|
VAR_SIGN => { emit(:VAR_SIGN, ts, te, meta) };
|
80
84
|
DIR_SIGN => { emit(:DIR_SIGN, ts, te, meta) };
|
81
85
|
ELLIPSIS => { emit(:ELLIPSIS, ts, te, meta) };
|
@@ -188,8 +192,13 @@ module GraphQL
|
|
188
192
|
PACK_DIRECTIVE = "c*"
|
189
193
|
UTF_8_ENCODING = "UTF-8"
|
190
194
|
|
191
|
-
def self.emit_string(ts, te, meta)
|
192
|
-
|
195
|
+
def self.emit_string(ts, te, meta, block:)
|
196
|
+
quotes_length = block ? 3 : 1
|
197
|
+
ts += quotes_length
|
198
|
+
value = meta[:data][ts...te - quotes_length].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING)
|
199
|
+
if block
|
200
|
+
value = GraphQL::Language::BlockString.trim_whitespace(value)
|
201
|
+
end
|
193
202
|
if value !~ VALID_STRING
|
194
203
|
meta[:tokens] << token = GraphQL::Language::Token.new(
|
195
204
|
name: :BAD_UNICODE_ESCAPE,
|
@@ -288,13 +288,16 @@ module GraphQL
|
|
288
288
|
private
|
289
289
|
|
290
290
|
def serialize_value_for_hash(value)
|
291
|
-
|
291
|
+
case value
|
292
|
+
when InputObject
|
292
293
|
value.to_h
|
293
|
-
|
294
|
+
when Array
|
294
295
|
value.map do |v|
|
295
296
|
serialize_value_for_hash v
|
296
297
|
end
|
297
|
-
|
298
|
+
when Enum
|
299
|
+
value.name
|
300
|
+
when NullValue
|
298
301
|
nil
|
299
302
|
else
|
300
303
|
value
|
data/lib/graphql/query.rb
CHANGED
@@ -157,7 +157,7 @@ module GraphQL
|
|
157
157
|
def result
|
158
158
|
if !@executed
|
159
159
|
with_prepared_ast {
|
160
|
-
Execution::Multiplex.run_queries(@schema, [self])
|
160
|
+
Execution::Multiplex.run_queries(@schema, [self], context: @context)
|
161
161
|
}
|
162
162
|
end
|
163
163
|
@result ||= Query::Result.new(query: self, values: @result_values)
|
@@ -2,6 +2,9 @@
|
|
2
2
|
module GraphQL
|
3
3
|
module StaticValidation
|
4
4
|
class FieldsWillMerge
|
5
|
+
# Special handling for fields without arguments
|
6
|
+
NO_ARGS = {}.freeze
|
7
|
+
|
5
8
|
def validate(context)
|
6
9
|
context.each_irep_node do |node|
|
7
10
|
if node.ast_nodes.size > 1
|
@@ -16,15 +19,19 @@ module GraphQL
|
|
16
19
|
|
17
20
|
# Check for incompatible / non-identical arguments on this node:
|
18
21
|
args = node.ast_nodes.map do |n|
|
19
|
-
n.arguments.
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
if n.arguments.any?
|
23
|
+
n.arguments.reduce({}) do |memo, a|
|
24
|
+
arg_value = a.value
|
25
|
+
memo[a.name] = case arg_value
|
26
|
+
when GraphQL::Language::Nodes::AbstractNode
|
27
|
+
arg_value.to_query_string
|
28
|
+
else
|
29
|
+
GraphQL::Language.serialize(arg_value)
|
30
|
+
end
|
31
|
+
memo
|
26
32
|
end
|
27
|
-
|
33
|
+
else
|
34
|
+
NO_ARGS
|
28
35
|
end
|
29
36
|
end
|
30
37
|
args.uniq!
|
@@ -102,7 +102,17 @@ module GraphQL
|
|
102
102
|
def follow_spreads(node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments)
|
103
103
|
spreads = spreads_for_context[node] - visited_fragments
|
104
104
|
spreads.each do |spread_name|
|
105
|
-
def_node
|
105
|
+
def_node = nil
|
106
|
+
variables = nil
|
107
|
+
# Implement `.find` by hand to avoid Ruby's internal allocations
|
108
|
+
fragment_definitions.each do |frag_def_node, vars|
|
109
|
+
if frag_def_node.name == spread_name
|
110
|
+
def_node = frag_def_node
|
111
|
+
variables = vars
|
112
|
+
break
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
106
116
|
next if !def_node
|
107
117
|
visited_fragments << spread_name
|
108
118
|
variables.each do |name, child_usage|
|
@@ -15,17 +15,12 @@ module GraphQL
|
|
15
15
|
}
|
16
16
|
|
17
17
|
def platform_trace(platform_key, key, data)
|
18
|
-
|
18
|
+
tracer.trace(platform_key, service: service_name) do |span|
|
19
|
+
span.span_type = 'custom'
|
19
20
|
|
20
|
-
pin = Datadog::Pin.get_from(self)
|
21
|
-
unless pin
|
22
|
-
pin = Datadog::Pin.new(service)
|
23
|
-
pin.onto(self)
|
24
|
-
end
|
25
|
-
|
26
|
-
pin.tracer.trace(platform_key, service: pin.service) do |span|
|
27
21
|
if key == 'execute_multiplex'
|
28
|
-
|
22
|
+
operations = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
|
23
|
+
span.resource = operations unless operations.empty?
|
29
24
|
end
|
30
25
|
|
31
26
|
if key == 'execute_query'
|
@@ -33,10 +28,19 @@ module GraphQL
|
|
33
28
|
span.set_tag(:selected_operation_type, data[:query].selected_operation.operation_type)
|
34
29
|
span.set_tag(:query_string, data[:query].query_string)
|
35
30
|
end
|
31
|
+
|
36
32
|
yield
|
37
33
|
end
|
38
34
|
end
|
39
35
|
|
36
|
+
def service_name
|
37
|
+
options.fetch(:service, 'ruby-graphql')
|
38
|
+
end
|
39
|
+
|
40
|
+
def tracer
|
41
|
+
options.fetch(:tracer, Datadog.tracer)
|
42
|
+
end
|
43
|
+
|
40
44
|
def platform_field_key(type, field)
|
41
45
|
"#{type.name}.#{field.name}"
|
42
46
|
end
|
data/lib/graphql/version.rb
CHANGED
@@ -126,6 +126,16 @@ describe GraphQL::Backtrace do
|
|
126
126
|
assert_includes err.message, "more lines"
|
127
127
|
end
|
128
128
|
|
129
|
+
it "annotates errors from Query#result" do
|
130
|
+
query_str = "query StrField { field2 { strField } __typename }"
|
131
|
+
context = { backtrace: true }
|
132
|
+
query = GraphQL::Query.new(schema, query_str, context: context)
|
133
|
+
err = assert_raises(GraphQL::Backtrace::TracedError) {
|
134
|
+
query.result
|
135
|
+
}
|
136
|
+
assert_instance_of RuntimeError, err.cause
|
137
|
+
end
|
138
|
+
|
129
139
|
it "annotates errors inside lazy resolution" do
|
130
140
|
# Test context-based flag
|
131
141
|
err = assert_raises(GraphQL::Backtrace::TracedError) {
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
describe GraphQL::Language::BlockString do
|
5
|
+
describe "trimming whitespace" do
|
6
|
+
def trim_whitespace(str)
|
7
|
+
GraphQL::Language::BlockString.trim_whitespace(str)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "matches the examples in graphql-js" do
|
11
|
+
# these are taken from:
|
12
|
+
# https://github.com/graphql/graphql-js/blob/36ec0e9d34666362ff0e2b2b18edeb98e3c9abee/src/language/__tests__/blockStringValue-test.js#L12
|
13
|
+
# A set of [before, after] pairs:
|
14
|
+
examples = [
|
15
|
+
[
|
16
|
+
# Removes common whitespace:
|
17
|
+
"
|
18
|
+
Hello,
|
19
|
+
World!
|
20
|
+
|
21
|
+
Yours,
|
22
|
+
GraphQL.
|
23
|
+
",
|
24
|
+
"Hello,\n World!\n\nYours,\n GraphQL."
|
25
|
+
],
|
26
|
+
[
|
27
|
+
# Removes leading and trailing newlines:
|
28
|
+
"
|
29
|
+
|
30
|
+
Hello,
|
31
|
+
World!
|
32
|
+
|
33
|
+
Yours,
|
34
|
+
GraphQL.
|
35
|
+
|
36
|
+
",
|
37
|
+
"Hello,\n World!\n\nYours,\n GraphQL."
|
38
|
+
],
|
39
|
+
[
|
40
|
+
# Removes blank lines (with whitespace _and_ newlines:)
|
41
|
+
"\n \n
|
42
|
+
Hello,
|
43
|
+
World!
|
44
|
+
|
45
|
+
Yours,
|
46
|
+
GraphQL.
|
47
|
+
|
48
|
+
\n \n",
|
49
|
+
"Hello,\n World!\n\nYours,\n GraphQL."
|
50
|
+
],
|
51
|
+
[
|
52
|
+
# Retains indentation from the first line
|
53
|
+
" Hello,\n World!\n\n Yours,\n GraphQL.",
|
54
|
+
" Hello,\n World!\n\nYours,\n GraphQL.",
|
55
|
+
],
|
56
|
+
[
|
57
|
+
# Doesn't alter trailing spaces
|
58
|
+
"\n \n Hello, \n World! \n\n Yours, \n GraphQL. ",
|
59
|
+
"Hello, \n World! \n\nYours, \n GraphQL. ",
|
60
|
+
|
61
|
+
],
|
62
|
+
]
|
63
|
+
|
64
|
+
examples.each_with_index do |(before, after), idx|
|
65
|
+
transformed_str = trim_whitespace(before)
|
66
|
+
assert_equal(after, transformed_str, "Example ##{idx + 1}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -26,6 +26,15 @@ describe GraphQL::Language::Lexer do
|
|
26
26
|
assert_equal tokens[0], tokens[1].prev_token
|
27
27
|
end
|
28
28
|
|
29
|
+
describe "block strings" do
|
30
|
+
let(:query_string) { %|{ a(b: """\nc\n d\n""")}|}
|
31
|
+
|
32
|
+
it "tokenizes them" do
|
33
|
+
str_token = tokens[5]
|
34
|
+
assert_equal "c\n d", str_token.value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
29
38
|
it "unescapes escaped characters" do
|
30
39
|
assert_equal "\" \\ / \b \f \n \r \t", subject.tokenize('"\\" \\\\ \\/ \\b \\f \\n \\r \\t"').first.to_s
|
31
40
|
end
|
data/spec/graphql/query_spec.rb
CHANGED
@@ -85,7 +85,7 @@ describe GraphQL::Query do
|
|
85
85
|
operation_name: operation_name,
|
86
86
|
max_depth: max_depth,
|
87
87
|
)
|
88
|
-
query.query_string = '{ __type(name: "Cheese") { name } }'
|
88
|
+
query.query_string = '{ __type(name: """Cheese""") { name } }'
|
89
89
|
assert_equal "Cheese", query.result["data"] ["__type"]["name"]
|
90
90
|
end
|
91
91
|
end
|
@@ -14,7 +14,11 @@ describe GraphQL::Schema::Printer do
|
|
14
14
|
|
15
15
|
value "FOO", value: :foo
|
16
16
|
value "BAR", value: :bar
|
17
|
-
value "BAZ", deprecation_reason:
|
17
|
+
value "BAZ", deprecation_reason: <<-REASON
|
18
|
+
Use "BAR" instead.
|
19
|
+
|
20
|
+
It's the replacement for this value.
|
21
|
+
REASON
|
18
22
|
value "WOZ", deprecation_reason: GraphQL::Directive::DEFAULT_DEPRECATION_REASON
|
19
23
|
end
|
20
24
|
|
@@ -352,7 +356,6 @@ SCHEMA
|
|
352
356
|
custom_mutation = schema.mutation.redefine(name: "MyMutationRoot")
|
353
357
|
custom_schema = schema.redefine(mutation: custom_mutation)
|
354
358
|
|
355
|
-
|
356
359
|
expected = <<SCHEMA
|
357
360
|
schema {
|
358
361
|
query: Query
|
@@ -389,7 +392,7 @@ type Audio {
|
|
389
392
|
|
390
393
|
enum Choice {
|
391
394
|
BAR
|
392
|
-
BAZ @deprecated(reason: "Use "BAR"
|
395
|
+
BAZ @deprecated(reason: "Use \\\"BAR\\\" instead.\\n\\nIt's the replacement for this value.\\n")
|
393
396
|
FOO
|
394
397
|
WOZ @deprecated
|
395
398
|
}
|
@@ -438,7 +441,7 @@ interface Node {
|
|
438
441
|
type Post {
|
439
442
|
body: String!
|
440
443
|
comments: [Comment!]
|
441
|
-
comments_count: Int! @deprecated(reason: "Use "comments".")
|
444
|
+
comments_count: Int! @deprecated(reason: "Use \\\"comments\\\".")
|
442
445
|
id: ID!
|
443
446
|
title: String!
|
444
447
|
}
|
@@ -600,7 +603,7 @@ SCHEMA
|
|
600
603
|
type Post {
|
601
604
|
body: String!
|
602
605
|
comments: [Comment!]
|
603
|
-
comments_count: Int! @deprecated(reason: "Use "comments".")
|
606
|
+
comments_count: Int! @deprecated(reason: "Use \\\"comments\\\".")
|
604
607
|
id: ID!
|
605
608
|
title: String!
|
606
609
|
}
|
@@ -608,4 +611,44 @@ SCHEMA
|
|
608
611
|
assert_equal expected.chomp, GraphQL::Schema::Printer.new(schema).print_type(schema.types['Post'])
|
609
612
|
end
|
610
613
|
end
|
614
|
+
|
615
|
+
describe "#print_directive" do
|
616
|
+
it "prints the deprecation reason in a single line escaped string including line breaks" do
|
617
|
+
expected = <<SCHEMA
|
618
|
+
enum Choice {
|
619
|
+
BAR
|
620
|
+
BAZ @deprecated(reason: "Use \\\"BAR\\\" instead.\\n\\nIt's the replacement for this value.\\n")
|
621
|
+
FOO
|
622
|
+
WOZ @deprecated
|
623
|
+
}
|
624
|
+
|
625
|
+
type Subscription {
|
626
|
+
}
|
627
|
+
|
628
|
+
input Varied {
|
629
|
+
bool: Boolean
|
630
|
+
enum: Choice = FOO
|
631
|
+
float: Float
|
632
|
+
int: Int
|
633
|
+
}
|
634
|
+
SCHEMA
|
635
|
+
|
636
|
+
only_filter = ->(member, ctx) {
|
637
|
+
case member
|
638
|
+
when GraphQL::ScalarType
|
639
|
+
true
|
640
|
+
when GraphQL::BaseType
|
641
|
+
ctx[:names].include?(member.name)
|
642
|
+
when GraphQL::Argument
|
643
|
+
member.name != "id"
|
644
|
+
else
|
645
|
+
true
|
646
|
+
end
|
647
|
+
}
|
648
|
+
|
649
|
+
context = { names: ["Varied", "Choice", "Subscription"] }
|
650
|
+
|
651
|
+
assert_equal expected.chomp, GraphQL::Schema::Printer.new(schema, context: context, only: only_filter).print_schema
|
652
|
+
end
|
653
|
+
end
|
611
654
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-01
|
11
|
+
date: 2018-02-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -136,6 +136,20 @@ dependencies:
|
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: 3.0.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: memory_profiler
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
name: minitest
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -417,6 +431,7 @@ files:
|
|
417
431
|
- lib/graphql/invalid_name_error.rb
|
418
432
|
- lib/graphql/invalid_null_error.rb
|
419
433
|
- lib/graphql/language.rb
|
434
|
+
- lib/graphql/language/block_string.rb
|
420
435
|
- lib/graphql/language/comments.rb
|
421
436
|
- lib/graphql/language/definition_slice.rb
|
422
437
|
- lib/graphql/language/document_from_schema_definition.rb
|
@@ -899,6 +914,7 @@ files:
|
|
899
914
|
- spec/graphql/introspection/schema_type_spec.rb
|
900
915
|
- spec/graphql/introspection/type_by_name_field_spec.rb
|
901
916
|
- spec/graphql/introspection/type_type_spec.rb
|
917
|
+
- spec/graphql/language/block_string_spec.rb
|
902
918
|
- spec/graphql/language/definition_slice_spec.rb
|
903
919
|
- spec/graphql/language/document_from_schema_definition_spec.rb
|
904
920
|
- spec/graphql/language/equality_spec.rb
|
@@ -1370,6 +1386,7 @@ test_files:
|
|
1370
1386
|
- spec/graphql/introspection/schema_type_spec.rb
|
1371
1387
|
- spec/graphql/introspection/type_by_name_field_spec.rb
|
1372
1388
|
- spec/graphql/introspection/type_type_spec.rb
|
1389
|
+
- spec/graphql/language/block_string_spec.rb
|
1373
1390
|
- spec/graphql/language/definition_slice_spec.rb
|
1374
1391
|
- spec/graphql/language/document_from_schema_definition_spec.rb
|
1375
1392
|
- spec/graphql/language/equality_spec.rb
|