graphql-client 0.13.0 → 0.14.0

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