graphql 1.10.2 → 1.10.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +10 -2
- data/lib/generators/graphql/templates/schema.erb +1 -0
- data/lib/graphql/define/defined_object_proxy.rb +1 -1
- data/lib/graphql/define/instance_definable.rb +1 -1
- data/lib/graphql/execution/interpreter.rb +3 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
- data/lib/graphql/execution/interpreter/runtime.rb +37 -121
- data/lib/graphql/field.rb +1 -1
- data/lib/graphql/language/nodes.rb +41 -83
- data/lib/graphql/language/parser.rb +96 -96
- data/lib/graphql/language/parser.y +96 -96
- data/lib/graphql/pagination/connections.rb +21 -5
- data/lib/graphql/query/null_context.rb +5 -1
- data/lib/graphql/query/variables.rb +9 -3
- data/lib/graphql/schema.rb +18 -2
- data/lib/graphql/schema/build_from_definition.rb +15 -1
- data/lib/graphql/schema/enum.rb +4 -1
- data/lib/graphql/schema/field.rb +11 -0
- data/lib/graphql/schema/field/connection_extension.rb +2 -1
- data/lib/graphql/schema/input_object.rb +21 -38
- data/lib/graphql/schema/member/base_dsl_methods.rb +11 -4
- data/lib/graphql/schema/member/has_arguments.rb +53 -2
- data/lib/graphql/schema/member/has_ast_node.rb +4 -1
- data/lib/graphql/schema/member/has_fields.rb +1 -1
- data/lib/graphql/schema/resolver.rb +2 -2
- data/lib/graphql/schema/resolver/has_payload_type.rb +3 -1
- data/lib/graphql/schema/warden.rb +10 -0
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +0 -1
- data/lib/graphql/subscriptions.rb +14 -9
- data/lib/graphql/subscriptions/subscription_root.rb +2 -1
- data/lib/graphql/version.rb +1 -1
- metadata +4 -3
@@ -23,6 +23,10 @@ module GraphQL
|
|
23
23
|
|
24
24
|
def [](key); end
|
25
25
|
|
26
|
+
def interpreter?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
26
30
|
class << self
|
27
31
|
extend Forwardable
|
28
32
|
|
@@ -32,7 +36,7 @@ module GraphQL
|
|
32
36
|
@instance = self.new
|
33
37
|
end
|
34
38
|
|
35
|
-
def_delegators :instance, :query, :schema, :warden
|
39
|
+
def_delegators :instance, :query, :schema, :warden, :interpreter?
|
36
40
|
end
|
37
41
|
end
|
38
42
|
end
|
@@ -34,7 +34,9 @@ module GraphQL
|
|
34
34
|
if validation_result.valid?
|
35
35
|
if value_was_provided
|
36
36
|
# Add the variable if a value was provided
|
37
|
-
memo[variable_name] = if
|
37
|
+
memo[variable_name] = if ctx.interpreter?
|
38
|
+
provided_value
|
39
|
+
elsif provided_value.nil?
|
38
40
|
nil
|
39
41
|
else
|
40
42
|
schema.error_handler.with_error_handling(context) do
|
@@ -42,8 +44,12 @@ module GraphQL
|
|
42
44
|
end
|
43
45
|
end
|
44
46
|
elsif default_value != nil
|
45
|
-
|
46
|
-
|
47
|
+
memo[variable_name] = if ctx.interpreter?
|
48
|
+
default_value
|
49
|
+
else
|
50
|
+
# Add the variable if it wasn't provided but it has a default value (including `null`)
|
51
|
+
GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
|
52
|
+
end
|
47
53
|
end
|
48
54
|
end
|
49
55
|
rescue GraphQL::CoercionError, GraphQL::ExecutionError => ex
|
data/lib/graphql/schema.rb
CHANGED
@@ -787,7 +787,7 @@ module GraphQL
|
|
787
787
|
def_delegators :graphql_definition,
|
788
788
|
# Execution
|
789
789
|
:execution_strategy_for_operation,
|
790
|
-
:validate,
|
790
|
+
:validate,
|
791
791
|
# Configuration
|
792
792
|
:metadata, :redefine,
|
793
793
|
:id_from_object_proc, :object_from_id_proc,
|
@@ -942,8 +942,24 @@ module GraphQL
|
|
942
942
|
find_inherited_value(:types, EMPTY_HASH)[type_name]
|
943
943
|
end
|
944
944
|
|
945
|
+
# @api private
|
946
|
+
attr_writer :connections
|
947
|
+
|
945
948
|
# @return [GraphQL::Pagination::Connections] if installed
|
946
|
-
|
949
|
+
def connections
|
950
|
+
if defined?(@connections)
|
951
|
+
@connections
|
952
|
+
else
|
953
|
+
inherited_connections = find_inherited_value(:connections, nil)
|
954
|
+
# This schema is part of an inheritance chain which is using new connections,
|
955
|
+
# make a new instance, so we don't pollute the upstream one.
|
956
|
+
if inherited_connections
|
957
|
+
@connections = Pagination::Connections.new(schema: self)
|
958
|
+
else
|
959
|
+
nil
|
960
|
+
end
|
961
|
+
end
|
962
|
+
end
|
947
963
|
|
948
964
|
def new_connections?
|
949
965
|
!!connections
|
@@ -42,7 +42,6 @@ module GraphQL
|
|
42
42
|
end
|
43
43
|
schema_definition = schema_defns.first
|
44
44
|
types = {}
|
45
|
-
types.merge!(GraphQL::Schema::BUILT_IN_TYPES)
|
46
45
|
directives = {}
|
47
46
|
type_resolver = ->(type) { resolve_type(types, type) }
|
48
47
|
|
@@ -69,6 +68,21 @@ module GraphQL
|
|
69
68
|
end
|
70
69
|
end
|
71
70
|
|
71
|
+
# At this point, if types named by the built in types are _late-bound_ types,
|
72
|
+
# that means they were referenced in the schema but not defined in the schema.
|
73
|
+
# That's supported for built-in types. (Eg, you can use `String` without defining it.)
|
74
|
+
# In that case, insert the concrete type definition now.
|
75
|
+
#
|
76
|
+
# However, if the type in `types` is a _concrete_ type definition, that means that
|
77
|
+
# the document contained an explicit definition of the scalar type.
|
78
|
+
# Don't override it in this case.
|
79
|
+
GraphQL::Schema::BUILT_IN_TYPES.each do |scalar_name, built_in_scalar|
|
80
|
+
existing_type = types[scalar_name]
|
81
|
+
if existing_type.is_a?(GraphQL::Schema::LateBoundType)
|
82
|
+
types[scalar_name] = built_in_scalar
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
72
86
|
directives = GraphQL::Schema.default_directives.merge(directives)
|
73
87
|
|
74
88
|
if schema_definition
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -63,8 +63,11 @@ module GraphQL
|
|
63
63
|
def enum_value_class(new_enum_value_class = nil)
|
64
64
|
if new_enum_value_class
|
65
65
|
@enum_value_class = new_enum_value_class
|
66
|
+
elsif defined?(@enum_value_class) && @enum_value_class
|
67
|
+
@enum_value_class
|
68
|
+
else
|
69
|
+
superclass <= GraphQL::Schema::Enum ? superclass.enum_value_class : nil
|
66
70
|
end
|
67
|
-
@enum_value_class || (superclass <= GraphQL::Schema::Enum ? superclass.enum_value_class : nil)
|
68
71
|
end
|
69
72
|
|
70
73
|
def kind
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -648,6 +648,17 @@ module GraphQL
|
|
648
648
|
# Apply any `prepare` methods. Not great code organization, can this go somewhere better?
|
649
649
|
arguments.each do |name, arg_defn|
|
650
650
|
ruby_kwargs_key = arg_defn.keyword
|
651
|
+
|
652
|
+
loads = arg_defn.loads
|
653
|
+
if ruby_kwargs.key?(ruby_kwargs_key) && loads && !arg_defn.from_resolver?
|
654
|
+
value = ruby_kwargs[ruby_kwargs_key]
|
655
|
+
ruby_kwargs[ruby_kwargs_key] = if arg_defn.type.list?
|
656
|
+
value.map { |val| load_application_object(arg_defn, loads, val, field_ctx.query.context) }
|
657
|
+
else
|
658
|
+
load_application_object(arg_defn, loads, value, field_ctx.query.context)
|
659
|
+
end
|
660
|
+
end
|
661
|
+
|
651
662
|
if ruby_kwargs.key?(ruby_kwargs_key) && arg_defn.prepare
|
652
663
|
ruby_kwargs[ruby_kwargs_key] = arg_defn.prepare_value(obj, ruby_kwargs[ruby_kwargs_key])
|
653
664
|
end
|
@@ -38,7 +38,8 @@ module GraphQL
|
|
38
38
|
value.max_page_size ||= field.max_page_size
|
39
39
|
value
|
40
40
|
elsif context.schema.new_connections?
|
41
|
-
context.schema.connections.
|
41
|
+
wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
|
42
|
+
context.schema.connections.wrap(field, value, arguments, context, wrappers: wrappers)
|
42
43
|
else
|
43
44
|
if object.is_a?(GraphQL::Schema::Object)
|
44
45
|
object = object.object
|
@@ -5,6 +5,7 @@ module GraphQL
|
|
5
5
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
6
6
|
extend Forwardable
|
7
7
|
extend GraphQL::Schema::Member::HasArguments
|
8
|
+
extend GraphQL::Schema::Member::HasArguments::ArgumentObjectLoader
|
8
9
|
extend GraphQL::Schema::Member::ValidatesInput
|
9
10
|
|
10
11
|
include GraphQL::Dig
|
@@ -23,19 +24,25 @@ module GraphQL
|
|
23
24
|
self.class.arguments.each do |name, arg_defn|
|
24
25
|
@arguments_by_keyword[arg_defn.keyword] = arg_defn
|
25
26
|
ruby_kwargs_key = arg_defn.keyword
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
|
28
|
+
if @ruby_style_hash.key?(ruby_kwargs_key)
|
29
|
+
loads = arg_defn.loads
|
30
|
+
# Resolvers do this loading themselves;
|
31
|
+
# With the interpreter, it's done during `coerce_arguments`
|
32
|
+
if loads && !arg_defn.from_resolver? && !context.interpreter?
|
33
|
+
value = @ruby_style_hash[ruby_kwargs_key]
|
34
|
+
@ruby_style_hash[ruby_kwargs_key] = if arg_defn.type.list?
|
35
|
+
value.map { |val| load_application_object(arg_defn, loads, val, context) }
|
36
|
+
else
|
37
|
+
load_application_object(arg_defn, loads, value, context)
|
38
|
+
end
|
34
39
|
end
|
35
|
-
end
|
36
40
|
|
37
|
-
|
38
|
-
|
41
|
+
# Weirdly, procs are applied during coercion, but not methods.
|
42
|
+
# Probably because these methods require a `self`.
|
43
|
+
if arg_defn.prepare.is_a?(Symbol) || context.nil? || !context.interpreter?
|
44
|
+
@ruby_style_hash[ruby_kwargs_key] = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
|
45
|
+
end
|
39
46
|
end
|
40
47
|
end
|
41
48
|
end
|
@@ -169,7 +176,7 @@ module GraphQL
|
|
169
176
|
# Items in the input that are unexpected
|
170
177
|
input.each do |name, value|
|
171
178
|
if visible_arguments_map[name].nil?
|
172
|
-
result.add_problem("
|
179
|
+
result.add_problem("Field is not defined on #{self.graphql_name}", [name])
|
173
180
|
end
|
174
181
|
end
|
175
182
|
|
@@ -188,32 +195,8 @@ module GraphQL
|
|
188
195
|
if value.nil?
|
189
196
|
return nil
|
190
197
|
end
|
191
|
-
|
192
|
-
|
193
|
-
arguments.each do |name, argument_defn|
|
194
|
-
arg_key = argument_defn.keyword
|
195
|
-
has_value = false
|
196
|
-
|
197
|
-
# Accept either string or symbol
|
198
|
-
field_value = if value.key?(name)
|
199
|
-
has_value = true
|
200
|
-
value[name]
|
201
|
-
elsif value.key?(arg_key)
|
202
|
-
has_value = true
|
203
|
-
value[arg_key]
|
204
|
-
elsif argument_defn.default_value?
|
205
|
-
has_value = true
|
206
|
-
argument_defn.default_value
|
207
|
-
else
|
208
|
-
nil
|
209
|
-
end
|
210
|
-
# Only continue if some value was found for this argument
|
211
|
-
if has_value
|
212
|
-
coerced_value = argument_defn.type.coerce_input(field_value, ctx)
|
213
|
-
prepared_value = argument_defn.prepare_value(nil, coerced_value, context: ctx)
|
214
|
-
input_values[arg_key] = prepared_value
|
215
|
-
end
|
216
|
-
end
|
198
|
+
|
199
|
+
input_values = coerce_arguments(nil, value, ctx)
|
217
200
|
|
218
201
|
input_obj_instance = self.new(ruby_kwargs: input_values, context: ctx, defaults_used: nil)
|
219
202
|
input_obj_instance.prepare
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def overridden_graphql_name
|
29
|
-
@graphql_name
|
29
|
+
defined?(@graphql_name) ? @graphql_name : nil
|
30
30
|
end
|
31
31
|
|
32
32
|
# Just a convenience method to point out that people should use graphql_name instead
|
@@ -46,8 +46,10 @@ module GraphQL
|
|
46
46
|
def description(new_description = nil)
|
47
47
|
if new_description
|
48
48
|
@description = new_description
|
49
|
-
|
49
|
+
elsif defined?(@description)
|
50
50
|
@description
|
51
|
+
else
|
52
|
+
nil
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
@@ -68,8 +70,10 @@ module GraphQL
|
|
68
70
|
def introspection(new_introspection = nil)
|
69
71
|
if !new_introspection.nil?
|
70
72
|
@introspection = new_introspection
|
71
|
-
|
73
|
+
elsif defined?(@introspection)
|
72
74
|
@introspection
|
75
|
+
else
|
76
|
+
false
|
73
77
|
end
|
74
78
|
end
|
75
79
|
|
@@ -82,8 +86,11 @@ module GraphQL
|
|
82
86
|
def mutation(mutation_class = nil)
|
83
87
|
if mutation_class
|
84
88
|
@mutation = mutation_class
|
89
|
+
elsif defined?(@mutation)
|
90
|
+
@mutation
|
91
|
+
else
|
92
|
+
nil
|
85
93
|
end
|
86
|
-
@mutation
|
87
94
|
end
|
88
95
|
|
89
96
|
# @return [GraphQL::BaseType] Convert this type to a legacy-style object.
|
@@ -63,12 +63,63 @@ module GraphQL
|
|
63
63
|
self.class.argument_class(new_arg_class)
|
64
64
|
end
|
65
65
|
|
66
|
+
# @param values [Hash<String, Object>]
|
67
|
+
# @param context [GraphQL::Query::Context]
|
68
|
+
# @return Hash<Symbol, Object>
|
69
|
+
def coerce_arguments(parent_object, values, context)
|
70
|
+
kwarg_arguments = {}
|
71
|
+
# Cache this hash to avoid re-merging it
|
72
|
+
arg_defns = self.arguments
|
73
|
+
|
74
|
+
arg_defns.each do |arg_name, arg_defn|
|
75
|
+
arg_key = arg_defn.keyword
|
76
|
+
has_value = false
|
77
|
+
if values.key?(arg_name)
|
78
|
+
has_value = true
|
79
|
+
value = values[arg_name]
|
80
|
+
elsif values.key?(arg_key)
|
81
|
+
has_value = true
|
82
|
+
value = values[arg_key]
|
83
|
+
elsif arg_defn.default_value?
|
84
|
+
has_value = true
|
85
|
+
value = arg_defn.default_value
|
86
|
+
end
|
87
|
+
|
88
|
+
if has_value
|
89
|
+
loads = arg_defn.loads
|
90
|
+
loaded_value = nil
|
91
|
+
if loads && !arg_defn.from_resolver?
|
92
|
+
loaded_value = if arg_defn.type.list?
|
93
|
+
value.map { |val| load_application_object(arg_defn, loads, val, context) }
|
94
|
+
else
|
95
|
+
load_application_object(arg_defn, loads, value, context)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
prepared_value = context.schema.error_handler.with_error_handling(context) do
|
100
|
+
|
101
|
+
coerced_value = if loaded_value
|
102
|
+
loaded_value
|
103
|
+
else
|
104
|
+
arg_defn.type.coerce_input(value, context)
|
105
|
+
end
|
106
|
+
|
107
|
+
arg_defn.prepare_value(parent_object, coerced_value, context: context)
|
108
|
+
end
|
109
|
+
kwarg_arguments[arg_defn.keyword] = prepared_value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
kwarg_arguments
|
113
|
+
end
|
114
|
+
|
66
115
|
module ArgumentClassAccessor
|
67
116
|
def argument_class(new_arg_class = nil)
|
68
117
|
if new_arg_class
|
69
118
|
@argument_class = new_arg_class
|
119
|
+
elsif defined?(@argument_class) && @argument_class
|
120
|
+
@argument_class
|
70
121
|
else
|
71
|
-
|
122
|
+
superclass.respond_to?(:argument_class) ? superclass.argument_class : GraphQL::Schema::Argument
|
72
123
|
end
|
73
124
|
end
|
74
125
|
end
|
@@ -85,7 +136,7 @@ module GraphQL
|
|
85
136
|
context.schema.object_from_id(id, context)
|
86
137
|
end
|
87
138
|
|
88
|
-
def load_application_object(argument, lookup_as_type, id)
|
139
|
+
def load_application_object(argument, lookup_as_type, id, context)
|
89
140
|
# See if any object can be found for this ID
|
90
141
|
loaded_application_object = object_from_id(lookup_as_type, id, context)
|
91
142
|
context.schema.after_lazy(loaded_application_object) do |application_object|
|
@@ -70,7 +70,7 @@ module GraphQL
|
|
70
70
|
def field_class(new_field_class = nil)
|
71
71
|
if new_field_class
|
72
72
|
@field_class = new_field_class
|
73
|
-
elsif @field_class
|
73
|
+
elsif defined?(@field_class) && @field_class
|
74
74
|
@field_class
|
75
75
|
elsif self.is_a?(Class)
|
76
76
|
superclass.respond_to?(:field_class) ? superclass.field_class : GraphQL::Schema::Field
|
@@ -285,7 +285,7 @@ module GraphQL
|
|
285
285
|
argument = @arguments_by_keyword[:#{arg_defn.keyword}]
|
286
286
|
lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
|
287
287
|
context.schema.after_lazy(values) do |values2|
|
288
|
-
GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, lookup_as_type, value) })
|
288
|
+
GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, lookup_as_type, value, context) })
|
289
289
|
end
|
290
290
|
end
|
291
291
|
RUBY
|
@@ -294,7 +294,7 @@ module GraphQL
|
|
294
294
|
def load_#{arg_defn.keyword}(value)
|
295
295
|
argument = @arguments_by_keyword[:#{arg_defn.keyword}]
|
296
296
|
lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
|
297
|
-
load_application_object(argument, lookup_as_type, value)
|
297
|
+
load_application_object(argument, lookup_as_type, value, context)
|
298
298
|
end
|
299
299
|
RUBY
|
300
300
|
else
|
@@ -26,8 +26,10 @@ module GraphQL
|
|
26
26
|
def field_class(new_class = nil)
|
27
27
|
if new_class
|
28
28
|
@field_class = new_class
|
29
|
+
elsif defined?(@field_class) && @field_class
|
30
|
+
@field_class
|
29
31
|
else
|
30
|
-
|
32
|
+
find_inherited_value(:field_class, GraphQL::Schema::Field)
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
@@ -230,6 +230,16 @@ module GraphQL
|
|
230
230
|
unvisited_types << root_type if root_type
|
231
231
|
end
|
232
232
|
unvisited_types.concat(@schema.introspection_system.types.values)
|
233
|
+
|
234
|
+
directives.each do |dir_class|
|
235
|
+
dir_class.arguments.values.each do |arg_defn|
|
236
|
+
arg_t = arg_defn.type.unwrap
|
237
|
+
if get_type(arg_t.graphql_name)
|
238
|
+
unvisited_types << arg_t
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
233
243
|
@schema.orphan_types.each do |orphan_type|
|
234
244
|
if get_type(orphan_type.graphql_name)
|
235
245
|
unvisited_types << orphan_type
|
@@ -27,7 +27,6 @@ module GraphQL
|
|
27
27
|
|
28
28
|
if !valid
|
29
29
|
error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
|
30
|
-
VariableDefaultValuesAreCorrectlyTypedError
|
31
30
|
add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
|
32
31
|
error_message,
|
33
32
|
nodes: node,
|
@@ -88,6 +88,10 @@ module GraphQL
|
|
88
88
|
def execute(subscription_id, event, object)
|
89
89
|
# Lookup the saved data for this subscription
|
90
90
|
query_data = read_subscription(subscription_id)
|
91
|
+
if query_data.nil?
|
92
|
+
# Jump down to the `delete_subscription` call
|
93
|
+
raise GraphQL::Schema::Subscription::UnsubscribedError
|
94
|
+
end
|
91
95
|
# Fetch the required keys from the saved data
|
92
96
|
query_string = query_data.fetch(:query_string)
|
93
97
|
variables = query_data.fetch(:variables)
|
@@ -95,14 +99,12 @@ module GraphQL
|
|
95
99
|
operation_name = query_data.fetch(:operation_name)
|
96
100
|
# Re-evaluate the saved query
|
97
101
|
result = @schema.execute(
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
root_value: object,
|
105
|
-
}
|
102
|
+
query: query_string,
|
103
|
+
context: context,
|
104
|
+
subscription_topic: event.topic,
|
105
|
+
operation_name: operation_name,
|
106
|
+
variables: variables,
|
107
|
+
root_value: object,
|
106
108
|
)
|
107
109
|
deliver(subscription_id, result)
|
108
110
|
rescue GraphQL::Schema::Subscription::NoUpdateError
|
@@ -226,7 +228,10 @@ module GraphQL
|
|
226
228
|
# match query arguments.
|
227
229
|
arg_owner.arguments.each do |name, arg_defn|
|
228
230
|
if arg_defn.default_value? && !normalized_args.key?(arg_defn.name)
|
229
|
-
|
231
|
+
default_value = arg_defn.default_value
|
232
|
+
# We don't have an underlying "object" here, so it can't call methods.
|
233
|
+
# This is broken.
|
234
|
+
normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context: GraphQL::Query::NullContext)
|
230
235
|
end
|
231
236
|
end
|
232
237
|
|