Skip to content

Commit f24d712

Browse files
committed
Properly handle lists of input objects as directive arguments
1 parent 87d0257 commit f24d712

File tree

2 files changed

+37
-4
lines changed

2 files changed

+37
-4
lines changed

lib/graphql/schema/directive.rb

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,22 @@ def initialize(owner, **arguments)
129129
# not runtime arguments.
130130
context = Query::NullContext.instance
131131
self.class.all_argument_definitions.each do |arg_defn|
132-
if arguments.key?(arg_defn.keyword)
133-
value = arguments[arg_defn.keyword]
132+
keyword = arg_defn.keyword
133+
arg_type = arg_defn.type
134+
if arguments.key?(keyword)
135+
value = arguments[keyword]
134136
# This is a Ruby-land value; convert it to graphql for validation
135137
graphql_value = begin
136-
arg_defn.type.unwrap.coerce_isolated_result(value)
138+
coerce_value = value
139+
if arg_type.list? && (!coerce_value.nil?) && (!coerce_value.is_a?(Array))
140+
# When validating inputs, GraphQL accepts a single item
141+
# and implicitly converts it to a one-item list.
142+
# However, we're using result coercion here to go from Ruby value
143+
# to GraphQL value, so it doesn't have that feature.
144+
# Keep the GraphQL-type behavior but implement it manually:
145+
coerce_value = [coerce_value]
146+
end
147+
arg_type.coerce_isolated_result(coerce_value)
137148
rescue GraphQL::Schema::Enum::UnresolvedValueError
138149
# Let validation handle this
139150
value
@@ -142,7 +153,7 @@ def initialize(owner, **arguments)
142153
value = graphql_value = nil
143154
end
144155

145-
result = arg_defn.type.validate_input(graphql_value, context)
156+
result = arg_type.validate_input(graphql_value, context)
146157
if !result.valid?
147158
raise InvalidArgumentError, "@#{graphql_name}.#{arg_defn.graphql_name} on #{owner.path} is invalid (#{value.inspect}): #{result.problems.first["explanation"]}"
148159
end

spec/graphql/schema/directive_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ class MultiWord < GraphQL::Schema::Directive
1212
module DirectiveTest
1313
class Secret < GraphQL::Schema::Directive
1414
argument :top_secret, Boolean
15+
class PermissionRule < GraphQL::Schema::InputObject
16+
class Permission < GraphQL::Schema::Enum
17+
value :READ
18+
value :WRITE
19+
end
20+
argument :team, String
21+
argument :permission, Permission
22+
end
23+
argument :permission_rules, [PermissionRule], required: false
1524
locations(FIELD_DEFINITION, ARGUMENT_DEFINITION)
1625
end
1726

@@ -22,6 +31,13 @@ class Thing < GraphQL::Schema::Object
2231
directive Secret, top_secret: false
2332
end
2433
end
34+
35+
field :other_info, String do
36+
directive Secret, top_secret: false, permission_rules: [
37+
{ team: "admins", permission: "WRITE" },
38+
{ team: "others", permission: "READ"},
39+
]
40+
end
2541
end
2642
end
2743

@@ -36,6 +52,12 @@ class Thing < GraphQL::Schema::Object
3652
assert_equal [DirectiveTest::Secret], argument.directives.map(&:class)
3753
assert_equal [argument], argument.directives.map(&:owner)
3854
assert_equal [false], argument.directives.map{ |d| d.arguments[:top_secret] }
55+
56+
other_field = DirectiveTest::Thing.fields.values.last
57+
other_field_dir = other_field.directives.first
58+
perm_roles = other_field_dir.arguments[:permission_rules]
59+
assert_equal Array.new(2, DirectiveTest::Secret::PermissionRule), perm_roles.map(&:class)
60+
assert_equal ["WRITE", "READ"], perm_roles.map(&:permission)
3961
end
4062

4163
it "raises an error when added to the wrong thing" do

0 commit comments

Comments
 (0)