Skip to content

Commit 982a750

Browse files
authored
Merge pull request #2416 from modosc/2356-required-default-values
Support required arguments with default values [fixes #2356]
2 parents c936d37 + a39a3a1 commit 982a750

File tree

4 files changed

+47
-17
lines changed

4 files changed

+47
-17
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: 42 additions & 8 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 }
@@ -30,7 +30,11 @@ def call(val, context)
3030
end
3131

3232
def field(**args)
33-
args.inspect
33+
# sort the fields so that they match the output of the new interpreter
34+
sorted_keys = args.keys.sort
35+
sorted_args = {}
36+
sorted_keys.each {|k| sorted_args[k] = args[k] }
37+
sorted_args.inspect
3438
end
3539

3640
def multiply(val)
@@ -48,7 +52,7 @@ class Schema < GraphQL::Schema
4852

4953
describe "#keys" do
5054
it "is not overwritten by the 'keys' argument" do
51-
expected_keys = ["aliasedArg", "arg", "argWithBlock", "explodingPreparedArg", "keys", "preparedArg", "preparedByCallableArg", "preparedByProcArg"]
55+
expected_keys = ["aliasedArg", "arg", "argWithBlock", "explodingPreparedArg", "keys", "preparedArg", "preparedByCallableArg", "preparedByProcArg", "requiredWithDefaultArg"]
5256
assert_equal expected_keys, SchemaArgumentTest::Query.fields["field"].arguments.keys.sort
5357
end
5458
end
@@ -103,7 +107,7 @@ class Schema < GraphQL::Schema
103107

104108
res = SchemaArgumentTest::Schema.execute(query_str)
105109
# Make sure it's getting the renamed symbol:
106-
assert_equal '{:renamed=>"x"}', res["data"]["field"]
110+
assert_equal '{:renamed=>"x", :required_with_default_arg=>1}', res["data"]["field"]
107111
end
108112
end
109113

@@ -115,7 +119,7 @@ class Schema < GraphQL::Schema
115119

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

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

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

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

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

141145
it "handles exceptions raised by prepare" do
@@ -144,9 +148,39 @@ class Schema < GraphQL::Schema
144148
GRAPHQL
145149

146150
res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
147-
assert_equal({ 'f1' => '{:arg=>"echo"}', 'f2' => nil }, res['data'])
151+
assert_equal({ 'f1' => '{:arg=>"echo", :required_with_default_arg=>1}', 'f2' => nil }, res['data'])
148152
assert_equal(res['errors'][0]['message'], 'boom!')
149153
assert_equal(res['errors'][0]['path'], ['f2'])
150154
end
151155
end
156+
157+
describe "default_value:" do
158+
it 'uses default_value: with no input' do
159+
query_str = <<-GRAPHQL
160+
{ field() }
161+
GRAPHQL
162+
163+
res = SchemaArgumentTest::Schema.execute(query_str)
164+
assert_equal '{:required_with_default_arg=>1}', res["data"]["field"]
165+
end
166+
167+
it 'uses provided input value' do
168+
query_str = <<-GRAPHQL
169+
{ field(requiredWithDefaultArg: 2) }
170+
GRAPHQL
171+
172+
res = SchemaArgumentTest::Schema.execute(query_str)
173+
assert_equal '{:required_with_default_arg=>2}', res["data"]["field"]
174+
end
175+
176+
it 'respects non-null type' do
177+
query_str = <<-GRAPHQL
178+
{ field(requiredWithDefaultArg: null) }
179+
GRAPHQL
180+
181+
res = SchemaArgumentTest::Schema.execute(query_str)
182+
assert_equal "Argument 'requiredWithDefaultArg' on Field 'field' has an invalid value. Expected type 'Int!'.", res['errors'][0]['message']
183+
end
184+
185+
end
152186
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)