graphql-client 0.9.0 → 0.10.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 +4 -4
- data/README.md +1 -1
- data/lib/graphql/client.rb +20 -10
- data/lib/graphql/client/definition.rb +116 -23
- data/lib/graphql/client/deprecation.rb +2 -2
- data/lib/graphql/client/document_types.rb +3 -0
- data/lib/graphql/client/error.rb +3 -0
- data/lib/graphql/client/response.rb +2 -2
- data/lib/graphql/client/schema.rb +106 -0
- data/lib/graphql/client/schema/base_type.rb +39 -0
- data/lib/graphql/client/schema/enum_type.rb +56 -0
- data/lib/graphql/client/schema/include_directive.rb +44 -0
- data/lib/graphql/client/schema/interface_type.rb +31 -0
- data/lib/graphql/client/schema/list_type.rb +57 -0
- data/lib/graphql/client/schema/non_null_type.rb +52 -0
- data/lib/graphql/client/schema/object_type.rb +162 -0
- data/lib/graphql/client/schema/possible_types.rb +55 -0
- data/lib/graphql/client/schema/scalar_type.rb +47 -0
- data/lib/graphql/client/schema/skip_directive.rb +44 -0
- data/lib/graphql/client/schema/union_type.rb +31 -0
- metadata +14 -3
- data/lib/graphql/client/query_result.rb +0 -402
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54d0c609ffe31a1375380c8647fe4a1502f19a76
|
4
|
+
data.tar.gz: 57dc7311a2da955fa6a7297aa383eacf7473bee6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 645186f0b676ade5ec368fe51c0a0fc97110fb6d100eb08411b8f079b8e793c5837c94bde44d44a4ec119fe40593ddce826c943a3ee660635e4c8899dbcdfdcd
|
7
|
+
data.tar.gz: ad0e699625c3f34dfec487284e94ca6a4e4dc31b2522f4a7909a162f9d7db6d27d42873b6a057e3b95158027a93f9eb3708dd3f12535a2d51f0da3fa1604a31c
|
data/README.md
CHANGED
@@ -94,7 +94,7 @@ end
|
|
94
94
|
|
95
95
|
### Executing queries
|
96
96
|
|
97
|
-
Pass the reference of a parsed query definition to `GraphQL::Client#query`. Data is returned back in a wrapped `GraphQL::
|
97
|
+
Pass the reference of a parsed query definition to `GraphQL::Client#query`. Data is returned back in a wrapped `GraphQL::Client::Schema::ObjectType` struct that provides Ruby-ish accessors.
|
98
98
|
|
99
99
|
``` ruby
|
100
100
|
result = SWAPI::Client.query(Hero::Query)
|
data/lib/graphql/client.rb
CHANGED
@@ -9,9 +9,9 @@ require "graphql/client/error"
|
|
9
9
|
require "graphql/client/errors"
|
10
10
|
require "graphql/client/fragment_definition"
|
11
11
|
require "graphql/client/operation_definition"
|
12
|
-
require "graphql/client/query_result"
|
13
12
|
require "graphql/client/query_typename"
|
14
13
|
require "graphql/client/response"
|
14
|
+
require "graphql/client/schema"
|
15
15
|
require "graphql/language/nodes/deep_freeze_ext"
|
16
16
|
require "json"
|
17
17
|
|
@@ -31,6 +31,8 @@ module GraphQL
|
|
31
31
|
|
32
32
|
attr_reader :schema, :execute
|
33
33
|
|
34
|
+
attr_reader :types
|
35
|
+
|
34
36
|
attr_accessor :document_tracking_enabled
|
35
37
|
|
36
38
|
# Public: Check if collocated caller enforcement is enabled.
|
@@ -89,6 +91,8 @@ module GraphQL
|
|
89
91
|
@document_tracking_enabled = false
|
90
92
|
@allow_dynamic_queries = false
|
91
93
|
@enforce_collocated_callers = enforce_collocated_callers
|
94
|
+
|
95
|
+
@types = Schema.generate(@schema)
|
92
96
|
end
|
93
97
|
|
94
98
|
def parse(str, filename = nil, lineno = nil)
|
@@ -146,6 +150,9 @@ module GraphQL
|
|
146
150
|
|
147
151
|
doc = GraphQL.parse(str)
|
148
152
|
|
153
|
+
document_types = DocumentTypes.analyze_types(self.schema, doc).freeze
|
154
|
+
QueryTypename.insert_typename_fields(doc, types: document_types)
|
155
|
+
|
149
156
|
doc.definitions.each do |node|
|
150
157
|
node.name ||= "__anonymous__"
|
151
158
|
end
|
@@ -168,21 +175,24 @@ module GraphQL
|
|
168
175
|
raise error
|
169
176
|
end
|
170
177
|
|
171
|
-
document_types = DocumentTypes.analyze_types(self.schema, doc).freeze
|
172
|
-
|
173
|
-
QueryTypename.insert_typename_fields(doc, types: document_types)
|
174
|
-
|
175
178
|
definitions = {}
|
176
179
|
doc.definitions.each do |node|
|
180
|
+
irep_node = case node
|
181
|
+
when GraphQL::Language::Nodes::OperationDefinition
|
182
|
+
errors[:irep].operation_definitions[node.name]
|
183
|
+
when GraphQL::Language::Nodes::FragmentDefinition
|
184
|
+
errors[:irep].fragment_definitions[node.name]
|
185
|
+
else
|
186
|
+
raise TypeError, "unexpected #{node.class}"
|
187
|
+
end
|
188
|
+
|
177
189
|
node.name = nil if node.name == "__anonymous__"
|
178
190
|
sliced_document = Language::DefinitionSlice.slice(document_dependencies, node.name)
|
179
191
|
definition = Definition.for(
|
180
|
-
|
181
|
-
|
192
|
+
client: self,
|
193
|
+
irep_node: irep_node,
|
182
194
|
document: sliced_document,
|
183
|
-
|
184
|
-
source_location: source_location,
|
185
|
-
enforce_collocated_callers: enforce_collocated_callers
|
195
|
+
source_location: source_location
|
186
196
|
)
|
187
197
|
definitions[node.name] = definition
|
188
198
|
end
|
@@ -1,4 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "graphql"
|
4
|
+
require "graphql/client/collocated_enforcement"
|
5
|
+
require "graphql/client/schema/object_type"
|
6
|
+
require "graphql/client/schema/possible_types"
|
7
|
+
require "set"
|
8
|
+
|
2
9
|
module GraphQL
|
3
10
|
class Client
|
4
11
|
# Definitions are constructed by Client.parse and wrap a parsed AST of the
|
@@ -7,31 +14,48 @@ module GraphQL
|
|
7
14
|
#
|
8
15
|
# Definitions MUST be assigned to a constant.
|
9
16
|
class Definition < Module
|
10
|
-
def self.for(
|
11
|
-
case
|
17
|
+
def self.for(irep_node:, **kargs)
|
18
|
+
case irep_node.ast_node
|
12
19
|
when Language::Nodes::OperationDefinition
|
13
|
-
OperationDefinition.new(
|
20
|
+
OperationDefinition.new(irep_node: irep_node, **kargs)
|
14
21
|
when Language::Nodes::FragmentDefinition
|
15
|
-
FragmentDefinition.new(
|
22
|
+
FragmentDefinition.new(irep_node: irep_node, **kargs)
|
16
23
|
else
|
17
|
-
raise TypeError, "expected node to be a definition type, but was #{
|
24
|
+
raise TypeError, "expected node to be a definition type, but was #{irep_node.ast_node.class}"
|
18
25
|
end
|
19
26
|
end
|
20
27
|
|
21
|
-
def initialize(
|
22
|
-
@
|
28
|
+
def initialize(client:, document:, irep_node:, source_location:)
|
29
|
+
@client = client
|
23
30
|
@document = document
|
24
|
-
@
|
25
|
-
@document_types = document_types
|
31
|
+
@definition_irep_node = irep_node
|
26
32
|
@source_location = source_location
|
27
|
-
@
|
33
|
+
@schema_class = client.types.define_class(self, definition_irep_node, definition_irep_node.return_type)
|
28
34
|
end
|
29
35
|
|
36
|
+
# Internal: Get associated owner GraphQL::Client instance.
|
37
|
+
attr_reader :client
|
38
|
+
|
39
|
+
# Internal root schema class for defintion. Returns
|
40
|
+
# GraphQL::Client::Schema::ObjectType or
|
41
|
+
# GraphQL::Client::Schema::PossibleTypes.
|
42
|
+
attr_reader :schema_class
|
43
|
+
|
44
|
+
# Deprecated: Use schema_class
|
45
|
+
alias_method :type, :schema_class
|
46
|
+
|
47
|
+
# Internal: Get underlying IRep Node for the definition.
|
48
|
+
#
|
49
|
+
# Returns GraphQL::InternalRepresentation::Node object.
|
50
|
+
attr_reader :definition_irep_node
|
51
|
+
|
30
52
|
# Internal: Get underlying operation or fragment defintion AST node for
|
31
53
|
# definition.
|
32
54
|
#
|
33
55
|
# Returns OperationDefinition or FragmentDefinition object.
|
34
|
-
|
56
|
+
def definition_node
|
57
|
+
definition_irep_node.ast_node
|
58
|
+
end
|
35
59
|
|
36
60
|
# Public: Global name of definition in client document.
|
37
61
|
#
|
@@ -57,27 +81,96 @@ module GraphQL
|
|
57
81
|
# and any FragmentDefinition dependencies.
|
58
82
|
attr_reader :document
|
59
83
|
|
60
|
-
# Internal: Mapping of document nodes to schema types.
|
61
|
-
attr_reader :document_types
|
62
|
-
|
63
|
-
attr_reader :schema
|
64
|
-
|
65
84
|
# Public: Returns the Ruby source filename and line number containing this
|
66
85
|
# definition was not defined in Ruby.
|
67
86
|
#
|
68
87
|
# Returns Array pair of [String, Fixnum].
|
69
88
|
attr_reader :source_location
|
70
89
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
90
|
+
def new(obj, errors = Errors.new)
|
91
|
+
case schema_class
|
92
|
+
when GraphQL::Client::Schema::PossibleTypes
|
93
|
+
case obj
|
94
|
+
when NilClass
|
95
|
+
nil
|
96
|
+
else
|
97
|
+
schema_class.cast(obj.to_h, obj.errors)
|
98
|
+
end
|
99
|
+
when GraphQL::Client::Schema::ObjectType
|
100
|
+
case obj
|
101
|
+
when NilClass, schema_class
|
102
|
+
obj
|
103
|
+
when Hash
|
104
|
+
schema_class.new(obj, errors)
|
105
|
+
else
|
106
|
+
if obj.class.is_a?(GraphQL::Client::Schema::ObjectType)
|
107
|
+
unless obj.class._spreads.include?(definition_node.name)
|
108
|
+
raise TypeError, "#{definition_node.name} is not included in #{obj.class.source_definition.name}"
|
109
|
+
end
|
110
|
+
schema_class.cast(obj.to_h, obj.errors)
|
111
|
+
else
|
112
|
+
raise TypeError, "unexpected #{obj.class}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
else
|
116
|
+
raise TypeError, "unexpected #{schema_class}"
|
117
|
+
end
|
75
118
|
end
|
76
119
|
|
77
|
-
|
78
|
-
|
79
|
-
@
|
120
|
+
# Internal: Nodes AST indexes.
|
121
|
+
def indexes
|
122
|
+
@indexes ||= begin
|
123
|
+
visitor = GraphQL::Language::Visitor.new(document)
|
124
|
+
definitions = index_node_definitions(visitor)
|
125
|
+
spreads = index_spreads(visitor)
|
126
|
+
visitor.visit
|
127
|
+
{ definitions: definitions, spreads: spreads }
|
128
|
+
end
|
80
129
|
end
|
130
|
+
|
131
|
+
private
|
132
|
+
def index_spreads(visitor)
|
133
|
+
spreads = {}
|
134
|
+
on_node = ->(node, _parent) { spreads[node] = Set.new(flatten_spreads(node).map(&:name)) }
|
135
|
+
|
136
|
+
visitor[GraphQL::Language::Nodes::Field] << on_node
|
137
|
+
visitor[GraphQL::Language::Nodes::FragmentDefinition] << on_node
|
138
|
+
visitor[GraphQL::Language::Nodes::OperationDefinition] << on_node
|
139
|
+
|
140
|
+
spreads
|
141
|
+
end
|
142
|
+
|
143
|
+
def flatten_spreads(node)
|
144
|
+
node.selections.flat_map do |selection|
|
145
|
+
case selection
|
146
|
+
when Language::Nodes::FragmentSpread
|
147
|
+
selection
|
148
|
+
when Language::Nodes::InlineFragment
|
149
|
+
flatten_spreads(selection)
|
150
|
+
else
|
151
|
+
[]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def index_node_definitions(visitor)
|
157
|
+
current_definition = nil
|
158
|
+
enter_definition = ->(node, _parent) { current_definition = node }
|
159
|
+
leave_definition = ->(node, _parent) { current_definition = nil }
|
160
|
+
|
161
|
+
visitor[GraphQL::Language::Nodes::FragmentDefinition].enter << enter_definition
|
162
|
+
visitor[GraphQL::Language::Nodes::FragmentDefinition].leave << leave_definition
|
163
|
+
visitor[GraphQL::Language::Nodes::OperationDefinition].enter << enter_definition
|
164
|
+
visitor[GraphQL::Language::Nodes::OperationDefinition].leave << leave_definition
|
165
|
+
|
166
|
+
definitions = {}
|
167
|
+
on_node = ->(node, _parent) { definitions[node] = current_definition }
|
168
|
+
visitor[GraphQL::Language::Nodes::Field] << on_node
|
169
|
+
visitor[GraphQL::Language::Nodes::FragmentDefinition] << on_node
|
170
|
+
visitor[GraphQL::Language::Nodes::InlineFragment] << on_node
|
171
|
+
visitor[GraphQL::Language::Nodes::OperationDefinition] << on_node
|
172
|
+
definitions
|
173
|
+
end
|
81
174
|
end
|
82
175
|
end
|
83
176
|
end
|
@@ -4,7 +4,7 @@ require "active_support/deprecation"
|
|
4
4
|
module GraphQL
|
5
5
|
class Client
|
6
6
|
if ActiveSupport::Deprecation.is_a?(Class)
|
7
|
-
Deprecation = ActiveSupport::Deprecation.new("0
|
7
|
+
Deprecation = ActiveSupport::Deprecation.new("11.0", "graphql-client")
|
8
8
|
else
|
9
9
|
module Deprecation
|
10
10
|
extend self
|
@@ -22,7 +22,7 @@ module GraphQL
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
|
25
|
-
warn "#{deprecated_method_name} is deprecated and will be removed from graphql-client 0.
|
25
|
+
warn "#{deprecated_method_name} is deprecated and will be removed from graphql-client 0.11 (#{message})"
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/graphql/client/error.rb
CHANGED
@@ -7,11 +7,11 @@ module GraphQL
|
|
7
7
|
#
|
8
8
|
# https://facebook.github.io/graphql/#sec-Response-Format
|
9
9
|
class Response
|
10
|
-
# Public: Wrapped
|
10
|
+
# Public: Wrapped ObjectType of data returned from the server.
|
11
11
|
#
|
12
12
|
# https://facebook.github.io/graphql/#sec-Data
|
13
13
|
#
|
14
|
-
# Returns instance of
|
14
|
+
# Returns instance of ObjectType subclass.
|
15
15
|
attr_reader :data
|
16
16
|
|
17
17
|
# Public: Get partial failures from response.
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "graphql"
|
4
|
+
require "graphql/client/schema/enum_type"
|
5
|
+
require "graphql/client/schema/include_directive"
|
6
|
+
require "graphql/client/schema/interface_type"
|
7
|
+
require "graphql/client/schema/list_type"
|
8
|
+
require "graphql/client/schema/non_null_type"
|
9
|
+
require "graphql/client/schema/object_type"
|
10
|
+
require "graphql/client/schema/scalar_type"
|
11
|
+
require "graphql/client/schema/skip_directive"
|
12
|
+
require "graphql/client/schema/union_type"
|
13
|
+
|
14
|
+
module GraphQL
|
15
|
+
class Client
|
16
|
+
module Schema
|
17
|
+
module ClassMethods
|
18
|
+
def define_class(definition, irep_node, type)
|
19
|
+
type_klass = case type
|
20
|
+
when GraphQL::NonNullType
|
21
|
+
define_class(definition, irep_node, type.of_type).to_non_null_type
|
22
|
+
when GraphQL::ListType
|
23
|
+
define_class(definition, irep_node, type.of_type).to_list_type
|
24
|
+
else
|
25
|
+
const_get(type.name).define_class(definition, irep_node)
|
26
|
+
end
|
27
|
+
|
28
|
+
irep_node.ast_node.directives.inject(type_klass) do |klass, directive|
|
29
|
+
if directive = self.directives[directive.name.to_sym]
|
30
|
+
directive.new(klass)
|
31
|
+
else
|
32
|
+
klass
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.generate(schema)
|
39
|
+
mod = Module.new
|
40
|
+
mod.extend ClassMethods
|
41
|
+
|
42
|
+
mod.define_singleton_method :schema do
|
43
|
+
schema
|
44
|
+
end
|
45
|
+
|
46
|
+
cache = {}
|
47
|
+
schema.types.each do |name, type|
|
48
|
+
next if name.start_with?("__")
|
49
|
+
if klass = class_for(schema, type, cache)
|
50
|
+
klass.schema_module = mod
|
51
|
+
mod.const_set(name, klass)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
directives = {}
|
56
|
+
mod.define_singleton_method(:directives) { directives }
|
57
|
+
directives[:include] = IncludeDirective
|
58
|
+
directives[:skip] = SkipDirective
|
59
|
+
|
60
|
+
mod
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.class_for(schema, type, cache)
|
64
|
+
return cache[type] if cache[type]
|
65
|
+
|
66
|
+
case type
|
67
|
+
when GraphQL::InputObjectType
|
68
|
+
nil
|
69
|
+
when GraphQL::ScalarType
|
70
|
+
cache[type] = ScalarType.new(type)
|
71
|
+
when GraphQL::EnumType
|
72
|
+
cache[type] = EnumType.new(type)
|
73
|
+
when GraphQL::ListType
|
74
|
+
cache[type] = class_for(schema, type.of_type, cache).to_list_type
|
75
|
+
when GraphQL::NonNullType
|
76
|
+
cache[type] = class_for(schema, type.of_type, cache).to_non_null_type
|
77
|
+
when GraphQL::UnionType
|
78
|
+
klass = cache[type] = UnionType.new(type)
|
79
|
+
|
80
|
+
type.possible_types.each do |possible_type|
|
81
|
+
possible_klass = class_for(schema, possible_type, cache)
|
82
|
+
possible_klass.send :include, klass
|
83
|
+
end
|
84
|
+
|
85
|
+
klass
|
86
|
+
when GraphQL::InterfaceType
|
87
|
+
cache[type] = InterfaceType.new(type)
|
88
|
+
when GraphQL::ObjectType
|
89
|
+
klass = cache[type] = ObjectType.new(type)
|
90
|
+
|
91
|
+
type.interfaces.each do |interface|
|
92
|
+
klass.send :include, class_for(schema, interface, cache)
|
93
|
+
end
|
94
|
+
|
95
|
+
type.all_fields.each do |field|
|
96
|
+
klass.fields[field.name.to_sym] = class_for(schema, field.type, cache)
|
97
|
+
end
|
98
|
+
|
99
|
+
klass
|
100
|
+
else
|
101
|
+
raise TypeError, "unexpected #{type.class}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Client
|
5
|
+
module Schema
|
6
|
+
module BaseType
|
7
|
+
# Public: Get associated GraphQL::BaseType with for this class.
|
8
|
+
attr_reader :type
|
9
|
+
|
10
|
+
# Internal: Get owner schema Module container.
|
11
|
+
attr_accessor :schema_module
|
12
|
+
|
13
|
+
# Internal: Cast JSON value to wrapped value.
|
14
|
+
#
|
15
|
+
# value - JSON value
|
16
|
+
# errors - Errors instance
|
17
|
+
#
|
18
|
+
# Returns BaseType instance.
|
19
|
+
def cast(value, errors)
|
20
|
+
raise NotImplementedError, "subclasses must implement #cast(value, errors)"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Internal: Get non-nullable wrapper of this type class.
|
24
|
+
#
|
25
|
+
# Returns NonNullType instance.
|
26
|
+
def to_non_null_type
|
27
|
+
@null_type ||= NonNullType.new(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Internal: Get list wrapper of this type class.
|
31
|
+
#
|
32
|
+
# Returns ListType instance.
|
33
|
+
def to_list_type
|
34
|
+
@list_type ||= ListType.new(self)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|