graphql-client 0.16.0 → 0.18.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/collocated_enforcement.rb +21 -14
- data/lib/graphql/client/definition.rb +18 -3
- data/lib/graphql/client/http.rb +1 -1
- data/lib/graphql/client/schema/interface_type.rb +1 -1
- data/lib/graphql/client/schema/object_type.rb +145 -86
- data/lib/graphql/client/schema/union_type.rb +1 -1
- data/lib/graphql/client/view_module.rb +1 -1
- data/lib/graphql/client.rb +70 -61
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82b89599e02036868b2249fbd3ac2aeb89b6043b19040bdaeaf66f31b5f1b28e
|
4
|
+
data.tar.gz: 2e168f61452bd5041aa01dce3a09c99959b629afeb2880ca629d1034f22fb0d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f14f21ff57e8dc29dbef6e5fb2ee6d2fb552075986ef57c8f89a3d7ce5b2faa8e2965b20a5b8f7735394b3ee13cdc8e55923bda0dd02fa8625f7e2b824dab7b
|
7
|
+
data.tar.gz: ee04576697c258bdc3959366d0cccef720e24eb2358ecbb28c020d912c5b241ac7ba195a8240656c6f825109c23a321ef2449892b4d893e29802c03b6580f557
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# graphql-client [](https://badge.fury.io/rb/graphql-client) [](https://badge.fury.io/rb/graphql-client) [](https://github.com/github/graphql-client/actions?query=workflow)
|
2
2
|
|
3
3
|
GraphQL Client is a Ruby library for declaring, composing and executing GraphQL queries.
|
4
4
|
|
@@ -12,6 +12,8 @@ module GraphQL
|
|
12
12
|
|
13
13
|
# Enforcements collocated object access best practices.
|
14
14
|
module CollocatedEnforcement
|
15
|
+
extend self
|
16
|
+
|
15
17
|
# Public: Ignore collocated caller enforcement for the scope of the block.
|
16
18
|
def allow_noncollocated_callers
|
17
19
|
Thread.current[:query_result_caller_location_ignore] = true
|
@@ -20,6 +22,23 @@ module GraphQL
|
|
20
22
|
Thread.current[:query_result_caller_location_ignore] = nil
|
21
23
|
end
|
22
24
|
|
25
|
+
def verify_collocated_path(location, path, method = "method")
|
26
|
+
return yield if Thread.current[:query_result_caller_location_ignore]
|
27
|
+
|
28
|
+
if (location.path != path) && !(WHITELISTED_GEM_NAMES.any? { |g| location.path.include?("gems/#{g}") })
|
29
|
+
error = NonCollocatedCallerError.new("#{method} was called outside of '#{path}' https://git.io/v1syX")
|
30
|
+
error.set_backtrace(caller(2))
|
31
|
+
raise error
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
Thread.current[:query_result_caller_location_ignore] = true
|
36
|
+
yield
|
37
|
+
ensure
|
38
|
+
Thread.current[:query_result_caller_location_ignore] = nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
23
42
|
# Internal: Decorate method with collocated caller enforcement.
|
24
43
|
#
|
25
44
|
# mod - Target Module/Class
|
@@ -31,21 +50,9 @@ module GraphQL
|
|
31
50
|
mod.prepend(Module.new do
|
32
51
|
methods.each do |method|
|
33
52
|
define_method(method) do |*args, &block|
|
34
|
-
|
35
|
-
|
36
|
-
locations = caller_locations(1, 1)
|
37
|
-
|
38
|
-
if (locations.first.path != path) && !(caller_locations.any? { |cl| WHITELISTED_GEM_NAMES.any? { |g| cl.path.include?("gems/#{g}") } })
|
39
|
-
error = NonCollocatedCallerError.new("#{method} was called outside of '#{path}' https://git.io/v1syX")
|
40
|
-
error.set_backtrace(caller(1))
|
41
|
-
raise error
|
42
|
-
end
|
43
|
-
|
44
|
-
begin
|
45
|
-
Thread.current[:query_result_caller_location_ignore] = true
|
53
|
+
location = caller_locations(1, 1)[0]
|
54
|
+
CollocatedEnforcement.verify_collocated_path(location, path, method) do
|
46
55
|
super(*args, &block)
|
47
|
-
ensure
|
48
|
-
Thread.current[:query_result_caller_location_ignore] = nil
|
49
56
|
end
|
50
57
|
end
|
51
58
|
end
|
@@ -115,9 +115,24 @@ module GraphQL
|
|
115
115
|
else
|
116
116
|
cast_object(obj)
|
117
117
|
end
|
118
|
+
when GraphQL::Client::Schema::ObjectType::WithDefinition
|
119
|
+
case obj
|
120
|
+
when schema_class.klass
|
121
|
+
if obj._definer == schema_class
|
122
|
+
obj
|
123
|
+
else
|
124
|
+
cast_object(obj)
|
125
|
+
end
|
126
|
+
when nil
|
127
|
+
nil
|
128
|
+
when Hash
|
129
|
+
schema_class.new(obj, errors)
|
130
|
+
else
|
131
|
+
cast_object(obj)
|
132
|
+
end
|
118
133
|
when GraphQL::Client::Schema::ObjectType
|
119
134
|
case obj
|
120
|
-
when
|
135
|
+
when nil, schema_class
|
121
136
|
obj
|
122
137
|
when Hash
|
123
138
|
schema_class.new(obj, errors)
|
@@ -144,8 +159,8 @@ module GraphQL
|
|
144
159
|
|
145
160
|
def cast_object(obj)
|
146
161
|
if obj.class.is_a?(GraphQL::Client::Schema::ObjectType)
|
147
|
-
unless obj.
|
148
|
-
raise TypeError, "#{definition_node.name} is not included in #{obj.
|
162
|
+
unless obj._spreads.include?(definition_node.name)
|
163
|
+
raise TypeError, "#{definition_node.name} is not included in #{obj.source_definition.name}"
|
149
164
|
end
|
150
165
|
schema_class.cast(obj.to_h, obj.errors)
|
151
166
|
else
|
data/lib/graphql/client/http.rb
CHANGED
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def define_class(definition, ast_nodes)
|
24
|
-
possible_type_names = definition.client.
|
24
|
+
possible_type_names = definition.client.possible_types(type).map(&:graphql_name)
|
25
25
|
possible_types = possible_type_names.map { |concrete_type_name|
|
26
26
|
schema_module.get_class(concrete_type_name).define_class(definition, ast_nodes)
|
27
27
|
}
|
@@ -16,6 +16,53 @@ module GraphQL
|
|
16
16
|
|
17
17
|
define_singleton_method(:type) { type }
|
18
18
|
define_singleton_method(:fields) { fields }
|
19
|
+
|
20
|
+
const_set(:READERS, {})
|
21
|
+
const_set(:PREDICATES, {})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class WithDefinition
|
26
|
+
include BaseType
|
27
|
+
include ObjectType
|
28
|
+
|
29
|
+
EMPTY_SET = Set.new.freeze
|
30
|
+
|
31
|
+
attr_reader :klass, :defined_fields, :definition
|
32
|
+
|
33
|
+
def type
|
34
|
+
@klass.type
|
35
|
+
end
|
36
|
+
|
37
|
+
def fields
|
38
|
+
@klass.fields
|
39
|
+
end
|
40
|
+
|
41
|
+
def spreads
|
42
|
+
if defined?(@spreads)
|
43
|
+
@spreads
|
44
|
+
else
|
45
|
+
EMPTY_SET
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(klass, defined_fields, definition, spreads)
|
50
|
+
@klass = klass
|
51
|
+
@defined_fields = defined_fields.map do |k, v|
|
52
|
+
[-k.to_s, v]
|
53
|
+
end.to_h
|
54
|
+
@definition = definition
|
55
|
+
@spreads = spreads unless spreads.empty?
|
56
|
+
|
57
|
+
@defined_fields.keys.each do |attr|
|
58
|
+
name = ActiveSupport::Inflector.underscore(attr)
|
59
|
+
@klass::READERS[:"#{name}"] ||= attr
|
60
|
+
@klass::PREDICATES[:"#{name}?"] ||= attr
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def new(data = {}, errors = Errors.new)
|
65
|
+
@klass.new(data, errors, self)
|
19
66
|
end
|
20
67
|
end
|
21
68
|
|
@@ -46,56 +93,9 @@ module GraphQL
|
|
46
93
|
field_classes[result_name.to_sym] = schema_module.define_class(definition, field_ast_nodes, field_return_type)
|
47
94
|
end
|
48
95
|
|
49
|
-
|
50
|
-
klass.define_fields(field_classes)
|
51
|
-
klass.instance_variable_set(:@source_definition, definition)
|
52
|
-
klass.instance_variable_set(:@_spreads, definition.indexes[:spreads][ast_nodes.first])
|
53
|
-
|
54
|
-
if definition.client.enforce_collocated_callers
|
55
|
-
keys = field_classes.keys.map { |key| ActiveSupport::Inflector.underscore(key) }
|
56
|
-
Client.enforce_collocated_callers(klass, keys, definition.source_location[0])
|
57
|
-
end
|
58
|
-
|
59
|
-
klass
|
60
|
-
end
|
61
|
-
|
62
|
-
PREDICATE_CACHE = Hash.new { |h, name|
|
63
|
-
h[name] = -> { @data[name] ? true : false }
|
64
|
-
}
|
96
|
+
spreads = definition.indexes[:spreads][ast_nodes.first]
|
65
97
|
|
66
|
-
|
67
|
-
h[key] = -> {
|
68
|
-
name = key.to_s
|
69
|
-
type = self.class::FIELDS[key]
|
70
|
-
@casted_data.fetch(name) do
|
71
|
-
@casted_data[name] = type.cast(@data[name], @errors.filter_by_path(name))
|
72
|
-
end
|
73
|
-
}
|
74
|
-
}
|
75
|
-
|
76
|
-
MODULE_CACHE = Hash.new do |h, fields|
|
77
|
-
h[fields] = Module.new do
|
78
|
-
fields.each do |name|
|
79
|
-
GraphQL::Client::Schema::ObjectType.define_cached_field(name, self)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
FIELDS_CACHE = Hash.new { |h, k| h[k] = k }
|
85
|
-
|
86
|
-
def define_fields(fields)
|
87
|
-
const_set :FIELDS, FIELDS_CACHE[fields]
|
88
|
-
mod = MODULE_CACHE[fields.keys.sort]
|
89
|
-
include mod
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.define_cached_field(name, ctx)
|
93
|
-
key = name
|
94
|
-
name = -name.to_s
|
95
|
-
method_name = ActiveSupport::Inflector.underscore(name)
|
96
|
-
|
97
|
-
ctx.send(:define_method, method_name, &METHOD_CACHE[key])
|
98
|
-
ctx.send(:define_method, "#{method_name}?", &PREDICATE_CACHE[name])
|
98
|
+
WithDefinition.new(self, field_classes, definition, spreads)
|
99
99
|
end
|
100
100
|
|
101
101
|
def define_field(name, type)
|
@@ -134,9 +134,8 @@ module GraphQL
|
|
134
134
|
continue_selection = if selected_ast_node.type.nil?
|
135
135
|
true
|
136
136
|
else
|
137
|
-
schema = definition.client.schema
|
138
137
|
type_condition = definition.client.get_type(selected_ast_node.type.name)
|
139
|
-
applicable_types =
|
138
|
+
applicable_types = definition.client.possible_types(type_condition)
|
140
139
|
# continue if this object type is one of the types matching the fragment condition
|
141
140
|
applicable_types.include?(type)
|
142
141
|
end
|
@@ -150,10 +149,8 @@ module GraphQL
|
|
150
149
|
fragment_definition = definition.document.definitions.find do |defn|
|
151
150
|
defn.is_a?(GraphQL::Language::Nodes::FragmentDefinition) && defn.name == selected_ast_node.name
|
152
151
|
end
|
153
|
-
|
154
|
-
schema = definition.client.schema
|
155
152
|
type_condition = definition.client.get_type(fragment_definition.type.name)
|
156
|
-
applicable_types =
|
153
|
+
applicable_types = definition.client.possible_types(type_condition)
|
157
154
|
# continue if this object type is one of the types matching the fragment condition
|
158
155
|
continue_selection = applicable_types.include?(type)
|
159
156
|
|
@@ -177,17 +174,16 @@ module GraphQL
|
|
177
174
|
end
|
178
175
|
|
179
176
|
class ObjectClass
|
180
|
-
|
181
|
-
attr_reader :source_definition
|
182
|
-
attr_reader :_spreads
|
183
|
-
end
|
184
|
-
|
185
|
-
extend ClassMethods
|
186
|
-
|
187
|
-
def initialize(data = {}, errors = Errors.new)
|
177
|
+
def initialize(data = {}, errors = Errors.new, definer = nil)
|
188
178
|
@data = data
|
189
179
|
@casted_data = {}
|
190
180
|
@errors = errors
|
181
|
+
|
182
|
+
# If we are not provided a definition, we can use this empty default
|
183
|
+
definer ||= ObjectType::WithDefinition.new(self.class, {}, nil, [])
|
184
|
+
|
185
|
+
@definer = definer
|
186
|
+
@enforce_collocated_callers = source_definition && source_definition.client.enforce_collocated_callers
|
191
187
|
end
|
192
188
|
|
193
189
|
# Public: Returns the raw response data
|
@@ -197,42 +193,85 @@ module GraphQL
|
|
197
193
|
@data
|
198
194
|
end
|
199
195
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
attr_reader :errors
|
196
|
+
def _definer
|
197
|
+
@definer
|
198
|
+
end
|
204
199
|
|
205
|
-
def
|
206
|
-
|
207
|
-
|
208
|
-
type = self.class.type
|
200
|
+
def _spreads
|
201
|
+
@definer.spreads
|
202
|
+
end
|
209
203
|
|
210
|
-
|
211
|
-
|
212
|
-
|
204
|
+
def source_definition
|
205
|
+
@definer.definition
|
206
|
+
end
|
213
207
|
|
214
|
-
|
215
|
-
|
216
|
-
|
208
|
+
def respond_to_missing?(name, priv)
|
209
|
+
if (attr = self.class::READERS[name]) || (attr = self.class::PREDICATES[name])
|
210
|
+
@definer.defined_fields.key?(attr) || super
|
211
|
+
else
|
212
|
+
super
|
217
213
|
end
|
214
|
+
end
|
218
215
|
|
219
|
-
|
220
|
-
|
216
|
+
# Public: Return errors associated with data.
|
217
|
+
#
|
218
|
+
# It's possible to define "errors" as a field. Ideally this shouldn't
|
219
|
+
# happen, but if it does we should prefer the field rather than the
|
220
|
+
# builtin error type.
|
221
|
+
#
|
222
|
+
# Returns Errors collection.
|
223
|
+
def errors
|
224
|
+
if type = @definer.defined_fields["errors"]
|
225
|
+
read_attribute("errors", type)
|
226
|
+
else
|
227
|
+
@errors
|
221
228
|
end
|
229
|
+
end
|
222
230
|
|
223
|
-
|
224
|
-
|
225
|
-
|
231
|
+
def method_missing(name, *args)
|
232
|
+
if (attr = self.class::READERS[name]) && (type = @definer.defined_fields[attr])
|
233
|
+
if @enforce_collocated_callers
|
234
|
+
verify_collocated_path do
|
235
|
+
read_attribute(attr, type)
|
236
|
+
end
|
237
|
+
else
|
238
|
+
read_attribute(attr, type)
|
239
|
+
end
|
240
|
+
elsif (attr = self.class::PREDICATES[name]) && @definer.defined_fields[attr]
|
241
|
+
has_attribute?(attr)
|
226
242
|
else
|
227
|
-
|
228
|
-
|
229
|
-
|
243
|
+
begin
|
244
|
+
super
|
245
|
+
rescue NoMethodError => e
|
246
|
+
type = self.class.type
|
230
247
|
|
231
|
-
|
248
|
+
if ActiveSupport::Inflector.underscore(e.name.to_s) != e.name.to_s
|
249
|
+
raise e
|
250
|
+
end
|
251
|
+
|
252
|
+
all_fields = type.respond_to?(:all_fields) ? type.all_fields : type.fields.values
|
253
|
+
field = all_fields.find do |f|
|
254
|
+
f.name == e.name.to_s || ActiveSupport::Inflector.underscore(f.name) == e.name.to_s
|
255
|
+
end
|
256
|
+
|
257
|
+
unless field
|
258
|
+
raise UnimplementedFieldError, "undefined field `#{e.name}' on #{type.graphql_name} type. https://git.io/v1y3m"
|
259
|
+
end
|
260
|
+
|
261
|
+
if @data.key?(field.name)
|
262
|
+
raise ImplicitlyFetchedFieldError, "implicitly fetched field `#{field.name}' on #{type} type. https://git.io/v1yGL"
|
263
|
+
else
|
264
|
+
raise UnfetchedFieldError, "unfetched field `#{field.name}' on #{type} type. https://git.io/v1y3U"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
232
268
|
end
|
233
269
|
|
234
270
|
def inspect
|
235
|
-
parent = self.class
|
271
|
+
parent = self.class
|
272
|
+
until parent.superclass == ObjectClass
|
273
|
+
parent = parent.superclass
|
274
|
+
end
|
236
275
|
|
237
276
|
ivars = @data.map { |key, value|
|
238
277
|
if value.is_a?(Hash) || value.is_a?(Array)
|
@@ -247,6 +286,26 @@ module GraphQL
|
|
247
286
|
buf << ">"
|
248
287
|
buf
|
249
288
|
end
|
289
|
+
|
290
|
+
private
|
291
|
+
|
292
|
+
def verify_collocated_path
|
293
|
+
location = caller_locations(2, 1)[0]
|
294
|
+
|
295
|
+
CollocatedEnforcement.verify_collocated_path(location, source_definition.source_location[0]) do
|
296
|
+
yield
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def read_attribute(attr, type)
|
301
|
+
@casted_data.fetch(attr) do
|
302
|
+
@casted_data[attr] = type.cast(@data[attr], @errors.filter_by_path(attr))
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def has_attribute?(attr)
|
307
|
+
!!@data[attr]
|
308
|
+
end
|
250
309
|
end
|
251
310
|
end
|
252
311
|
end
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def define_class(definition, ast_nodes)
|
24
|
-
possible_type_names = definition.client.
|
24
|
+
possible_type_names = definition.client.possible_types(type).map(&:graphql_name)
|
25
25
|
possible_types = possible_type_names.map { |concrete_type_name|
|
26
26
|
schema_module.get_class(concrete_type_name).define_class(definition, ast_nodes)
|
27
27
|
}
|
data/lib/graphql/client.rb
CHANGED
@@ -49,12 +49,12 @@ module GraphQL
|
|
49
49
|
when GraphQL::Schema, Class
|
50
50
|
schema
|
51
51
|
when Hash
|
52
|
-
GraphQL::Schema
|
52
|
+
GraphQL::Schema.from_introspection(schema)
|
53
53
|
when String
|
54
54
|
if schema.end_with?(".json") && File.exist?(schema)
|
55
55
|
load_schema(File.read(schema))
|
56
56
|
elsif schema =~ /\A\s*{/
|
57
|
-
load_schema(JSON.parse(schema))
|
57
|
+
load_schema(JSON.parse(schema, freeze: true))
|
58
58
|
end
|
59
59
|
else
|
60
60
|
if schema.respond_to?(:execute)
|
@@ -97,10 +97,31 @@ module GraphQL
|
|
97
97
|
@document_tracking_enabled = false
|
98
98
|
@allow_dynamic_queries = false
|
99
99
|
@enforce_collocated_callers = enforce_collocated_callers
|
100
|
-
|
100
|
+
if schema.is_a?(Class)
|
101
|
+
@possible_types = schema.possible_types
|
102
|
+
end
|
101
103
|
@types = Schema.generate(@schema)
|
102
104
|
end
|
103
105
|
|
106
|
+
# A cache of the schema's merged possible types
|
107
|
+
# @param type_condition [Class, String] a type definition or type name
|
108
|
+
def possible_types(type_condition = nil)
|
109
|
+
if type_condition
|
110
|
+
if defined?(@possible_types)
|
111
|
+
if type_condition.respond_to?(:graphql_name)
|
112
|
+
type_condition = type_condition.graphql_name
|
113
|
+
end
|
114
|
+
@possible_types[type_condition]
|
115
|
+
else
|
116
|
+
@schema.possible_types(type_condition)
|
117
|
+
end
|
118
|
+
elsif defined?(@possible_types)
|
119
|
+
@possible_types
|
120
|
+
else
|
121
|
+
@schema.possible_types(type_condition)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
104
125
|
def parse(str, filename = nil, lineno = nil)
|
105
126
|
if filename.nil? && lineno.nil?
|
106
127
|
location = caller_locations(1, 1).first
|
@@ -135,11 +156,7 @@ module GraphQL
|
|
135
156
|
# which corresponds to the spread.
|
136
157
|
# We depend on ActiveSupport to either find the already-loaded
|
137
158
|
# constant, or to load the constant by name
|
138
|
-
|
139
|
-
fragment = ActiveSupport::Inflector.constantize(const_name)
|
140
|
-
rescue NameError
|
141
|
-
fragment = nil
|
142
|
-
end
|
159
|
+
fragment = ActiveSupport::Inflector.safe_constantize(const_name)
|
143
160
|
|
144
161
|
case fragment
|
145
162
|
when FragmentDefinition
|
@@ -173,12 +190,8 @@ module GraphQL
|
|
173
190
|
|
174
191
|
doc.definitions.each do |node|
|
175
192
|
if node.name.nil?
|
176
|
-
|
177
|
-
|
178
|
-
doc = doc.replace_child(node, node_with_name)
|
179
|
-
else
|
180
|
-
node.name = "__anonymous__"
|
181
|
-
end
|
193
|
+
node_with_name = node.merge(name: "__anonymous__")
|
194
|
+
doc = doc.replace_child(node, node_with_name)
|
182
195
|
end
|
183
196
|
end
|
184
197
|
|
@@ -200,37 +213,13 @@ module GraphQL
|
|
200
213
|
raise error
|
201
214
|
end
|
202
215
|
|
203
|
-
definitions =
|
204
|
-
doc.definitions.each do |node|
|
205
|
-
sliced_document = Language::DefinitionSlice.slice(document_dependencies, node.name)
|
206
|
-
definition = Definition.for(
|
207
|
-
client: self,
|
208
|
-
ast_node: node,
|
209
|
-
document: sliced_document,
|
210
|
-
source_document: doc,
|
211
|
-
source_location: source_location
|
212
|
-
)
|
213
|
-
definitions[node.name] = definition
|
214
|
-
end
|
216
|
+
definitions = sliced_definitions(document_dependencies, doc, source_location: source_location)
|
215
217
|
|
216
|
-
|
217
|
-
|
218
|
-
visitor.visit
|
219
|
-
else
|
220
|
-
name_hook = RenameNodeHook.new(definitions)
|
221
|
-
visitor = Language::Visitor.new(document_dependencies)
|
222
|
-
visitor[Language::Nodes::FragmentDefinition].leave << name_hook.method(:rename_node)
|
223
|
-
visitor[Language::Nodes::OperationDefinition].leave << name_hook.method(:rename_node)
|
224
|
-
visitor[Language::Nodes::FragmentSpread].leave << name_hook.method(:rename_node)
|
225
|
-
visitor.visit
|
226
|
-
end
|
218
|
+
visitor = RenameNodeVisitor.new(document_dependencies, definitions: definitions)
|
219
|
+
visitor.visit
|
227
220
|
|
228
221
|
if document_tracking_enabled
|
229
|
-
|
230
|
-
@document = @document.merge(definitions: @document.definitions + doc.definitions)
|
231
|
-
else
|
232
|
-
@document.definitions.concat(doc.definitions)
|
233
|
-
end
|
222
|
+
@document = @document.merge(definitions: @document.definitions + doc.definitions)
|
234
223
|
end
|
235
224
|
|
236
225
|
if definitions["__anonymous__"]
|
@@ -276,27 +265,9 @@ module GraphQL
|
|
276
265
|
end
|
277
266
|
end
|
278
267
|
|
279
|
-
class RenameNodeHook
|
280
|
-
def initialize(definitions)
|
281
|
-
@definitions = definitions
|
282
|
-
end
|
283
|
-
|
284
|
-
def rename_node(node, _parent)
|
285
|
-
definition = @definitions[node.name]
|
286
|
-
if definition
|
287
|
-
node.extend(LazyName)
|
288
|
-
node._definition = definition
|
289
|
-
end
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
268
|
# Public: A wrapper to use the more-efficient `.get_type` when it's available from GraphQL-Ruby (1.10+)
|
294
269
|
def get_type(type_name)
|
295
|
-
|
296
|
-
@schema.get_type(type_name)
|
297
|
-
else
|
298
|
-
@schema.types[type_name]
|
299
|
-
end
|
270
|
+
@schema.get_type(type_name)
|
300
271
|
end
|
301
272
|
|
302
273
|
# Public: Create operation definition from a fragment definition.
|
@@ -424,6 +395,44 @@ module GraphQL
|
|
424
395
|
|
425
396
|
private
|
426
397
|
|
398
|
+
def sliced_definitions(document_dependencies, doc, source_location:)
|
399
|
+
dependencies = document_dependencies.definitions.map do |node|
|
400
|
+
[node.name, find_definition_dependencies(node)]
|
401
|
+
end.to_h
|
402
|
+
|
403
|
+
doc.definitions.map do |node|
|
404
|
+
deps = Set.new
|
405
|
+
definitions = document_dependencies.definitions.map { |x| [x.name, x] }.to_h
|
406
|
+
|
407
|
+
queue = [node.name]
|
408
|
+
while name = queue.shift
|
409
|
+
next if deps.include?(name)
|
410
|
+
deps.add(name)
|
411
|
+
queue.concat dependencies[name]
|
412
|
+
end
|
413
|
+
|
414
|
+
definitions = document_dependencies.definitions.select { |x| deps.include?(x.name) }
|
415
|
+
sliced_document = Language::Nodes::Document.new(definitions: definitions)
|
416
|
+
definition = Definition.for(
|
417
|
+
client: self,
|
418
|
+
ast_node: node,
|
419
|
+
document: sliced_document,
|
420
|
+
source_document: doc,
|
421
|
+
source_location: source_location
|
422
|
+
)
|
423
|
+
|
424
|
+
[node.name, definition]
|
425
|
+
end.to_h
|
426
|
+
end
|
427
|
+
|
428
|
+
def find_definition_dependencies(node)
|
429
|
+
names = []
|
430
|
+
visitor = Language::Visitor.new(node)
|
431
|
+
visitor[Language::Nodes::FragmentSpread] << -> (node, parent) { names << node.name }
|
432
|
+
visitor.visit
|
433
|
+
names.uniq
|
434
|
+
end
|
435
|
+
|
427
436
|
def deep_freeze_json_object(obj)
|
428
437
|
case obj
|
429
438
|
when String
|
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.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: graphql
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: actionpack
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,6 +115,9 @@ dependencies:
|
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0.10'
|
118
|
+
- - "<="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 0.16.0
|
118
121
|
type: :development
|
119
122
|
prerelease: false
|
120
123
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -122,6 +125,9 @@ dependencies:
|
|
122
125
|
- - "~>"
|
123
126
|
- !ruby/object:Gem::Version
|
124
127
|
version: '0.10'
|
128
|
+
- - "<="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 0.16.0
|
125
131
|
- !ruby/object:Gem::Dependency
|
126
132
|
name: rubocop
|
127
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,7 +204,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
204
|
- !ruby/object:Gem::Version
|
199
205
|
version: '0'
|
200
206
|
requirements: []
|
201
|
-
rubygems_version: 3.
|
207
|
+
rubygems_version: 3.2.9
|
202
208
|
signing_key:
|
203
209
|
specification_version: 4
|
204
210
|
summary: GraphQL Client
|