Skip to content

Commit 212eaf8

Browse files
committed
Properly validate SDL directive arguments
1 parent fb9d48c commit 212eaf8

File tree

3 files changed

+53
-4
lines changed

3 files changed

+53
-4
lines changed

lib/graphql/schema/directive.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ def inherited(subclass)
111111
# @return [GraphQL::Interpreter::Arguments]
112112
attr_reader :arguments
113113

114+
class InvalidArgumentError < GraphQL::Error
115+
end
116+
114117
def initialize(owner, **arguments)
115118
@owner = owner
116119
assert_valid_owner
@@ -119,6 +122,14 @@ def initialize(owner, **arguments)
119122
# - lazy resolution
120123
# Probably, those won't be needed here, since these are configuration arguments,
121124
# not runtime arguments.
125+
self.class.all_argument_definitions.each do |arg_defn|
126+
value = arguments[arg_defn.keyword]
127+
arg_type = arg_defn.type
128+
result = arg_defn.type.validate_input(value, Query::NullContext.instance)
129+
if !result.valid?
130+
raise InvalidArgumentError, "@#{graphql_name}.#{arg_defn.graphql_name} on #{owner.path} is invalid (#{value.inspect}): #{result.problems.first["explanation"]}"
131+
end
132+
end
122133
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext.instance)
123134
end
124135

lib/graphql/schema/input_object.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ def validate_non_null_input(input, ctx, max_errors: nil)
182182

183183
input.each do |argument_name, value|
184184
argument = types.argument(self, argument_name)
185+
if argument.nil? && ctx.is_a?(Query::NullContext) && argument_name.is_a?(Symbol)
186+
# Validating definition directive arguments which come in as Symbols
187+
argument = types.arguments(self).find { |arg| arg.keyword == argument_name }
188+
end
185189
# Items in the input that are unexpected
186190
if argument.nil?
187191
result ||= Query::InputValidationResult.new

spec/graphql/schema/directive_spec.rb

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class Thing < GraphQL::Schema::Object
5555
end
5656

5757
it "validates arguments" do
58-
err = assert_raises ArgumentError do
58+
err = assert_raises GraphQL::Schema::Directive::InvalidArgumentError do
5959
GraphQL::Schema::Field.from_options(
6060
name: :something,
6161
type: String,
@@ -65,9 +65,9 @@ class Thing < GraphQL::Schema::Object
6565
)
6666
end
6767

68-
assert_equal "@secret.topSecret is required, but no value was given", err.message
68+
assert_equal "@secret.topSecret on Thing.something is invalid (nil): Expected value to not be null", err.message
6969

70-
err2 = assert_raises ArgumentError do
70+
err2 = assert_raises GraphQL::Schema::Directive::InvalidArgumentError do
7171
GraphQL::Schema::Field.from_options(
7272
name: :something,
7373
type: String,
@@ -77,7 +77,7 @@ class Thing < GraphQL::Schema::Object
7777
)
7878
end
7979

80-
assert_equal "@secret.topSecret is required, but no value was given", err2.message
80+
assert_equal "@secret.topSecret on Thing.something is invalid (12.5): Could not coerce value 12.5 to Boolean", err2.message
8181
end
8282

8383
describe 'repeatable directives' do
@@ -400,4 +400,38 @@ def numbers
400400
enum_value = schema.get_type("Stuff").values["THING"]
401401
assert_equal [["tag", { name: "t7"}], ["tag", { name: "t8"}]], enum_value.directives.map { |dir| [dir.graphql_name, dir.arguments.to_h] }
402402
end
403+
404+
describe "Validating schema directives" do
405+
def build_sdl(size:)
406+
<<~GRAPHQL
407+
directive @tshirt(size: Size!) on INTERFACE | OBJECT
408+
409+
type MyType @tshirt(size: #{size}) {
410+
color: String
411+
}
412+
413+
type Query {
414+
myType: MyType
415+
}
416+
417+
enum Size {
418+
LARGE
419+
MEDIUM
420+
SMALL
421+
}
422+
GRAPHQL
423+
end
424+
425+
it "Raises a nice error for invalid enum values" do
426+
valid_sdl = build_sdl(size: "MEDIUM")
427+
assert_equal valid_sdl, GraphQL::Schema.from_definition(valid_sdl).to_definition
428+
429+
typo_sdl = build_sdl(size: "BLAH")
430+
err = assert_raises GraphQL::Schema::Directive::InvalidArgumentError do
431+
GraphQL::Schema.from_definition(typo_sdl)
432+
end
433+
expected_msg = '@tshirt.size on MyType is invalid ("BLAH"): Expected "BLAH" to be one of: LARGE, MEDIUM, SMALL'
434+
assert_equal expected_msg, err.message
435+
end
436+
end
403437
end

0 commit comments

Comments
 (0)