Skip to content

Commit bd4f4f5

Browse files
committed
fix(OneOf): count only known fields for one-of validation
Replicates graphql/graphql-js@c4f7d1b
1 parent 3c76ccf commit bd4f4f5

4 files changed

Lines changed: 58 additions & 35 deletions

File tree

src/graphql/utilities/validate_input_value.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
ListValueNode,
1111
Node,
1212
NullValueNode,
13+
ObjectFieldNode,
1314
ObjectValueNode,
1415
ValueNode,
1516
VariableNode,
@@ -139,7 +140,6 @@ def validate_input_value_impl(
139140
for field_name, field_value in input_value.items():
140141
if field_value is Undefined:
141142
continue
142-
fields.append(field_name)
143143
if field_name not in field_defs:
144144
suggestion = (
145145
""
@@ -154,6 +154,8 @@ def validate_input_value_impl(
154154
+ f": {inspect(input_value)}.",
155155
path,
156156
)
157+
continue
158+
fields.append(field_name)
157159

158160
if type_.is_one_of:
159161
if len(fields) != 1:
@@ -163,14 +165,15 @@ def validate_input_value_impl(
163165
path,
164166
)
165167

166-
field_name = fields[0]
167-
value = input_value[field_name]
168-
if value is None:
169-
report_invalid_value(
170-
on_error,
171-
get_one_of_input_object_error_message(type_),
172-
Path(path, field_name, type_.name),
173-
)
168+
if fields:
169+
field_name = fields[0]
170+
value = input_value[field_name]
171+
if value is None:
172+
report_invalid_value(
173+
on_error,
174+
get_one_of_input_object_error_message(type_),
175+
Path(path, field_name, type_.name),
176+
)
174177
else:
175178
assert_leaf_type(type_)
176179

@@ -393,6 +396,7 @@ def validate_input_literal_impl(
393396
)
394397

395398
fields = value_node.fields
399+
known_fields: list[ObjectFieldNode] = []
396400
# Ensure every provided field is defined.
397401
for field_node in fields:
398402
field_name = field_node.name.value
@@ -411,9 +415,11 @@ def validate_input_literal_impl(
411415
field_node,
412416
path,
413417
)
418+
else:
419+
known_fields.append(field_node)
414420

415421
if type_.is_one_of:
416-
is_not_exactly_one_field = len(fields) != 1
422+
is_not_exactly_one_field = len(known_fields) != 1
417423
if is_not_exactly_one_field:
418424
report_invalid_literal(
419425
context.on_error,
@@ -423,9 +429,9 @@ def validate_input_literal_impl(
423429
)
424430
return
425431

426-
field_value_node = fields[0].value
432+
field_value_node = known_fields[0].value
427433
if isinstance(field_value_node, NullValueNode):
428-
field_name = fields[0].name.value
434+
field_name = known_fields[0].name.value
429435
report_invalid_literal(
430436
context.on_error,
431437
get_one_of_input_object_error_message(type_),

tests/utilities/test_coerce_input_value.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ def coerces_input_objects_according_to_input_coercion_rules():
569569
_test("{ a: null }", test_one_of_input_obj, Undefined)
570570
_test("{ a: 1 }", test_one_of_input_obj, Undefined)
571571
_test('{ a: "abc", b: "def" }', test_one_of_input_obj, Undefined)
572+
_test('{ a: "abc", c: "def" }', test_one_of_input_obj, Undefined)
572573
_test("{}", test_one_of_input_obj, Undefined)
573574
_test('{ c: "abc" }', test_one_of_input_obj, Undefined)
574575

tests/utilities/test_validate_input_value.py

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -478,13 +478,6 @@ def returns_error_for_an_unknown_field():
478478
" found: {'foo': 123, 'unknownField': 123}.",
479479
"path": [],
480480
},
481-
{
482-
"error": "Within OneOf Input Object type"
483-
" 'TestInputObject', exactly one field must be"
484-
" specified, and the value for that field"
485-
" must be non-null.",
486-
"path": [],
487-
},
488481
],
489482
)
490483

@@ -498,7 +491,14 @@ def returns_error_for_a_misspelled_field():
498491
" not to include unknown field 'bart'."
499492
" Did you mean 'bar'? Found: {'bart': 123}.",
500493
"path": [],
501-
}
494+
},
495+
{
496+
"error": "Within OneOf Input Object type"
497+
" 'TestInputObject', exactly one field must be"
498+
" specified, and the value for that field"
499+
" must be non-null.",
500+
"path": [],
501+
},
502502
],
503503
)
504504

@@ -512,7 +512,14 @@ def returns_error_for_a_misspelled_field_no_suggestions():
512512
" not to include unknown field 'bart',"
513513
" found: {'bart': 123}.",
514514
"path": [],
515-
}
515+
},
516+
{
517+
"error": "Within OneOf Input Object type"
518+
" 'TestInputObject', exactly one field must be"
519+
" specified, and the value for that field"
520+
" must be non-null.",
521+
"path": [],
522+
},
516523
],
517524
True,
518525
)
@@ -1128,13 +1135,6 @@ def returns_error_for_an_unknown_field():
11281135
" found: { foo: 123, unknownField: 123 }.",
11291136
"path": [],
11301137
},
1131-
{
1132-
"error": "Within OneOf Input Object type"
1133-
" 'TestInputObject', exactly one field must be"
1134-
" specified, and the value for that field"
1135-
" must be non-null.",
1136-
"path": [],
1137-
},
11381138
],
11391139
)
11401140

@@ -1149,13 +1149,6 @@ def returns_error_for_a_misspelled_field():
11491149
" Did you mean 'bar'? Found: { foo: 123, bart: 123 }.",
11501150
"path": [],
11511151
},
1152-
{
1153-
"error": "Within OneOf Input Object type"
1154-
" 'TestInputObject', exactly one field must be"
1155-
" specified, and the value for that field"
1156-
" must be non-null.",
1157-
"path": [],
1158-
},
11591152
],
11601153
)
11611154

tests/validation/test_values_of_correct_type.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,29 @@ def more_than_one_field():
12771277
],
12781278
)
12791279

1280+
def unknown_field_does_not_add_a_one_of_error():
1281+
assert_errors(
1282+
"""
1283+
{
1284+
complicatedArgs {
1285+
oneOfArgField(oneOfArg: {
1286+
stringField: "abc",
1287+
invalidField: 123
1288+
})
1289+
}
1290+
}
1291+
""",
1292+
[
1293+
{
1294+
"message": "Expected value of type 'OneOfInput'"
1295+
" not to include unknown field 'invalidField'."
1296+
" Did you mean 'intField'?"
1297+
' Found: { stringField: "abc", invalidField: 123 }.',
1298+
"locations": [(6, 23)],
1299+
},
1300+
],
1301+
)
1302+
12801303
def describe_directive_arguments():
12811304
def with_directives_of_valid_types():
12821305
assert_valid(

0 commit comments

Comments
 (0)