graphql 1.5.4 → 1.5.5
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/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
|