graphql-client 0.2.6 → 0.3.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
  SHA1:
3
- metadata.gz: 6fb2c7543a34c26a3ffa6718ddb35a4e4ae9972c
4
- data.tar.gz: f879a3fff89104e8c5eff98ba15c62ec2694de17
3
+ metadata.gz: 490142029326d682bd6956d1f825d9429aa49389
4
+ data.tar.gz: 3cc776110140c024c801f8b3a9eb52bf72cc924f
5
5
  SHA512:
6
- metadata.gz: d0b95e7dd51deebc8edd667ac3b824f270173992f5b8d7017eb96490c0a9eab6e4fdee58a989209640062e3f70a74f52e607a056b730ca34eeb6bc6599ef6d99
7
- data.tar.gz: 2c21b4f7ec54721953afaeb88f41f10af9b13006cfbaca45e6573649df23b464afa39d6208d4dacc29ce0bfdc558a67290e3915afd6d094ea7aa7a50000137a0
6
+ metadata.gz: 2e3cba4f02d0fff593cadcbd0d39bcdecb0576f087bb9088f2dc5902426bb54b7829ea3eb7d8d96c86fae155eb6ba248c5414ba24006834c8a4d806063e1be4c
7
+ data.tar.gz: bb8b0a56f1f1d0be7a4315aa626af65a08e7285adb03fb02e382a0fb6fdb53e67326ed6654f171709f267897141e396813ac2245f6c45097157b78f492dba7da
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  require "active_support/inflector"
2
3
  require "active_support/notifications"
3
4
  require "graphql"
4
5
  require "graphql/client/error"
5
6
  require "graphql/client/errors"
6
7
  require "graphql/client/query_result"
8
+ require "graphql/client/query_typename"
7
9
  require "graphql/client/response"
8
10
  require "graphql/language/nodes/deep_freeze_ext"
9
11
  require "json"
@@ -95,9 +97,11 @@ module GraphQL
95
97
  end
96
98
  end
97
99
 
98
- def initialize(node:, document:)
100
+ def initialize(node:, document:, schema:, document_types:)
99
101
  @definition_node = node
100
102
  @document = document
103
+ @schema = schema
104
+ @document_types = document_types
101
105
  end
102
106
 
103
107
  # Internal: Get underlying operation or fragment defintion AST node for
@@ -130,13 +134,15 @@ module GraphQL
130
134
  # and any FragmentDefinition dependencies.
131
135
  attr_reader :document
132
136
 
137
+ attr_reader :schema
138
+
133
139
  def new(*args)
134
140
  type.new(*args)
135
141
  end
136
142
 
137
143
  def type
138
144
  # TODO: Fix type indirection
139
- @type ||= GraphQL::Client::QueryResult.wrap(definition_node, name: "#{name}.type")
145
+ @type ||= GraphQL::Client::QueryResult.wrap(definition_node, name: "#{name}.type", types: @document_types)
140
146
  end
141
147
  end
142
148
 
@@ -208,13 +214,24 @@ module GraphQL
208
214
  error.set_backtrace(["#{filename}:#{lineno + validation_line}"] + caller) if filename && lineno
209
215
  raise error
210
216
  end
217
+
218
+ document_types = DocumentTypes.analyze_types(@schema, doc).freeze
219
+ else
220
+ document_types = {}.freeze
211
221
  end
212
222
 
223
+ QueryTypename.insert_typename_fields(doc, types: document_types)
224
+
213
225
  definitions = {}
214
226
  doc.definitions.each do |node|
215
227
  node.name = nil if node.name == "__anonymous__"
216
228
  sliced_document = Language::DefinitionSlice.slice(document_dependencies, node.name)
217
- definition = Definition.for(node: node, document: sliced_document)
229
+ definition = Definition.for(
230
+ schema: @schema,
231
+ node: node,
232
+ document: sliced_document,
233
+ document_types: document_types
234
+ )
218
235
  definitions[node.name] = definition
219
236
  end
220
237
 
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require "graphql"
3
+
4
+ module GraphQL
5
+ class Client
6
+ # Internal: Use schema to detect definition and field types.
7
+ module DocumentTypes
8
+ # Internal: Detect all types used in a given document
9
+ #
10
+ # schema - A GraphQL::Schema
11
+ # document - A GraphQL::Language::Nodes::Document to scan
12
+ #
13
+ # Returns a Hash[Language::Nodes::Node] to GraphQL::Type objects.
14
+ def self.analyze_types(schema, document)
15
+ unless schema.is_a?(GraphQL::Schema)
16
+ raise TypeError, "expected schema to be a GraphQL::Schema, but was #{schema.class}"
17
+ end
18
+
19
+ unless document.is_a?(GraphQL::Language::Nodes::Document)
20
+ raise TypeError, "expected schema to be a GraphQL::Language::Nodes::Document, but was #{document.class}"
21
+ end
22
+
23
+ visitor = GraphQL::Language::Visitor.new(document)
24
+ type_stack = GraphQL::StaticValidation::TypeStack.new(schema, visitor)
25
+
26
+ fields = {}
27
+
28
+ visitor[GraphQL::Language::Nodes::OperationDefinition] << ->(node, _parent) do
29
+ fields[node] = type_stack.object_types.last
30
+ end
31
+ visitor[GraphQL::Language::Nodes::FragmentDefinition] << ->(node, _parent) do
32
+ fields[node] = type_stack.object_types.last
33
+ end
34
+ visitor[GraphQL::Language::Nodes::Field] << ->(node, _parent) do
35
+ fields[node] = type_stack.field_definitions.last.type
36
+ end
37
+ visitor.visit
38
+
39
+ fields
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GraphQL
2
3
  class Client
3
4
  # Public: Abstract base class for all errors raised by GraphQL::Client.
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "graphql/client/hash_with_indifferent_access"
2
3
 
3
4
  module GraphQL
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "action_view"
2
3
 
3
4
  module GraphQL
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "active_support/inflector"
2
3
 
3
4
  module GraphQL
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "json"
2
3
  require "net/http"
3
4
  require "uri"
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "graphql/client/errors"
2
3
 
3
4
  module GraphQL
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "active_support/log_subscriber"
2
3
 
3
4
  module GraphQL
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "active_support/inflector"
2
3
  require "graphql"
3
4
  require "graphql/client/errors"
@@ -16,7 +17,7 @@ module GraphQL
16
17
  # Internal: Get QueryResult class for result of query.
17
18
  #
18
19
  # Returns subclass of QueryResult or nil.
19
- def self.wrap(node, name: nil)
20
+ def self.wrap(node, name: nil, types: {})
20
21
  fields = {}
21
22
 
22
23
  node.selections.each do |selection|
@@ -25,27 +26,29 @@ module GraphQL
25
26
  nil
26
27
  when Language::Nodes::Field
27
28
  field_name = selection.alias || selection.name
28
- field_klass = selection.selections.any? ? wrap(selection, name: "#{name}[:#{field_name}]") : nil
29
+ field_klass = nil
30
+ field_klass = wrap(selection, name: "#{name}[:#{field_name}]", types: types) if selection.selections.any?
29
31
  fields[field_name] ? fields[field_name] |= field_klass : fields[field_name] = field_klass
30
32
  when Language::Nodes::InlineFragment
31
- wrap(selection, name: name).fields.each do |fragment_name, klass|
33
+ wrap(selection, name: name, types: types).fields.each do |fragment_name, klass|
32
34
  fields[fragment_name.to_s] ? fields[fragment_name.to_s] |= klass : fields[fragment_name.to_s] = klass
33
35
  end
34
36
  end
35
37
  end
36
38
 
37
- define(name: name, source_node: node, fields: fields)
39
+ define(name: name, source_node: node, fields: fields, type: types[node] && types[node].unwrap)
38
40
  end
39
41
 
40
42
  # Internal
41
- def self.define(name:, source_node:, fields: {})
43
+ def self.define(name:, source_node:, fields: {}, type: nil)
42
44
  Class.new(self) do
43
45
  @name = name
46
+ @type = type
44
47
  @source_node = source_node
45
48
  @fields = {}
46
49
 
47
- fields.each do |field, type|
48
- @fields[field.to_sym] = type
50
+ fields.each do |field, klass|
51
+ @fields[field.to_sym] = klass
49
52
 
50
53
  send :attr_reader, field
51
54
 
@@ -69,8 +72,8 @@ module GraphQL
69
72
  RUBY
70
73
  end
71
74
 
72
- assigns = fields.map do |field, type|
73
- if type
75
+ assigns = fields.map do |field, klass|
76
+ if klass
74
77
  <<-RUBY
75
78
  @#{field} = self.class.fields[:#{field}].cast(@data["#{field}"], @errors.filter_by_path("#{field}"))
76
79
  RUBY
@@ -81,6 +84,10 @@ module GraphQL
81
84
  end
82
85
  end
83
86
 
87
+ if @type && @type.is_a?(GraphQL::ObjectType)
88
+ assigns.unshift "@__typename = self.class.type.name"
89
+ end
90
+
84
91
  class_eval <<-RUBY, __FILE__, __LINE__
85
92
  def initialize(data, errors = Errors.new)
86
93
  @data = data
@@ -94,6 +101,8 @@ module GraphQL
94
101
  end
95
102
 
96
103
  class << self
104
+ attr_reader :type
105
+
97
106
  attr_reader :source_node
98
107
 
99
108
  attr_reader :fields
@@ -177,6 +186,9 @@ module GraphQL
177
186
  attr_reader :data
178
187
  alias to_h data
179
188
 
189
+ attr_reader :__typename
190
+ alias typename __typename
191
+
180
192
  def inspect
181
193
  ivars = self.class.fields.keys.map do |sym|
182
194
  value = instance_variable_get("@#{sym}")
@@ -186,7 +198,7 @@ module GraphQL
186
198
  "#{sym}=#{value.inspect}"
187
199
  end
188
200
  end
189
- buf = "#<#{self.class.name}"
201
+ buf = "#<#{self.class.name}".dup
190
202
  buf << " " << ivars.join(" ") if ivars.any?
191
203
  buf << ">"
192
204
  buf
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ require "graphql"
3
+ require "graphql/client/document_types"
4
+
5
+ module GraphQL
6
+ class Client
7
+ # Internal: Insert __typename field selections into query.
8
+ module QueryTypename
9
+ # Internal: Insert __typename field selections into query.
10
+ #
11
+ # Skips known types when schema is provided.
12
+ #
13
+ # document - GraphQL::Language::Nodes::Document to modify
14
+ # schema - Optional Map of GraphQL::Language::Nodes::Node to GraphQL::Type
15
+ #
16
+ # Returns nothing.
17
+ def self.insert_typename_fields(document, types: {})
18
+ on_selections = ->(node, _parent) do
19
+ return unless node.selections.any?
20
+
21
+ type = types[node]
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 }
25
+ names = Set.new(names.compact)
26
+
27
+ unless names.include?("__typename")
28
+ node.selections = [GraphQL::Language::Nodes::Field.new(name: "__typename")] + node.selections
29
+ end
30
+ end
31
+ end
32
+
33
+ visitor = GraphQL::Language::Visitor.new(document)
34
+ visitor[GraphQL::Language::Nodes::Field].leave << on_selections
35
+ visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << on_selections
36
+ visitor[GraphQL::Language::Nodes::OperationDefinition].leave << on_selections
37
+ visitor.visit
38
+
39
+ nil
40
+ end
41
+
42
+ def self.node_flatten_selections(selections)
43
+ selections.flat_map do |selection|
44
+ case selection
45
+ when GraphQL::Language::Nodes::Field
46
+ selection
47
+ when GraphQL::Language::Nodes::InlineFragment
48
+ node_flatten_selections(selection.selections)
49
+ else
50
+ []
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "graphql"
2
3
  require "graphql/client"
3
4
  require "rails/railtie"
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "graphql/client/errors"
2
3
 
3
4
  module GraphQL
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "active_support/dependencies"
2
3
  require "active_support/inflector"
3
4
  require "graphql/client/erubis"
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "graphql"
2
3
 
3
4
  module GraphQL
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "rubocop"
2
3
 
3
4
  module RuboCop
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require "active_support/inflector"
2
3
  require "graphql"
3
4
  require "graphql/client/erubis"
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.2.6
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-26 00:00:00.000000000 Z
11
+ date: 2016-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -115,6 +115,7 @@ files:
115
115
  - LICENSE
116
116
  - README.md
117
117
  - lib/graphql/client.rb
118
+ - lib/graphql/client/document_types.rb
118
119
  - lib/graphql/client/error.rb
119
120
  - lib/graphql/client/errors.rb
120
121
  - lib/graphql/client/erubis.rb
@@ -123,6 +124,7 @@ files:
123
124
  - lib/graphql/client/list.rb
124
125
  - lib/graphql/client/log_subscriber.rb
125
126
  - lib/graphql/client/query_result.rb
127
+ - lib/graphql/client/query_typename.rb
126
128
  - lib/graphql/client/railtie.rb
127
129
  - lib/graphql/client/response.rb
128
130
  - lib/graphql/client/view_module.rb
@@ -149,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
151
  version: '0'
150
152
  requirements: []
151
153
  rubyforge_project:
152
- rubygems_version: 2.5.1
154
+ rubygems_version: 2.5.2
153
155
  signing_key:
154
156
  specification_version: 4
155
157
  summary: GraphQL Client