graphql-client 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb27216cecd8b82430beda5f04f9eb8883be29ba245c0b7db606f835ede4a044
4
- data.tar.gz: 782d1321f053407d369e1a119feebf9799612b4103395c2ff72a17bb7680a6c2
3
+ metadata.gz: 342aa95830be1c53716c72962fad3c3785058f160404fba1aa603f9b94a96f92
4
+ data.tar.gz: c63ff270ab64ad1ee8a8ece5a36d20be9b12e138e86cc0a4eb88246bf2d4c2bb
5
5
  SHA512:
6
- metadata.gz: 251c12981e0af0c0b4c10261880b9c6f01aea92c2ccdd35a35cc484796354fbd8aeb365d077b93a41618269341e64bceb5a85985df3af623f12abeab6591db23
7
- data.tar.gz: 023c4081d943267049fc6f7b0be964445fc5f7bb65460015ac3d849177b75a76a1158eae4a67810c9d9ca3a3c5ca61f6f8c689fbafdc3a1010c6d912d9675433
6
+ metadata.gz: a701d2ad7b0776f43d04b63fa394d5470fe7f5a8d0d71467affd78d04069ec212249fc1e0ab9c86d82bdabadc750ac6eb63b7e6c77d3b842f82f5c6c88d81baf
7
+ data.tar.gz: 3033fcb57a59f3518436f0f903154f9eaaa070230afd6ba4f83d3c6c42904f71a503f92f42aa5b91932770c15bb8551a5ea7ce7130a9bf8ac7eca11756ef53ce
@@ -120,13 +120,21 @@ module GraphQL
120
120
 
121
121
  definition_dependencies = Set.new
122
122
 
123
+ # Replace Ruby constant reference with GraphQL fragment names,
124
+ # while populating `definition_dependencies` with
125
+ # GraphQL Fragment ASTs which this operation depends on
123
126
  str = str.gsub(/\.\.\.([a-zA-Z0-9_]+(::[a-zA-Z0-9_]+)*)/) do
124
127
  match = Regexp.last_match
125
128
  const_name = match[1]
126
129
 
127
130
  if str.match(/fragment\s*#{const_name}/)
131
+ # It's a fragment _definition_, not a fragment usage
128
132
  match[0]
129
133
  else
134
+ # It's a fragment spread, so we should load the fragment
135
+ # which corresponds to the spread.
136
+ # We depend on ActiveSupport to either find the already-loaded
137
+ # constant, or to load the constant by name
130
138
  begin
131
139
  fragment = ActiveSupport::Inflector.constantize(const_name)
132
140
  rescue NameError
@@ -135,6 +143,10 @@ module GraphQL
135
143
 
136
144
  case fragment
137
145
  when FragmentDefinition
146
+ # We found the fragment definition that this fragment spread belongs to.
147
+ # So, register the AST of this fragment in `definition_dependencies`
148
+ # and update the query string to valid GraphQL syntax,
149
+ # replacing the Ruby constant
138
150
  definition_dependencies.merge(fragment.document.definitions)
139
151
  "...#{fragment.definition_name}"
140
152
  else
@@ -157,10 +169,17 @@ module GraphQL
157
169
  doc = GraphQL.parse(str)
158
170
 
159
171
  document_types = DocumentTypes.analyze_types(self.schema, doc).freeze
160
- QueryTypename.insert_typename_fields(doc, types: document_types)
172
+ doc = QueryTypename.insert_typename_fields(doc, types: document_types)
161
173
 
162
174
  doc.definitions.each do |node|
163
- node.name ||= "__anonymous__"
175
+ if node.name.nil?
176
+ if node.respond_to?(:merge) # GraphQL 1.9 +
177
+ node_with_name = node.merge(name: "__anonymous__")
178
+ doc = doc.replace_child(node, node_with_name)
179
+ else
180
+ node.name = "__anonymous__"
181
+ end
182
+ end
164
183
  end
165
184
 
166
185
  document_dependencies = Language::Nodes::Document.new(definitions: doc.definitions + definition_dependencies.to_a)
@@ -192,7 +211,6 @@ module GraphQL
192
211
  raise TypeError, "unexpected #{node.class}"
193
212
  end
194
213
 
195
- node.name = nil if node.name == "__anonymous__"
196
214
  sliced_document = Language::DefinitionSlice.slice(document_dependencies, node.name)
197
215
  definition = Definition.for(
198
216
  client: self,
@@ -205,18 +223,26 @@ module GraphQL
205
223
  end
206
224
 
207
225
  name_hook = RenameNodeHook.new(definitions)
208
- visitor = Language::Visitor.new(doc)
226
+ visitor = Language::Visitor.new(document_dependencies)
209
227
  visitor[Language::Nodes::FragmentDefinition].leave << name_hook.method(:rename_node)
210
228
  visitor[Language::Nodes::OperationDefinition].leave << name_hook.method(:rename_node)
211
229
  visitor[Language::Nodes::FragmentSpread].leave << name_hook.method(:rename_node)
212
230
  visitor.visit
213
231
 
214
- doc.deep_freeze
232
+ if !doc.respond_to?(:merge)
233
+ doc.deep_freeze # 1.9 introduced immutable AST nodes, so we skip this on 1.9+
234
+ end
215
235
 
216
- document.definitions.concat(doc.definitions) if document_tracking_enabled
236
+ if document_tracking_enabled
237
+ if @document.respond_to?(:merge) # GraphQL 1.9+
238
+ @document = @document.merge(definitions: @document.definitions + doc.definitions)
239
+ else
240
+ @document.definitions.concat(doc.definitions)
241
+ end
242
+ end
217
243
 
218
- if definitions[nil]
219
- definitions[nil]
244
+ if definitions["__anonymous__"]
245
+ definitions["__anonymous__"]
220
246
  else
221
247
  Module.new do
222
248
  definitions.each do |name, definition|
@@ -235,7 +261,7 @@ module GraphQL
235
261
  definition = @definitions[node.name]
236
262
  if definition
237
263
  node.extend(LazyName)
238
- node.name = -> { definition.definition_name }
264
+ node.name_proc = -> { definition.definition_name }
239
265
  end
240
266
  end
241
267
  end
@@ -358,8 +384,10 @@ module GraphQL
358
384
  # name to point to a lazily defined Proc instead of a static string.
359
385
  module LazyName
360
386
  def name
361
- @name.call
387
+ @name_proc.call
362
388
  end
389
+
390
+ attr_writer :name_proc
363
391
  end
364
392
 
365
393
  private
@@ -13,33 +13,84 @@ module GraphQL
13
13
  # document - GraphQL::Language::Nodes::Document to modify
14
14
  # schema - Optional Map of GraphQL::Language::Nodes::Node to GraphQL::Type
15
15
  #
16
- # Returns nothing.
17
- def self.insert_typename_fields(document, types: {})
18
- on_selections = ->(node, _parent) do
19
- type = types[node]
20
-
21
- if node.selections.any?
22
- case type && type.unwrap
23
- when NilClass, GraphQL::InterfaceType, GraphQL::UnionType
24
- names = node_flatten_selections(node.selections).map { |s| s.respond_to?(:name) ? s.name : nil }
16
+ # Returns the document with `__typename` added to it
17
+ if GraphQL::Language::Nodes::AbstractNode.method_defined?(:merge)
18
+ # GraphQL 1.9 introduces a new visitor class
19
+ # and doesn't expose writer methods for node attributes.
20
+ # So, use the node mutation API instead.
21
+ class InsertTypenameVisitor < GraphQL::Language::Visitor
22
+ def initialize(document, types:)
23
+ @types = types
24
+ super(document)
25
+ end
26
+
27
+ def add_typename(node, parent)
28
+ type = @types[node]
29
+ type = type && type.unwrap
30
+
31
+ if (node.selections.any? && (type.nil? || type.is_a?(GraphQL::InterfaceType) || type.is_a?(GraphQL::UnionType))) ||
32
+ (node.selections.none? && type.is_a?(GraphQL::ObjectType))
33
+ names = QueryTypename.node_flatten_selections(node.selections).map { |s| s.respond_to?(:name) ? s.name : nil }
25
34
  names = Set.new(names.compact)
26
35
 
27
- unless names.include?("__typename")
28
- node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections
36
+ if names.include?("__typename")
37
+ yield(node, parent)
38
+ else
39
+ node_with_typename = node.merge(selections: [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections)
40
+ yield(node_with_typename, parent)
29
41
  end
42
+ else
43
+ yield(node, parent)
30
44
  end
31
- elsif type && type.unwrap.is_a?(GraphQL::ObjectType)
32
- node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")]
45
+ end
46
+
47
+ def on_operation_definition(node, parent)
48
+ add_typename(node, parent) { |n, p| super(n, p) }
49
+ end
50
+
51
+ def on_field(node, parent)
52
+ add_typename(node, parent) { |n, p| super(n, p) }
53
+ end
54
+
55
+ def on_fragment_definition(node, parent)
56
+ add_typename(node, parent) { |n, p| super(n, p) }
33
57
  end
34
58
  end
35
59
 
36
- visitor = GraphQL::Language::Visitor.new(document)
37
- visitor[GraphQL::Language::Nodes::Field].leave << on_selections
38
- visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << on_selections
39
- visitor[GraphQL::Language::Nodes::OperationDefinition].leave << on_selections
40
- visitor.visit
60
+ def self.insert_typename_fields(document, types: {})
61
+ visitor = InsertTypenameVisitor.new(document, types: types)
62
+ visitor.visit
63
+ visitor.result
64
+ end
65
+
66
+ else
67
+ def self.insert_typename_fields(document, types: {})
68
+ on_selections = ->(node, _parent) do
69
+ type = types[node]
70
+
71
+ if node.selections.any?
72
+ case type && type.unwrap
73
+ when NilClass, GraphQL::InterfaceType, GraphQL::UnionType
74
+ names = node_flatten_selections(node.selections).map { |s| s.respond_to?(:name) ? s.name : nil }
75
+ names = Set.new(names.compact)
41
76
 
42
- nil
77
+ unless names.include?("__typename")
78
+ node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections
79
+ end
80
+ end
81
+ elsif type && type.unwrap.is_a?(GraphQL::ObjectType)
82
+ node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")]
83
+ end
84
+ end
85
+
86
+ visitor = GraphQL::Language::Visitor.new(document)
87
+ visitor[GraphQL::Language::Nodes::Field].leave << on_selections
88
+ visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << on_selections
89
+ visitor[GraphQL::Language::Nodes::OperationDefinition].leave << on_selections
90
+ visitor.visit
91
+
92
+ document
93
+ end
43
94
  end
44
95
 
45
96
  def self.node_flatten_selections(selections)
@@ -41,7 +41,7 @@ module GraphQL
41
41
  if type = possible_types[typename]
42
42
  type.cast(value, errors)
43
43
  else
44
- raise InvariantError, "expected value to be one of (#{possible_types.keys.join(", ")}), but was #{typename}"
44
+ raise InvariantError, "expected value to be one of (#{possible_types.keys.join(", ")}), but was #{typename.inspect}"
45
45
  end
46
46
  when NilClass
47
47
  nil
@@ -10,6 +10,7 @@ module GraphQL
10
10
  #
11
11
  # Returns self Node.
12
12
  def deep_freeze
13
+ scalars # load internal state
13
14
  children.each(&:deep_freeze)
14
15
  scalars.each { |s| s && s.freeze }
15
16
  freeze
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-16 00:00:00.000000000 Z
11
+ date: 2018-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport