graphql 1.7.8 → 1.7.9

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