graphql 1.11.1 → 1.11.2
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/templates/graphql_controller.erb +11 -9
- data/lib/graphql/directive.rb +4 -0
- data/lib/graphql/execution/interpreter/runtime.rb +3 -2
- data/lib/graphql/field.rb +4 -0
- data/lib/graphql/input_object_type.rb +4 -0
- data/lib/graphql/language/nodes.rb +1 -0
- data/lib/graphql/language/visitor.rb +2 -2
- data/lib/graphql/pagination/connection.rb +6 -8
- data/lib/graphql/pagination/connections.rb +12 -0
- data/lib/graphql/schema.rb +7 -16
- data/lib/graphql/schema/argument.rb +5 -0
- data/lib/graphql/schema/enum_value.rb +1 -0
- data/lib/graphql/schema/field/connection_extension.rb +3 -0
- data/lib/graphql/schema/input_object.rb +16 -15
- data/lib/graphql/schema/loader.rb +19 -1
- data/lib/graphql/schema/member/has_arguments.rb +16 -0
- data/lib/graphql/schema/union.rb +29 -0
- data/lib/graphql/schema/warden.rb +6 -0
- data/lib/graphql/static_validation/literal_validator.rb +7 -7
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -2
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +24 -13
- data/lib/graphql/subscriptions/serialize.rb +22 -4
- data/lib/graphql/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bafdf456b47766daa370e9f8559e2f525b37d24b6f5a253742eba71759428f29
|
4
|
+
data.tar.gz: f2dc55cb9c2777104f23358a27e01328ed4b4df5334ce237e9af1cae731c3ab7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7120485f538e60577a55b9674cb21e89785d10becfb5545e8412c284547b4c066c3c7abff15b53edca990cb49fd659eeb0df16e84f6bfb511aca89dd41d2834
|
7
|
+
data.tar.gz: a48a2c59655e72c44d711f69cd94c7649a0ada18e1dba50cabadaa8a2748b12021ed7f03a6accca78bfba4aab93de2d37cf0b5e119f3f08a12a7d9784667af15
|
@@ -5,7 +5,7 @@ class GraphqlController < ApplicationController
|
|
5
5
|
# protect_from_forgery with: :null_session
|
6
6
|
|
7
7
|
def execute
|
8
|
-
variables =
|
8
|
+
variables = prepare_variables(params[:variables])
|
9
9
|
query = params[:query]
|
10
10
|
operation_name = params[:operationName]
|
11
11
|
context = {
|
@@ -21,21 +21,23 @@ class GraphqlController < ApplicationController
|
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
-
# Handle form data, JSON body, or a blank value
|
25
|
-
def
|
26
|
-
case
|
24
|
+
# Handle variables in form data, JSON body, or a blank value
|
25
|
+
def prepare_variables(variables_param)
|
26
|
+
case variables_param
|
27
27
|
when String
|
28
|
-
if
|
29
|
-
|
28
|
+
if variables_param.present?
|
29
|
+
JSON.parse(variables_param) || {}
|
30
30
|
else
|
31
31
|
{}
|
32
32
|
end
|
33
|
-
when Hash
|
34
|
-
|
33
|
+
when Hash
|
34
|
+
variables_param
|
35
|
+
when ActionController::Parameters
|
36
|
+
variables_param.to_unsafe_hash # GraphQL-Ruby will validate name and type of incoming variables.
|
35
37
|
when nil
|
36
38
|
{}
|
37
39
|
else
|
38
|
-
raise ArgumentError, "Unexpected parameter: #{
|
40
|
+
raise ArgumentError, "Unexpected parameter: #{variables_param}"
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
data/lib/graphql/directive.rb
CHANGED
@@ -461,8 +461,9 @@ module GraphQL
|
|
461
461
|
end
|
462
462
|
|
463
463
|
def arguments(graphql_object, arg_owner, ast_node)
|
464
|
-
# Don't cache arguments if field extras are requested since
|
465
|
-
if arg_owner.arguments_statically_coercible? &&
|
464
|
+
# Don't cache arguments if field extras or extensions are requested since they can mutate the argument data structure
|
465
|
+
if arg_owner.arguments_statically_coercible? &&
|
466
|
+
(!arg_owner.is_a?(GraphQL::Schema::Field) || (arg_owner.extras.empty? && arg_owner.extensions.empty?))
|
466
467
|
query.arguments_for(ast_node, arg_owner)
|
467
468
|
else
|
468
469
|
# The arguments must be prepared in the context of the given object
|
data/lib/graphql/field.rb
CHANGED
@@ -89,7 +89,7 @@ module GraphQL
|
|
89
89
|
# @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
|
90
90
|
# @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
|
91
91
|
def on_abstract_node(node, parent)
|
92
|
-
if node
|
92
|
+
if node.equal?(DELETE_NODE)
|
93
93
|
# This might be passed to `super(DELETE_NODE, ...)`
|
94
94
|
# by a user hook, don't want to keep visiting in that case.
|
95
95
|
nil
|
@@ -179,7 +179,7 @@ module GraphQL
|
|
179
179
|
# The user-provided hook returned a new node.
|
180
180
|
new_parent = new_parent && new_parent.replace_child(node, new_node)
|
181
181
|
return new_node, new_parent
|
182
|
-
elsif new_node
|
182
|
+
elsif new_node.equal?(DELETE_NODE)
|
183
183
|
# The user-provided hook requested to remove this node
|
184
184
|
new_parent = new_parent && new_parent.delete_child(node)
|
185
185
|
return nil, new_parent
|
@@ -15,11 +15,6 @@ module GraphQL
|
|
15
15
|
class PaginationImplementationMissingError < GraphQL::Error
|
16
16
|
end
|
17
17
|
|
18
|
-
# @return [Class] The class to use for wrapping items as `edges { ... }`. Defaults to `Connection::Edge`
|
19
|
-
def self.edge_class
|
20
|
-
self::Edge
|
21
|
-
end
|
22
|
-
|
23
18
|
# @return [Object] A list object, from the application. This is the unpaginated value passed into the connection.
|
24
19
|
attr_reader :items
|
25
20
|
|
@@ -58,7 +53,7 @@ module GraphQL
|
|
58
53
|
# @param last [Integer, nil] Limit parameter from the client, if provided
|
59
54
|
# @param before [String, nil] A cursor for pagination, if the client provided one.
|
60
55
|
# @param max_page_size [Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given.
|
61
|
-
def initialize(items, parent: nil, context: nil, first: nil, after: nil, max_page_size: :not_given, last: nil, before: nil)
|
56
|
+
def initialize(items, parent: nil, context: nil, first: nil, after: nil, max_page_size: :not_given, last: nil, before: nil, edge_class: nil)
|
62
57
|
@items = items
|
63
58
|
@parent = parent
|
64
59
|
@context = context
|
@@ -66,7 +61,7 @@ module GraphQL
|
|
66
61
|
@after_value = after
|
67
62
|
@last_value = last
|
68
63
|
@before_value = before
|
69
|
-
|
64
|
+
@edge_class = edge_class || self.class::Edge
|
70
65
|
# This is only true if the object was _initialized_ with an override
|
71
66
|
# or if one is assigned later.
|
72
67
|
@has_max_page_size_override = max_page_size != :not_given
|
@@ -117,9 +112,12 @@ module GraphQL
|
|
117
112
|
|
118
113
|
# @return [Array<Edge>] {nodes}, but wrapped with Edge instances
|
119
114
|
def edges
|
120
|
-
@edges ||= nodes.map { |n|
|
115
|
+
@edges ||= nodes.map { |n| @edge_class.new(n, self) }
|
121
116
|
end
|
122
117
|
|
118
|
+
# @return [Class] A wrapper class for edges of this connection
|
119
|
+
attr_accessor :edge_class
|
120
|
+
|
123
121
|
# @return [Array<Object>] A slice of {items}, constrained by {@first_value}/{@after_value}/{@last_value}/{@before_value}
|
124
122
|
def nodes
|
125
123
|
raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`"
|
@@ -86,9 +86,21 @@ module GraphQL
|
|
86
86
|
after: arguments[:after],
|
87
87
|
last: arguments[:last],
|
88
88
|
before: arguments[:before],
|
89
|
+
edge_class: edge_class_for_field(field),
|
89
90
|
)
|
90
91
|
end
|
91
92
|
|
93
|
+
# use an override if there is one
|
94
|
+
# @api private
|
95
|
+
def edge_class_for_field(field)
|
96
|
+
conn_type = field.type.unwrap
|
97
|
+
conn_type_edge_type = conn_type.respond_to?(:edge_class) && conn_type.edge_class
|
98
|
+
if conn_type_edge_type && conn_type_edge_type != Relay::Edge
|
99
|
+
conn_type_edge_type
|
100
|
+
else
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
end
|
92
104
|
protected
|
93
105
|
|
94
106
|
attr_reader :wrappers
|
data/lib/graphql/schema.rb
CHANGED
@@ -127,7 +127,7 @@ module GraphQL
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered
|
130
|
+
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}.
|
131
131
|
def lazy_method_name(obj)
|
132
132
|
lazy_methods.get(obj)
|
133
133
|
end
|
@@ -1534,9 +1534,9 @@ module GraphQL
|
|
1534
1534
|
|
1535
1535
|
# Add several directives at once
|
1536
1536
|
# @param new_directives [Class]
|
1537
|
-
def directives(new_directives
|
1538
|
-
if new_directives
|
1539
|
-
new_directives.each { |d| directive(d) }
|
1537
|
+
def directives(*new_directives)
|
1538
|
+
if new_directives.any?
|
1539
|
+
new_directives.flatten.each { |d| directive(d) }
|
1540
1540
|
end
|
1541
1541
|
|
1542
1542
|
find_inherited_value(:directives, default_directives).merge(own_directives)
|
@@ -1550,11 +1550,11 @@ module GraphQL
|
|
1550
1550
|
end
|
1551
1551
|
|
1552
1552
|
def default_directives
|
1553
|
-
{
|
1553
|
+
@default_directives ||= {
|
1554
1554
|
"include" => GraphQL::Schema::Directive::Include,
|
1555
1555
|
"skip" => GraphQL::Schema::Directive::Skip,
|
1556
1556
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
1557
|
-
}
|
1557
|
+
}.freeze
|
1558
1558
|
end
|
1559
1559
|
|
1560
1560
|
def tracer(new_tracer)
|
@@ -1783,16 +1783,7 @@ module GraphQL
|
|
1783
1783
|
if owner.kind.union?
|
1784
1784
|
# It's a union with possible_types
|
1785
1785
|
# Replace the item by class name
|
1786
|
-
owner.
|
1787
|
-
possible_type = tm.object_type
|
1788
|
-
if possible_type.is_a?(String) && (possible_type == type.name)
|
1789
|
-
# This is a match of Ruby class names, not graphql names,
|
1790
|
-
# since strings are used to refer to constants.
|
1791
|
-
tm.object_type = type
|
1792
|
-
elsif possible_type.is_a?(LateBoundType) && possible_type.graphql_name == type.graphql_name
|
1793
|
-
tm.object_type = type
|
1794
|
-
end
|
1795
|
-
}
|
1786
|
+
owner.assign_type_membership_object_type(type)
|
1796
1787
|
own_possible_types[owner.graphql_name] = owner.possible_types
|
1797
1788
|
elsif type.kind.interface? && owner.kind.object?
|
1798
1789
|
new_interfaces = []
|
@@ -61,6 +61,11 @@ module GraphQL
|
|
61
61
|
@from_resolver = from_resolver
|
62
62
|
@method_access = method_access
|
63
63
|
|
64
|
+
if !@null && default_value?
|
65
|
+
raise ArgumentError, "Argument '#{@name}' has conflicting params, " \
|
66
|
+
"either use `required: false` or remove `default_value:`."
|
67
|
+
end
|
68
|
+
|
64
69
|
if definition_block
|
65
70
|
if definition_block.arity == 1
|
66
71
|
instance_exec(self, &definition_block)
|
@@ -41,6 +41,7 @@ module GraphQL
|
|
41
41
|
|
42
42
|
def initialize(graphql_name, desc = nil, owner:, ast_node: nil, description: nil, value: nil, deprecation_reason: nil, &block)
|
43
43
|
@graphql_name = graphql_name.to_s
|
44
|
+
GraphQL::NameValidator.validate!(@graphql_name)
|
44
45
|
@description = desc || description
|
45
46
|
@value = value.nil? ? @graphql_name : value
|
46
47
|
@deprecation_reason = deprecation_reason
|
@@ -39,6 +39,9 @@ module GraphQL
|
|
39
39
|
if field.has_max_page_size? && !value.has_max_page_size_override?
|
40
40
|
value.max_page_size = field.max_page_size
|
41
41
|
end
|
42
|
+
if (custom_t = context.schema.connections.edge_class_for_field(@field))
|
43
|
+
value.edge_class = custom_t
|
44
|
+
end
|
42
45
|
value
|
43
46
|
elsif context.schema.new_connections?
|
44
47
|
wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
|
@@ -166,10 +166,7 @@ module GraphQL
|
|
166
166
|
return result
|
167
167
|
end
|
168
168
|
|
169
|
-
|
170
|
-
# using these methods to make sure that the object will
|
171
|
-
# behave like a hash below, when we call `each` on it.
|
172
|
-
begin
|
169
|
+
input = begin
|
173
170
|
input.to_h
|
174
171
|
rescue
|
175
172
|
begin
|
@@ -182,21 +179,25 @@ module GraphQL
|
|
182
179
|
end
|
183
180
|
end
|
184
181
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
if visible_arguments_map[name].nil?
|
190
|
-
result.add_problem("Field is not defined on #{self.graphql_name}", [name])
|
182
|
+
# Inject missing required arguments
|
183
|
+
missing_required_inputs = self.arguments.reduce({}) do |m, (argument_name, argument)|
|
184
|
+
if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
|
185
|
+
m[argument_name] = nil
|
191
186
|
end
|
187
|
+
|
188
|
+
m
|
192
189
|
end
|
193
190
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
result.
|
191
|
+
input.merge(missing_required_inputs).each do |argument_name, value|
|
192
|
+
argument = warden.get_argument(self, argument_name)
|
193
|
+
# Items in the input that are unexpected
|
194
|
+
unless argument
|
195
|
+
result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
|
196
|
+
next
|
199
197
|
end
|
198
|
+
# Items in the input that are expected, but have invalid values
|
199
|
+
argument_result = argument.type.validate_input(value, ctx)
|
200
|
+
result.merge_result!(argument_name, argument_result) unless argument_result.valid?
|
200
201
|
end
|
201
202
|
|
202
203
|
result
|
@@ -25,8 +25,15 @@ module GraphQL
|
|
25
25
|
types[type["name"]] = type_object
|
26
26
|
end
|
27
27
|
|
28
|
+
directives = []
|
29
|
+
schema.fetch("directives", []).each do |directive|
|
30
|
+
next if GraphQL::Schema.default_directives.include?(directive.fetch("name"))
|
31
|
+
directives << define_directive(directive, type_resolver)
|
32
|
+
end
|
33
|
+
|
28
34
|
Class.new(GraphQL::Schema) do
|
29
35
|
orphan_types(types.values)
|
36
|
+
directives(directives)
|
30
37
|
|
31
38
|
def self.resolve_type(*)
|
32
39
|
raise(GraphQL::RequiredImplementationMissingError, "This schema was loaded from string, so it can't resolve types for objects")
|
@@ -98,7 +105,7 @@ module GraphQL
|
|
98
105
|
value(
|
99
106
|
enum_value["name"],
|
100
107
|
description: enum_value["description"],
|
101
|
-
deprecation_reason: enum_value["
|
108
|
+
deprecation_reason: enum_value["deprecationReason"],
|
102
109
|
)
|
103
110
|
end
|
104
111
|
end
|
@@ -147,6 +154,16 @@ module GraphQL
|
|
147
154
|
end
|
148
155
|
end
|
149
156
|
|
157
|
+
def define_directive(directive, type_resolver)
|
158
|
+
loader = self
|
159
|
+
Class.new(GraphQL::Schema::Directive) do
|
160
|
+
graphql_name(directive["name"])
|
161
|
+
description(directive["description"])
|
162
|
+
locations(*directive["locations"].map(&:to_sym))
|
163
|
+
loader.build_arguments(self, directive["args"], type_resolver)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
150
167
|
public
|
151
168
|
|
152
169
|
def build_fields(type_defn, fields, type_resolver)
|
@@ -156,6 +173,7 @@ module GraphQL
|
|
156
173
|
field_hash["name"],
|
157
174
|
type: type_resolver.call(field_hash["type"]),
|
158
175
|
description: field_hash["description"],
|
176
|
+
deprecation_reason: field_hash["deprecationReason"],
|
159
177
|
null: true,
|
160
178
|
camelize: false,
|
161
179
|
) do
|
@@ -58,6 +58,22 @@ module GraphQL
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
# @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
|
62
|
+
def get_argument(argument_name)
|
63
|
+
a = own_arguments[argument_name]
|
64
|
+
|
65
|
+
if a || !self.is_a?(Class)
|
66
|
+
a
|
67
|
+
else
|
68
|
+
for ancestor in ancestors
|
69
|
+
if ancestor.respond_to?(:own_arguments) && a = ancestor.own_arguments[argument_name]
|
70
|
+
return a
|
71
|
+
end
|
72
|
+
end
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
61
77
|
# @param new_arg_class [Class] A class to use for building argument definitions
|
62
78
|
def argument_class(new_arg_class = nil)
|
63
79
|
self.class.argument_class(new_arg_class)
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -14,6 +14,7 @@ module GraphQL
|
|
14
14
|
def possible_types(*types, context: GraphQL::Query::NullContext, **options)
|
15
15
|
if types.any?
|
16
16
|
types.each do |t|
|
17
|
+
assert_valid_union_member(t)
|
17
18
|
type_memberships << type_membership_class.new(self, t, **options)
|
18
19
|
end
|
19
20
|
else
|
@@ -55,6 +56,34 @@ module GraphQL
|
|
55
56
|
def type_memberships
|
56
57
|
@type_memberships ||= []
|
57
58
|
end
|
59
|
+
|
60
|
+
# Update a type membership whose `.object_type` is a string or late-bound type
|
61
|
+
# so that the type membership's `.object_type` is the given `object_type`.
|
62
|
+
# (This is used for updating the union after the schema as lazily loaded the union member.)
|
63
|
+
# @api private
|
64
|
+
def assign_type_membership_object_type(object_type)
|
65
|
+
assert_valid_union_member(object_type)
|
66
|
+
type_memberships.each { |tm|
|
67
|
+
possible_type = tm.object_type
|
68
|
+
if possible_type.is_a?(String) && (possible_type == object_type.name)
|
69
|
+
# This is a match of Ruby class names, not graphql names,
|
70
|
+
# since strings are used to refer to constants.
|
71
|
+
tm.object_type = object_type
|
72
|
+
elsif possible_type.is_a?(LateBoundType) && possible_type.graphql_name == object_type.graphql_name
|
73
|
+
tm.object_type = object_type
|
74
|
+
end
|
75
|
+
}
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def assert_valid_union_member(type_defn)
|
82
|
+
if type_defn.is_a?(Module) && !type_defn.is_a?(Class)
|
83
|
+
# it's an interface type, defined as a module
|
84
|
+
raise ArgumentError, "Union possible_types can only be object types (not interface types), remove #{type_defn.graphql_name} (#{type_defn.inspect})"
|
85
|
+
end
|
86
|
+
end
|
58
87
|
end
|
59
88
|
end
|
60
89
|
end
|
@@ -105,6 +105,12 @@ module GraphQL
|
|
105
105
|
@visible_parent_fields[parent_type][field_name]
|
106
106
|
end
|
107
107
|
|
108
|
+
# @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible
|
109
|
+
def get_argument(parent_type, argument_name)
|
110
|
+
argument = parent_type.get_argument(argument_name)
|
111
|
+
return argument if argument && visible_argument?(argument)
|
112
|
+
end
|
113
|
+
|
108
114
|
# @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
|
109
115
|
def possible_types(type_defn)
|
110
116
|
@visible_possible_types ||= read_through { |type_defn|
|
@@ -95,16 +95,17 @@ module GraphQL
|
|
95
95
|
def required_input_fields_are_present(type, ast_node)
|
96
96
|
# TODO - would be nice to use these to create an error message so the caller knows
|
97
97
|
# that required fields are missing
|
98
|
-
required_field_names =
|
99
|
-
.select { |
|
98
|
+
required_field_names = type.arguments.each_value
|
99
|
+
.select { |argument| argument.type.kind.non_null? && @warden.get_argument(type, argument.name) }
|
100
100
|
.map(&:name)
|
101
|
+
|
101
102
|
present_field_names = ast_node.arguments.map(&:name)
|
102
103
|
missing_required_field_names = required_field_names - present_field_names
|
103
104
|
if @context.schema.error_bubbling
|
104
105
|
missing_required_field_names.empty? ? @valid_response : @invalid_response
|
105
106
|
else
|
106
107
|
results = missing_required_field_names.map do |name|
|
107
|
-
arg_type = @warden.
|
108
|
+
arg_type = @warden.get_argument(type, name).type
|
108
109
|
recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
|
109
110
|
end
|
110
111
|
merge_results(results)
|
@@ -112,13 +113,12 @@ module GraphQL
|
|
112
113
|
end
|
113
114
|
|
114
115
|
def present_input_field_values_are_valid(type, ast_node)
|
115
|
-
field_map = @warden.arguments(type).reduce({}) { |m, f| m[f.name] = f; m}
|
116
116
|
results = ast_node.arguments.map do |value|
|
117
|
-
field =
|
117
|
+
field = @warden.get_argument(type, value.name)
|
118
118
|
# we want to call validate on an argument even if it's an invalid one
|
119
119
|
# so that our raise exception is on it instead of the entire InputObject
|
120
|
-
|
121
|
-
recursively_validate(value.value,
|
120
|
+
field_type = field && field.type
|
121
|
+
recursively_validate(value.value, field_type)
|
122
122
|
end
|
123
123
|
merge_results(results)
|
124
124
|
end
|
@@ -5,7 +5,7 @@ module GraphQL
|
|
5
5
|
def on_argument(node, parent)
|
6
6
|
parent_defn = parent_definition(parent)
|
7
7
|
|
8
|
-
if parent_defn && context.warden.
|
8
|
+
if parent_defn && context.warden.get_argument(parent_defn, node.name)
|
9
9
|
super
|
10
10
|
elsif parent_defn
|
11
11
|
kind_of_node = node_type(parent)
|
@@ -17,8 +17,8 @@ module GraphQL
|
|
17
17
|
|
18
18
|
def assert_required_args(ast_node, defn)
|
19
19
|
present_argument_names = ast_node.arguments.map(&:name)
|
20
|
-
required_argument_names =
|
21
|
-
.select { |a| a.type.kind.non_null? && !a.default_value? }
|
20
|
+
required_argument_names = defn.arguments.each_value
|
21
|
+
.select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
|
22
22
|
.map(&:name)
|
23
23
|
|
24
24
|
missing_names = required_argument_names - present_argument_names
|
@@ -26,8 +26,7 @@ module GraphQL
|
|
26
26
|
context.field_definition
|
27
27
|
end
|
28
28
|
|
29
|
-
parent_type = context.warden.
|
30
|
-
.find{|f| f.name == parent_name(parent, defn) }
|
29
|
+
parent_type = context.warden.get_argument(defn, parent_name(parent, defn))
|
31
30
|
parent_type ? parent_type.type.unwrap : nil
|
32
31
|
end
|
33
32
|
|
@@ -126,8 +126,9 @@ module GraphQL
|
|
126
126
|
node_variables
|
127
127
|
.select { |name, usage| usage.declared? && !usage.used? }
|
128
128
|
.each { |var_name, usage|
|
129
|
+
declared_by_error_name = usage.declared_by.name || "anonymous #{usage.declared_by.operation_type}"
|
129
130
|
add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new(
|
130
|
-
"Variable $#{var_name} is declared by #{
|
131
|
+
"Variable $#{var_name} is declared by #{declared_by_error_name} but not used",
|
131
132
|
nodes: usage.declared_by,
|
132
133
|
path: usage.path,
|
133
134
|
name: var_name,
|
@@ -139,8 +140,9 @@ module GraphQL
|
|
139
140
|
node_variables
|
140
141
|
.select { |name, usage| usage.used? && !usage.declared? }
|
141
142
|
.each { |var_name, usage|
|
143
|
+
used_by_error_name = usage.used_by.name || "anonymous #{usage.used_by.operation_type}"
|
142
144
|
add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new(
|
143
|
-
"Variable $#{var_name} is used by #{
|
145
|
+
"Variable $#{var_name} is used by #{used_by_error_name} but not declared",
|
144
146
|
nodes: usage.ast_node,
|
145
147
|
path: usage.path,
|
146
148
|
name: var_name,
|
@@ -165,24 +165,35 @@ module GraphQL
|
|
165
165
|
# Return the query from "storage" (in memory)
|
166
166
|
def read_subscription(subscription_id)
|
167
167
|
query = @subscriptions[subscription_id]
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
168
|
+
if query.nil?
|
169
|
+
# This can happen when a subscription is triggered from an unsubscribed channel,
|
170
|
+
# see https://github.com/rmosolgo/graphql-ruby/issues/2478.
|
171
|
+
# (This `nil` is handled by `#execute_update`)
|
172
|
+
nil
|
173
|
+
else
|
174
|
+
{
|
175
|
+
query_string: query.query_string,
|
176
|
+
variables: query.provided_variables,
|
177
|
+
context: query.context.to_h,
|
178
|
+
operation_name: query.operation_name,
|
179
|
+
}
|
180
|
+
end
|
174
181
|
end
|
175
182
|
|
176
183
|
# The channel was closed, forget about it.
|
177
184
|
def delete_subscription(subscription_id)
|
178
185
|
query = @subscriptions.delete(subscription_id)
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
ev_by_fingerprint
|
186
|
+
# This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
|
187
|
+
# see https://github.com/rmosolgo/graphql-ruby/issues/2478
|
188
|
+
if query
|
189
|
+
events = query.context.namespace(:subscriptions)[:events]
|
190
|
+
events.each do |event|
|
191
|
+
ev_by_fingerprint = @events[event.topic]
|
192
|
+
ev_for_fingerprint = ev_by_fingerprint[event.fingerprint]
|
193
|
+
ev_for_fingerprint.delete(event)
|
194
|
+
if ev_for_fingerprint.empty?
|
195
|
+
ev_by_fingerprint.delete(event.fingerprint)
|
196
|
+
end
|
186
197
|
end
|
187
198
|
end
|
188
199
|
end
|
@@ -9,6 +9,9 @@ module GraphQL
|
|
9
9
|
GLOBALID_KEY = "__gid__"
|
10
10
|
SYMBOL_KEY = "__sym__"
|
11
11
|
SYMBOL_KEYS_KEY = "__sym_keys__"
|
12
|
+
TIMESTAMP_KEY = "__timestamp__"
|
13
|
+
TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%Z" # eg '2020-01-01 23:59:59.123456789+05:00'
|
14
|
+
OPEN_STRUCT_KEY = "__ostruct__"
|
12
15
|
|
13
16
|
module_function
|
14
17
|
|
@@ -55,10 +58,20 @@ module GraphQL
|
|
55
58
|
if value.is_a?(Array)
|
56
59
|
value.map{|item| load_value(item)}
|
57
60
|
elsif value.is_a?(Hash)
|
58
|
-
if value.size == 1
|
59
|
-
|
60
|
-
|
61
|
-
|
61
|
+
if value.size == 1
|
62
|
+
case value.keys.first # there's only 1 key
|
63
|
+
when GLOBALID_KEY
|
64
|
+
GlobalID::Locator.locate(value[GLOBALID_KEY])
|
65
|
+
when SYMBOL_KEY
|
66
|
+
value[SYMBOL_KEY].to_sym
|
67
|
+
when TIMESTAMP_KEY
|
68
|
+
timestamp_class_name, timestamp_s = value[TIMESTAMP_KEY]
|
69
|
+
timestamp_class = Object.const_get(timestamp_class_name)
|
70
|
+
timestamp_class.strptime(timestamp_s, TIMESTAMP_FORMAT)
|
71
|
+
when OPEN_STRUCT_KEY
|
72
|
+
ostruct_values = load_value(value[OPEN_STRUCT_KEY])
|
73
|
+
OpenStruct.new(ostruct_values)
|
74
|
+
end
|
62
75
|
else
|
63
76
|
loaded_h = {}
|
64
77
|
sym_keys = value.fetch(SYMBOL_KEYS_KEY, [])
|
@@ -101,6 +114,11 @@ module GraphQL
|
|
101
114
|
{ SYMBOL_KEY => obj.to_s }
|
102
115
|
elsif obj.respond_to?(:to_gid_param)
|
103
116
|
{GLOBALID_KEY => obj.to_gid_param}
|
117
|
+
elsif obj.is_a?(Date) || obj.is_a?(Time)
|
118
|
+
# DateTime extends Date; for TimeWithZone, call `.utc` first.
|
119
|
+
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
120
|
+
elsif obj.is_a?(OpenStruct)
|
121
|
+
{ OPEN_STRUCT_KEY => dump_value(obj.to_h) }
|
104
122
|
else
|
105
123
|
obj
|
106
124
|
end
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.11.
|
4
|
+
version: 1.11.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|