graphql 1.7.8 → 1.7.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 + 1, te, meta) };
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
- value = meta[:data][ts...te - 1].pack(PACK_DIRECTIVE).force_encoding(UTF_8_ENCODING)
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
- if value.is_a? InputObject
291
+ case value
292
+ when InputObject
292
293
  value.to_h
293
- elsif value.is_a? Array
294
+ when Array
294
295
  value.map do |v|
295
296
  serialize_value_for_hash v
296
297
  end
297
- elsif value.is_a? NullValue
298
+ when Enum
299
+ value.name
300
+ when NullValue
298
301
  nil
299
302
  else
300
303
  value
@@ -93,7 +93,7 @@ module GraphQL
93
93
  end
94
94
 
95
95
  def print_input_object(input_object)
96
- print_node(input_object.to_h)
96
+ "{#{input_object.arguments.map { |a| "#{a.name}: #{print(a.value)}" }.join(", ")}}"
97
97
  end
98
98
 
99
99
  def print_list_type(list_type)
@@ -12,7 +12,7 @@ module GraphQL
12
12
 
13
13
  def initialize(value:, name:, line:, col:, prev_token:)
14
14
  @name = name
15
- @value = value
15
+ @value = value.freeze
16
16
  @line = line
17
17
  @col = col
18
18
  @prev_token = prev_token
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)
@@ -193,6 +193,8 @@ module GraphQL
193
193
  nil
194
194
  when GraphQL::Language::Nodes::InputObject
195
195
  default_value.to_h
196
+ when Array
197
+ default_value.map { |v| build_default_value(v) }
196
198
  else
197
199
  default_value
198
200
  end
@@ -100,7 +100,7 @@ module GraphQL
100
100
  if reason.value == GraphQL::Directive::DEFAULT_DEPRECATION_REASON
101
101
  "@deprecated"
102
102
  else
103
- "@deprecated(reason: \"#{reason.value}\")"
103
+ "@deprecated(reason: #{reason.value.to_s.inspect})"
104
104
  end
105
105
  else
106
106
  super
@@ -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.reduce({}) do |memo, a|
20
- arg_value = a.value
21
- memo[a.name] = case arg_value
22
- when GraphQL::Language::Nodes::AbstractNode
23
- arg_value.to_query_string
24
- else
25
- GraphQL::Language.serialize(arg_value)
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
- memo
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, variables = fragment_definitions.find { |def_node, vars| def_node.name == spread_name }
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
- service = options.fetch(:service, 'ruby-graphql')
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
- span.resource = data[:multiplex].queries.map(&:selected_operation_name).join(', ')
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.7.8"
3
+ VERSION = "1.7.9"
4
4
  end
@@ -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) {
@@ -31,7 +31,9 @@ describe GraphQL::Directive do
31
31
  describe "child fields" do
32
32
  let(:query_string) { <<-GRAPHQL
33
33
  {
34
- __type(name: "Cheese") {
34
+ __type(name: """
35
+ Cheese
36
+ """) {
35
37
  fields { name }
36
38
  fields @skip(if: true) { isDeprecated }
37
39
  }
@@ -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
@@ -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: 'Use "BAR".'
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.8
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 00:00:00.000000000 Z
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