diff --git a/graphene/experimental/decorators/mutation.py b/graphene/experimental/decorators/mutation.py index 140a0acb..92fe1b09 100644 --- a/graphene/experimental/decorators/mutation.py +++ b/graphene/experimental/decorators/mutation.py @@ -1,8 +1,10 @@ +import inspect from typing import List from graphene.types.field import Field from graphene.types.inputobjecttype import InputObjectType from graphene.types.scalars import Scalar +from graphene.types.utils import get_underlying_type from graphene.utils.str_converters import to_camel_case @@ -37,9 +39,11 @@ def mutation(return_type, arguments=None, **kwargs): invalid_arguments = [] for argument_name, argument in arguments.items(): - if not ( - isinstance(argument, Scalar) or isinstance(argument, InputObjectType) - ): + if inspect.isclass(argument): + type_ = argument + else: + type_ = get_underlying_type(argument.get_type()) + if not (issubclass(type_, Scalar) or issubclass(type_, InputObjectType)): invalid_arguments.append(argument_name) if len(invalid_arguments) > 0: diff --git a/graphene/experimental/decorators/tests/test_mutation.py b/graphene/experimental/decorators/tests/test_mutation.py index 8ca3b89c..c4e54559 100644 --- a/graphene/experimental/decorators/tests/test_mutation.py +++ b/graphene/experimental/decorators/tests/test_mutation.py @@ -2,7 +2,17 @@ from textwrap import dedent import pytest -from graphene import Boolean, Field, InputObjectType, ObjectType, Schema, String, Union +from graphene import ( + Boolean, + Field, + InputObjectType, + ObjectType, + Schema, + String, + Union, + List, + NonNull, +) from ..mutation import mutation, MutationInvalidArgumentsError @@ -251,6 +261,69 @@ def test_mutation_complex_input(): ) +def test_mutation_list_input(): + class User(ObjectType): + name = String(required=True) + email = String(required=True) + + class CreateUsersSuccess(ObjectType): + users = List(NonNull(User), required=True) + + class CreateUsersError(ObjectType): + error_message = String(required=True) + + class CreateUsersOutput(Union): + class Meta: + types = [ + CreateUsersSuccess, + CreateUsersError, + ] + + class CreateUserInput(InputObjectType): + name = String(required=True) + email = String(required=True) + + @mutation( + CreateUsersOutput, + required=True, + arguments={"users": List(NonNull(CreateUserInput), required=True)}, + ) + def create_users(root, info, users): + return CreateUsersSuccess(users=[User(**user) for user in users]) + + class Query(ObjectType): + a = String() + + schema = Schema(query=Query, mutations=[create_users]) + result = schema.execute( + """ + mutation CreateUserMutation { + createUsers( + users: [ + { name: "Kate", email: "kate@example.com" }, + { name: "Jo", email: "jo@example.com" }, + ] + ) { + __typename + ... on CreateUsersSuccess { + users { + name + } + } + } + } + """ + ) + + assert not result.errors + assert result.data == { + "createUsers": { + "__typename": "CreateUsersSuccess", + "users": [{"name": "Kate"}, {"name": "Jo"}], + } + } + + def test_raises_error_invalid_input(): class User(ObjectType): name = String(required=True) @@ -281,3 +354,16 @@ def test_raises_error_invalid_input(): "Arguments `user` and `user2` are not valid types in mutation `create_user2`. " "Arguments to a mutation need to be either a Scalar type or an InputObjectType." ) + + with pytest.raises(MutationInvalidArgumentsError) as validation_error: + + @mutation( + Boolean, required=True, arguments={"users": List(User)}, + ) + def create_user3(root, info, user): + return True + + assert str(validation_error.value) == ( + "Argument `users` is not a valid type in mutation `create_user3`. " + "Arguments to a mutation need to be either a Scalar type or an InputObjectType." + )