graphql 1.5.4 → 1.5.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/define/instance_definable.rb +54 -29
- data/lib/graphql/enum_type.rb +0 -1
- data/lib/graphql/execution/execute.rb +15 -9
- data/lib/graphql/execution/field_result.rb +3 -6
- data/lib/graphql/execution/lazy/lazy_method_map.rb +66 -12
- data/lib/graphql/language.rb +15 -1
- data/lib/graphql/language/nodes.rb +17 -1
- data/lib/graphql/language/parser.rb +8 -8
- data/lib/graphql/language/parser.y +8 -8
- data/lib/graphql/query.rb +3 -4
- data/lib/graphql/query/literal_input.rb +2 -0
- data/lib/graphql/relay/mutation.rb +2 -3
- data/lib/graphql/schema.rb +1 -3
- data/lib/graphql/schema/loader.rb +26 -4
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +32 -0
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +11 -5
- data/lib/graphql/static_validation/validation_context.rb +0 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/define/instance_definable_spec.rb +21 -0
- data/spec/graphql/execution/execute_spec.rb +61 -0
- data/spec/graphql/execution/lazy/lazy_method_map_spec.rb +57 -0
- data/spec/graphql/input_object_type_spec.rb +2 -2
- data/spec/graphql/introspection/input_value_type_spec.rb +3 -1
- data/spec/graphql/introspection/type_type_spec.rb +1 -0
- data/spec/graphql/query_spec.rb +69 -10
- data/spec/graphql/schema/loader_spec.rb +7 -3
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +0 -1
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +6 -6
- data/spec/graphql/static_validation/rules/no_definitions_are_present_spec.rb +28 -0
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +17 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/dummy/schema.rb +11 -0
- data/spec/support/star_wars/data.rb +16 -2
- metadata +21 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef472807dc31f5d5bc215491e5c4a512d853b517
|
4
|
+
data.tar.gz: 151d4556ac7c07a270e37e40a71616dfad89129b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 825fdd6d9aac63cf8904e1a52be462499286063ccab8799497affabf52d1a0b8b2afe090e5fef84a564c087803ca4b4b38d4dc7fa898e145047e749aa1d0e12c
|
7
|
+
data.tar.gz: 43c0d734ac9014bd7df9f9e26e70052e62f2ae0d7a84b59642622e2786ffd806fbc076638411ce4621b2dd355c7479ac05c7e9fed8993701126611c8ebe428d6
|
@@ -116,18 +116,7 @@ module GraphQL
|
|
116
116
|
def define(**kwargs, &block)
|
117
117
|
# make sure the previous definition_proc was executed:
|
118
118
|
ensure_defined
|
119
|
-
|
120
|
-
method_names = self.class.ensure_defined_method_names
|
121
|
-
@pending_methods = method_names.map { |n| self.class.instance_method(n) }
|
122
|
-
self.singleton_class.class_eval do
|
123
|
-
method_names.each do |method_name|
|
124
|
-
define_method(method_name) { |*args, &block|
|
125
|
-
ensure_defined
|
126
|
-
self.send(method_name, *args, &block)
|
127
|
-
}
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
119
|
+
stash_dependent_methods
|
131
120
|
@pending_definition = Definition.new(kwargs, block)
|
132
121
|
nil
|
133
122
|
end
|
@@ -156,31 +145,67 @@ module GraphQL
|
|
156
145
|
# @return [void]
|
157
146
|
def ensure_defined
|
158
147
|
if @pending_definition
|
159
|
-
|
160
148
|
defn = @pending_definition
|
161
149
|
@pending_definition = nil
|
162
150
|
|
163
|
-
|
164
|
-
self.singleton_class.class_eval {
|
165
|
-
pending_methods.each do |method|
|
166
|
-
define_method(method.name, method)
|
167
|
-
end
|
168
|
-
}
|
169
|
-
@pending_methods = nil
|
151
|
+
revive_dependent_methods
|
170
152
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
153
|
+
begin
|
154
|
+
defn_proxy = DefinedObjectProxy.new(self)
|
155
|
+
# Apply definition from `define(...)` kwargs
|
156
|
+
defn.define_keywords.each do |keyword, value|
|
157
|
+
defn_proxy.public_send(keyword, value)
|
158
|
+
end
|
159
|
+
# and/or apply definition from `define { ... }` block
|
160
|
+
if defn.define_proc
|
161
|
+
defn_proxy.instance_eval(&defn.define_proc)
|
162
|
+
end
|
163
|
+
rescue StandardError
|
164
|
+
# The definition block failed to run, so make this object pending again:
|
165
|
+
stash_dependent_methods
|
166
|
+
@pending_definition = defn
|
167
|
+
raise
|
179
168
|
end
|
169
|
+
end
|
170
|
+
nil
|
171
|
+
end
|
180
172
|
|
173
|
+
# Take the pending methods and put them back on this object's singleton class.
|
174
|
+
# This reverts the process done by {#stash_dependent_methods}
|
175
|
+
# @return [void]
|
176
|
+
def revive_dependent_methods
|
177
|
+
pending_methods = @pending_methods
|
178
|
+
self.singleton_class.class_eval {
|
179
|
+
pending_methods.each do |method|
|
180
|
+
define_method(method.name, method)
|
181
|
+
end
|
182
|
+
}
|
183
|
+
@pending_methods = nil
|
184
|
+
end
|
181
185
|
|
186
|
+
# Find the method names which were declared as definition-dependent,
|
187
|
+
# then grab the method definitions off of this object's class
|
188
|
+
# and store them for later.
|
189
|
+
#
|
190
|
+
# Then make a dummy method for each of those method names which:
|
191
|
+
#
|
192
|
+
# - Triggers the pending definition, if there is one
|
193
|
+
# - Calls the same method again.
|
194
|
+
#
|
195
|
+
# It's assumed that {#ensure_defined} will put the original method definitions
|
196
|
+
# back in place with {#revive_dependent_methods}.
|
197
|
+
# @return [void]
|
198
|
+
def stash_dependent_methods
|
199
|
+
method_names = self.class.ensure_defined_method_names
|
200
|
+
@pending_methods = method_names.map { |n| self.class.instance_method(n) }
|
201
|
+
self.singleton_class.class_eval do
|
202
|
+
method_names.each do |method_name|
|
203
|
+
define_method(method_name) { |*args, &block|
|
204
|
+
ensure_defined
|
205
|
+
self.send(method_name, *args, &block)
|
206
|
+
}
|
207
|
+
end
|
182
208
|
end
|
183
|
-
nil
|
184
209
|
end
|
185
210
|
|
186
211
|
class Definition
|
data/lib/graphql/enum_type.rb
CHANGED
@@ -72,22 +72,22 @@ module GraphQL
|
|
72
72
|
|
73
73
|
result = if query.schema.lazy?(raw_value)
|
74
74
|
field.prepare_lazy(raw_value, arguments, field_ctx).then { |inner_value|
|
75
|
-
continue_resolve_field(selection, parent_type, field, inner_value, field_ctx)
|
75
|
+
continue_resolve_field(owner, selection, parent_type, field, inner_value, field_ctx)
|
76
76
|
}
|
77
77
|
elsif raw_value.is_a?(GraphQL::Execution::Lazy)
|
78
78
|
# It came from a connection resolve, assume it was already instrumented
|
79
79
|
raw_value.then { |inner_value|
|
80
|
-
continue_resolve_field(selection, parent_type, field, inner_value, field_ctx)
|
80
|
+
continue_resolve_field(owner, selection, parent_type, field, inner_value, field_ctx)
|
81
81
|
}
|
82
82
|
else
|
83
|
-
continue_resolve_field(selection, parent_type, field, raw_value, field_ctx)
|
83
|
+
continue_resolve_field(owner, selection, parent_type, field, raw_value, field_ctx)
|
84
84
|
end
|
85
85
|
|
86
86
|
case result
|
87
87
|
when PROPAGATE_NULL, GraphQL::Execution::Lazy, SelectionResult
|
88
88
|
FieldResult.new(
|
89
89
|
owner: owner,
|
90
|
-
|
90
|
+
type: field.type,
|
91
91
|
value: result,
|
92
92
|
)
|
93
93
|
else
|
@@ -95,7 +95,7 @@ module GraphQL
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
def continue_resolve_field(selection, parent_type, field, raw_value, field_ctx)
|
98
|
+
def continue_resolve_field(owner, selection, parent_type, field, raw_value, field_ctx)
|
99
99
|
query = field_ctx.query
|
100
100
|
|
101
101
|
case raw_value
|
@@ -115,6 +115,7 @@ module GraphQL
|
|
115
115
|
end
|
116
116
|
|
117
117
|
resolve_value(
|
118
|
+
owner,
|
118
119
|
parent_type,
|
119
120
|
field,
|
120
121
|
field.type,
|
@@ -124,7 +125,7 @@ module GraphQL
|
|
124
125
|
)
|
125
126
|
end
|
126
127
|
|
127
|
-
def resolve_value(parent_type, field_defn, field_type, value, selection, field_ctx)
|
128
|
+
def resolve_value(owner, parent_type, field_defn, field_type, value, selection, field_ctx)
|
128
129
|
if value.nil?
|
129
130
|
if field_type.kind.non_null?
|
130
131
|
type_error = GraphQL::InvalidNullError.new(parent_type, field_defn, value)
|
@@ -146,7 +147,7 @@ module GraphQL
|
|
146
147
|
when GraphQL::TypeKinds::ENUM
|
147
148
|
field_type.coerce_result(value, field_ctx.query.warden)
|
148
149
|
when GraphQL::TypeKinds::LIST
|
149
|
-
|
150
|
+
inner_type = field_type.of_type
|
150
151
|
i = 0
|
151
152
|
result = []
|
152
153
|
value.each do |inner_value|
|
@@ -157,20 +158,24 @@ module GraphQL
|
|
157
158
|
field: field_defn,
|
158
159
|
)
|
159
160
|
|
160
|
-
|
161
|
+
inner_result = resolve_value(
|
162
|
+
owner,
|
161
163
|
parent_type,
|
162
164
|
field_defn,
|
163
|
-
|
165
|
+
inner_type,
|
164
166
|
inner_value,
|
165
167
|
selection,
|
166
168
|
inner_ctx,
|
167
169
|
)
|
170
|
+
|
171
|
+
result << GraphQL::Execution::FieldResult.new(type: inner_type, owner: owner, value: inner_result)
|
168
172
|
i += 1
|
169
173
|
end
|
170
174
|
result
|
171
175
|
when GraphQL::TypeKinds::NON_NULL
|
172
176
|
wrapped_type = field_type.of_type
|
173
177
|
inner_value = resolve_value(
|
178
|
+
owner,
|
174
179
|
parent_type,
|
175
180
|
field_defn,
|
176
181
|
wrapped_type,
|
@@ -196,6 +201,7 @@ module GraphQL
|
|
196
201
|
PROPAGATE_NULL
|
197
202
|
else
|
198
203
|
resolve_value(
|
204
|
+
owner,
|
199
205
|
parent_type,
|
200
206
|
field_defn,
|
201
207
|
resolved_type,
|
@@ -7,14 +7,11 @@ module GraphQL
|
|
7
7
|
# @return [Any, Lazy] the GraphQL-ready response value, or a {Lazy} instance
|
8
8
|
attr_reader :value
|
9
9
|
|
10
|
-
# @return [GraphQL::Field] The field which resolved this value
|
11
|
-
attr_reader :field
|
12
|
-
|
13
10
|
# @return [SelectionResult] The result object that this field belongs to
|
14
11
|
attr_reader :owner
|
15
12
|
|
16
|
-
def initialize(
|
17
|
-
@
|
13
|
+
def initialize(type:, value:, owner:)
|
14
|
+
@type = type
|
18
15
|
@owner = owner
|
19
16
|
self.value = value
|
20
17
|
end
|
@@ -35,7 +32,7 @@ module GraphQL
|
|
35
32
|
end
|
36
33
|
|
37
34
|
if new_value == GraphQL::Execution::Execute::PROPAGATE_NULL
|
38
|
-
if
|
35
|
+
if @type.kind.non_null?
|
39
36
|
@owner.propagate_null
|
40
37
|
else
|
41
38
|
@value = nil
|
@@ -1,22 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'thread'
|
3
|
+
begin
|
4
|
+
require 'concurrent'
|
5
|
+
rescue LoadError
|
6
|
+
# no problem, we'll fallback to our own map
|
7
|
+
end
|
8
|
+
|
2
9
|
module GraphQL
|
3
10
|
module Execution
|
4
11
|
class Lazy
|
5
12
|
# {GraphQL::Schema} uses this to match returned values to lazy resolution methods.
|
6
13
|
# Methods may be registered for classes, they apply to its subclasses also.
|
7
14
|
# The result of this lookup is cached for future resolutions.
|
15
|
+
# Instances of this class are thread-safe.
|
8
16
|
# @api private
|
9
17
|
# @see {Schema#lazy?} looks up values from this map
|
10
18
|
class LazyMethodMap
|
11
|
-
def initialize
|
12
|
-
@storage =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
h[value_class] = h[registered_superclass]
|
18
|
-
end
|
19
|
-
end
|
19
|
+
def initialize(use_concurrent: defined?(Concurrent::Map))
|
20
|
+
@storage = use_concurrent ? Concurrent::Map.new : ConcurrentishMap.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize_copy(other)
|
24
|
+
@storage = other.storage.dup
|
20
25
|
end
|
21
26
|
|
22
27
|
# @param lazy_class [Class] A class which represents a lazy value (subclasses may also be used)
|
@@ -28,11 +33,60 @@ module GraphQL
|
|
28
33
|
# @param value [Object] an object which may have a `lazy_value_method` registered for its class or superclasses
|
29
34
|
# @return [Symbol, nil] The `lazy_value_method` for this object, or nil
|
30
35
|
def get(value)
|
31
|
-
@storage
|
36
|
+
@storage.compute_if_absent(value.class) { find_superclass_method(value.class) }
|
32
37
|
end
|
33
38
|
|
34
|
-
|
35
|
-
|
39
|
+
protected
|
40
|
+
|
41
|
+
attr_reader :storage
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def find_superclass_method(value_class)
|
46
|
+
@storage.each { |lazy_class, lazy_value_method|
|
47
|
+
return lazy_value_method if value_class < lazy_class
|
48
|
+
}
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Mock the Concurrent::Map API
|
53
|
+
class ConcurrentishMap
|
54
|
+
extend Forwardable
|
55
|
+
# Technically this should be under the mutex too,
|
56
|
+
# but I know it's only used when the lock is already acquired.
|
57
|
+
def_delegators :@storage, :each, :size
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
@semaphore = Mutex.new
|
61
|
+
# Access to this hash must always be managed by the mutex
|
62
|
+
# since it may be modified at runtime
|
63
|
+
@storage = {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def []=(key, value)
|
67
|
+
@semaphore.synchronize {
|
68
|
+
@storage[key] = value
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def compute_if_absent(key)
|
73
|
+
@semaphore.synchronize {
|
74
|
+
@storage.fetch(key) { @storage[key] = yield }
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def initialize_copy(other)
|
79
|
+
@semaphore = Mutex.new
|
80
|
+
@storage = other.copy_storage
|
81
|
+
end
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
def copy_storage
|
86
|
+
@semaphore.synchronize {
|
87
|
+
@storage.dup
|
88
|
+
}
|
89
|
+
end
|
36
90
|
end
|
37
91
|
end
|
38
92
|
end
|
data/lib/graphql/language.rb
CHANGED
@@ -12,7 +12,21 @@ module GraphQL
|
|
12
12
|
module Language
|
13
13
|
# @api private
|
14
14
|
def self.serialize(value)
|
15
|
-
|
15
|
+
if value.is_a?(Hash)
|
16
|
+
serialized_hash = value.map do |k, v|
|
17
|
+
"#{k}:#{serialize v}"
|
18
|
+
end.join(",")
|
19
|
+
|
20
|
+
"{#{serialized_hash}}"
|
21
|
+
elsif value.is_a?(Array)
|
22
|
+
serialized_array = value.map do |v|
|
23
|
+
serialize v
|
24
|
+
end.join(",")
|
25
|
+
|
26
|
+
"[#{serialized_array}]"
|
27
|
+
else
|
28
|
+
JSON.generate(value, quirks_mode: true)
|
29
|
+
end
|
16
30
|
end
|
17
31
|
end
|
18
32
|
end
|
@@ -268,10 +268,26 @@ module GraphQL
|
|
268
268
|
def to_h(options={})
|
269
269
|
arguments.inject({}) do |memo, pair|
|
270
270
|
v = pair.value
|
271
|
-
memo[pair.name] =
|
271
|
+
memo[pair.name] = serialize_value_for_hash v
|
272
272
|
memo
|
273
273
|
end
|
274
274
|
end
|
275
|
+
|
276
|
+
private
|
277
|
+
|
278
|
+
def serialize_value_for_hash(value)
|
279
|
+
if value.is_a? InputObject
|
280
|
+
value.to_h
|
281
|
+
elsif value.is_a? Array
|
282
|
+
value.map do |v|
|
283
|
+
serialize_value_for_hash v
|
284
|
+
end
|
285
|
+
elsif value.is_a? NullValue
|
286
|
+
nil
|
287
|
+
else
|
288
|
+
value
|
289
|
+
end
|
290
|
+
end
|
275
291
|
end
|
276
292
|
|
277
293
|
|
@@ -1383,7 +1383,7 @@ module_eval(<<'.,.,', 'parser.y', 258)
|
|
1383
1383
|
|
1384
1384
|
module_eval(<<'.,.,', 'parser.y', 267)
|
1385
1385
|
def _reduce_106(val, _values, result)
|
1386
|
-
return make_node(:SchemaDefinition, val[2])
|
1386
|
+
return make_node(:SchemaDefinition, position_source: val[0], **val[2])
|
1387
1387
|
result
|
1388
1388
|
end
|
1389
1389
|
.,.,
|
@@ -1418,14 +1418,14 @@ module_eval(<<'.,.,', 'parser.y', 274)
|
|
1418
1418
|
|
1419
1419
|
module_eval(<<'.,.,', 'parser.y', 284)
|
1420
1420
|
def _reduce_116(val, _values, result)
|
1421
|
-
return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2], description: get_description(val[0]))
|
1421
|
+
return make_node(:ScalarTypeDefinition, name: val[1], directives: val[2], description: get_description(val[0]), position_source: val[0])
|
1422
1422
|
result
|
1423
1423
|
end
|
1424
1424
|
.,.,
|
1425
1425
|
|
1426
1426
|
module_eval(<<'.,.,', 'parser.y', 288)
|
1427
1427
|
def _reduce_117(val, _values, result)
|
1428
|
-
return make_node(:ObjectTypeDefinition, name: val[1], interfaces: val[2], directives: val[3], fields: val[5], description: get_description(val[0]))
|
1428
|
+
return make_node(:ObjectTypeDefinition, name: val[1], interfaces: val[2], directives: val[3], fields: val[5], description: get_description(val[0]), position_source: val[0])
|
1429
1429
|
|
1430
1430
|
result
|
1431
1431
|
end
|
@@ -1505,7 +1505,7 @@ module_eval(<<'.,.,', 'parser.y', 315)
|
|
1505
1505
|
|
1506
1506
|
module_eval(<<'.,.,', 'parser.y', 319)
|
1507
1507
|
def _reduce_128(val, _values, result)
|
1508
|
-
return make_node(:InterfaceTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]))
|
1508
|
+
return make_node(:InterfaceTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]), position_source: val[0])
|
1509
1509
|
|
1510
1510
|
result
|
1511
1511
|
end
|
@@ -1527,7 +1527,7 @@ module_eval(<<'.,.,', 'parser.y', 324)
|
|
1527
1527
|
|
1528
1528
|
module_eval(<<'.,.,', 'parser.y', 328)
|
1529
1529
|
def _reduce_131(val, _values, result)
|
1530
|
-
return make_node(:UnionTypeDefinition, name: val[1], directives: val[2], types: val[4], description: get_description(val[0]))
|
1530
|
+
return make_node(:UnionTypeDefinition, name: val[1], directives: val[2], types: val[4], description: get_description(val[0]), position_source: val[0])
|
1531
1531
|
|
1532
1532
|
result
|
1533
1533
|
end
|
@@ -1535,7 +1535,7 @@ module_eval(<<'.,.,', 'parser.y', 328)
|
|
1535
1535
|
|
1536
1536
|
module_eval(<<'.,.,', 'parser.y', 333)
|
1537
1537
|
def _reduce_132(val, _values, result)
|
1538
|
-
return make_node(:EnumTypeDefinition, name: val[1], directives: val[2], values: val[4], description: get_description(val[0]))
|
1538
|
+
return make_node(:EnumTypeDefinition, name: val[1], directives: val[2], values: val[4], description: get_description(val[0]), position_source: val[0])
|
1539
1539
|
|
1540
1540
|
result
|
1541
1541
|
end
|
@@ -1543,7 +1543,7 @@ module_eval(<<'.,.,', 'parser.y', 333)
|
|
1543
1543
|
|
1544
1544
|
module_eval(<<'.,.,', 'parser.y', 338)
|
1545
1545
|
def _reduce_133(val, _values, result)
|
1546
|
-
return make_node(:InputObjectTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]))
|
1546
|
+
return make_node(:InputObjectTypeDefinition, name: val[1], directives: val[2], fields: val[4], description: get_description(val[0]), position_source: val[0])
|
1547
1547
|
|
1548
1548
|
result
|
1549
1549
|
end
|
@@ -1551,7 +1551,7 @@ module_eval(<<'.,.,', 'parser.y', 338)
|
|
1551
1551
|
|
1552
1552
|
module_eval(<<'.,.,', 'parser.y', 343)
|
1553
1553
|
def _reduce_134(val, _values, result)
|
1554
|
-
return make_node(:DirectiveDefinition, name: val[2], arguments: val[3], locations: val[5], description: get_description(val[0]))
|
1554
|
+
return make_node(:DirectiveDefinition, name: val[2], arguments: val[3], locations: val[5], description: get_description(val[0]), position_source: val[0])
|
1555
1555
|
|
1556
1556
|
result
|
1557
1557
|
end
|