Skip to content

Commit cde6204

Browse files
committed
Support required arguments with default values [fixes #2356]
1 parent 356d9d3 commit cde6204

File tree

4 files changed

+42
-16
lines changed

4 files changed

+42
-16
lines changed

lib/graphql/schema/validation.rb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,6 @@ def self.assert_named_items_are_valid(item_name, get_items_proc)
104104
ARGUMENTS_ARE_VALID = Rules.assert_named_items_are_valid("argument", ->(type) { type.arguments.values })
105105

106106
DEFAULT_VALUE_IS_VALID_FOR_TYPE = ->(type) {
107-
if !type.default_value.nil? && type.type.is_a?(NonNullType)
108-
return %Q(Variable #{type.name} of type "#{type.type}" is required and will not use the default value. Perhaps you meant to use type "#{type.type.of_type}".)
109-
end
110-
111107
if !type.default_value.nil?
112108
coerced_value = begin
113109
type.type.coerce_isolated_result(type.default_value)

lib/graphql/static_validation/rules/required_arguments_are_present.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def on_directive(node, _parent)
1818
def assert_required_args(ast_node, defn)
1919
present_argument_names = ast_node.arguments.map(&:name)
2020
required_argument_names = context.warden.arguments(defn)
21-
.select { |a| a.type.kind.non_null? }
21+
.select { |a| a.type.kind.non_null? && !a.default_value? }
2222
.map(&:name)
2323

2424
missing_names = required_argument_names - present_argument_names

spec/graphql/schema/argument_spec.rb

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class Query < GraphQL::Schema::Object
1010
argument :arg_with_block, String, required: false do
1111
description "test"
1212
end
13-
13+
argument :required_with_default_arg, Int, required: true, default_value: 1
1414
argument :aliased_arg, String, required: false, as: :renamed
1515
argument :prepared_arg, Int, required: false, prepare: :multiply
1616
argument :prepared_by_proc_arg, Int, required: false, prepare: ->(val, context) { context[:multiply_by] * val }
@@ -48,7 +48,7 @@ class Schema < GraphQL::Schema
4848

4949
describe "#keys" do
5050
it "is not overwritten by the 'keys' argument" do
51-
expected_keys = ["aliasedArg", "arg", "argWithBlock", "explodingPreparedArg", "keys", "preparedArg", "preparedByCallableArg", "preparedByProcArg"]
51+
expected_keys = ["aliasedArg", "arg", "argWithBlock", "explodingPreparedArg", "keys", "preparedArg", "preparedByCallableArg", "preparedByProcArg", "requiredWithDefaultArg"]
5252
assert_equal expected_keys, SchemaArgumentTest::Query.fields["field"].arguments.keys.sort
5353
end
5454
end
@@ -103,7 +103,7 @@ class Schema < GraphQL::Schema
103103

104104
res = SchemaArgumentTest::Schema.execute(query_str)
105105
# Make sure it's getting the renamed symbol:
106-
assert_equal '{:renamed=>"x"}', res["data"]["field"]
106+
assert_equal '{:required_with_default_arg=>1, :renamed=>"x"}', res["data"]["field"]
107107
end
108108
end
109109

@@ -115,7 +115,7 @@ class Schema < GraphQL::Schema
115115

116116
res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
117117
# Make sure it's getting the renamed symbol:
118-
assert_equal '{:prepared_arg=>15}', res["data"]["field"]
118+
assert_equal '{:required_with_default_arg=>1, :prepared_arg=>15}', res["data"]["field"]
119119
end
120120

121121
it "calls the method on the provided Proc" do
@@ -125,7 +125,7 @@ class Schema < GraphQL::Schema
125125

126126
res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
127127
# Make sure it's getting the renamed symbol:
128-
assert_equal '{:prepared_by_proc_arg=>15}', res["data"]["field"]
128+
assert_equal '{:required_with_default_arg=>1, :prepared_by_proc_arg=>15}', res["data"]["field"]
129129
end
130130

131131
it "calls the method on the provided callable object" do
@@ -135,7 +135,7 @@ class Schema < GraphQL::Schema
135135

136136
res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
137137
# Make sure it's getting the renamed symbol:
138-
assert_equal '{:prepared_by_callable_arg=>15}', res["data"]["field"]
138+
assert_equal '{:required_with_default_arg=>1, :prepared_by_callable_arg=>15}', res["data"]["field"]
139139
end
140140

141141
it "handles exceptions raised by prepare" do
@@ -144,9 +144,39 @@ class Schema < GraphQL::Schema
144144
GRAPHQL
145145

146146
res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
147-
assert_equal({ 'f1' => '{:arg=>"echo"}', 'f2' => nil }, res['data'])
147+
assert_equal({ 'f1' => '{:arg=>"echo", :required_with_default_arg=>1}', 'f2' => nil }, res['data'])
148148
assert_equal(res['errors'][0]['message'], 'boom!')
149149
assert_equal(res['errors'][0]['path'], ['f2'])
150150
end
151151
end
152+
153+
describe "default_value:" do
154+
it 'uses default_value: with no input' do
155+
query_str = <<-GRAPHQL
156+
{ field() }
157+
GRAPHQL
158+
159+
res = SchemaArgumentTest::Schema.execute(query_str)
160+
assert_equal '{:required_with_default_arg=>1}', res["data"]["field"]
161+
end
162+
163+
it 'uses provided input value' do
164+
query_str = <<-GRAPHQL
165+
{ field(requiredWithDefaultArg: 2) }
166+
GRAPHQL
167+
168+
res = SchemaArgumentTest::Schema.execute(query_str)
169+
assert_equal '{:required_with_default_arg=>2}', res["data"]["field"]
170+
end
171+
172+
it 'respects non-null type' do
173+
query_str = <<-GRAPHQL
174+
{ field(requiredWithDefaultArg: null) }
175+
GRAPHQL
176+
177+
res = SchemaArgumentTest::Schema.execute(query_str)
178+
assert_equal "Argument 'requiredWithDefaultArg' on Field 'field' has an invalid value. Expected type 'Int!'.", res['errors'][0]['message']
179+
end
180+
181+
end
152182
end

spec/graphql/schema/validation_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,9 @@ def assert_validation_warns(object, warning)
297297
end
298298
}
299299

300-
let(:invalid_default_argument_for_non_null_argument) {
300+
let(:default_argument_for_non_null_argument) {
301301
GraphQL::Argument.define do
302-
name "InvalidDefault"
302+
name "ValidDefault"
303303
type !GraphQL::INT_TYPE
304304
default_value 1
305305
end
@@ -324,8 +324,8 @@ def assert_validation_warns(object, warning)
324324
assert_error_includes untyped_argument, "must be a valid input type (Scalar or InputObject), not Symbol"
325325
end
326326

327-
it "does not allow default values for non-null argument" do
328-
assert_error_includes invalid_default_argument_for_non_null_argument, 'Variable InvalidDefault of type "Int!" is required and will not use the default value. Perhaps you meant to use type "Int".'
327+
it "allows default values for non-null argument" do
328+
assert_nil GraphQL::Schema::Validation.validate(default_argument_for_non_null_argument)
329329
end
330330

331331
it "cannot use reserved name" do

0 commit comments

Comments
 (0)