From f84c3c5beb8145dc1ffaa01a460fbec23342fd3b Mon Sep 17 00:00:00 2001 From: Abhinand C Date: Thu, 2 Mar 2023 21:10:27 +0530 Subject: [PATCH 1/3] fix(List Input Coercion): Adds validation for nested lists with multiple values --- src/graphql/utilities/coerce_input_value.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/graphql/utilities/coerce_input_value.py b/src/graphql/utilities/coerce_input_value.py index 31237d1e..4d795f7b 100644 --- a/src/graphql/utilities/coerce_input_value.py +++ b/src/graphql/utilities/coerce_input_value.py @@ -70,7 +70,15 @@ def coerce_input_value( if is_iterable(input_value): coerced_list: List[Any] = [] append_item = coerced_list.append + is_nested_list = bool(is_list_type(item_type) and len(input_value) > 1) for index, item_value in enumerate(input_value): + if is_nested_list and not is_iterable(item_value): + # All input values should be iterable for multivalued nested list types + on_error( + path.as_list() if path else Path(path, index, None).as_list(), + item_value, + GraphQLError(f"Expected type '{inspect(item_type)}' to be a list."), + ) append_item( coerce_input_value( item_value, item_type, on_error, Path(path, index, None) From b335f5ffebbea02a5f21861f5815cbda27c0afcc Mon Sep 17 00:00:00 2001 From: Abhinand C Date: Fri, 3 Mar 2023 00:21:27 +0530 Subject: [PATCH 2/3] style: Fixed lint errors, and black formatting --- src/graphql/utilities/coerce_input_value.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/graphql/utilities/coerce_input_value.py b/src/graphql/utilities/coerce_input_value.py index 4d795f7b..6017769c 100644 --- a/src/graphql/utilities/coerce_input_value.py +++ b/src/graphql/utilities/coerce_input_value.py @@ -70,14 +70,18 @@ def coerce_input_value( if is_iterable(input_value): coerced_list: List[Any] = [] append_item = coerced_list.append - is_nested_list = bool(is_list_type(item_type) and len(input_value) > 1) + is_nested_list = bool( + is_list_type(item_type) and len(tuple(input_value)) > 1 + ) for index, item_value in enumerate(input_value): if is_nested_list and not is_iterable(item_value): - # All input values should be iterable for multivalued nested list types + # Input values should be iterable for multivalued nested list type on_error( path.as_list() if path else Path(path, index, None).as_list(), item_value, - GraphQLError(f"Expected type '{inspect(item_type)}' to be a list."), + GraphQLError( + f"Expected type '{inspect(item_type)}' to be a list." + ), ) append_item( coerce_input_value( From 5eb9e70777d3a4eed0717aa2075575c334490d8a Mon Sep 17 00:00:00 2001 From: Abhinand C Date: Fri, 3 Mar 2023 00:23:12 +0530 Subject: [PATCH 3/3] feat(Tests): Updates test to support proper nested list input coerce --- tests/utilities/test_coerce_input_value.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/utilities/test_coerce_input_value.py b/tests/utilities/test_coerce_input_value.py index f34e8564..5b0155df 100644 --- a/tests/utilities/test_coerce_input_value.py +++ b/tests/utilities/test_coerce_input_value.py @@ -352,13 +352,24 @@ def returns_null_for_a_null_value(): result = _coerce_value(None, TestNestedList) assert expect_value(result) is None - def returns_nested_list_for_nested_non_list_values(): + def returns_error_for_nested_non_list_values(): result = _coerce_value([1, 2, 3], TestNestedList) - assert expect_value(result) == [[1], [2], [3]] + assert expect_errors(result) == [ + ("Expected type '[Int]' to be a list.", [0], 1), + ("Expected type '[Int]' to be a list.", [1], 2), + ("Expected type '[Int]' to be a list.", [2], 3), + ] def returns_nested_null_for_nested_null_values(): + result = _coerce_value([[None], [None]], TestNestedList) + assert expect_value(result) == [[None], [None]] + + def returns_errors_for_null_values(): result = _coerce_value([42, [None], None], TestNestedList) - assert expect_value(result) == [[42], [None], None] + assert expect_errors(result) == [ + ("Expected type '[Int]' to be a list.", [0], 42), + ("Expected type '[Int]' to be a list.", [2], None), + ] def describe_with_default_on_error(): def throw_error_without_path():