mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-02 12:44:15 +03:00
Add some validation to input arguments
This commit is contained in:
parent
48f249af3b
commit
11e04df9c1
|
@ -1,9 +1,33 @@
|
|||
from typing import List
|
||||
|
||||
from graphene.types.field import Field
|
||||
from graphene.types.inputobjecttype import InputObjectType
|
||||
from graphene.types.scalars import Scalar
|
||||
from graphene.utils.str_converters import to_camel_case
|
||||
|
||||
|
||||
class MutationInvalidArgumentsError(Exception):
|
||||
def __init__(self, mutation_name: str, invalid_arguments: List[str]):
|
||||
invalid_arguments = sorted(invalid_arguments)
|
||||
|
||||
if len(invalid_arguments) == 1:
|
||||
message = (
|
||||
f"Argument `{invalid_arguments[0]}` is not a valid type "
|
||||
f"in mutation `{mutation_name}`. "
|
||||
)
|
||||
else:
|
||||
head = ", ".join(invalid_arguments[:-1])
|
||||
message = (
|
||||
f"Arguments `{head}` and `{invalid_arguments[-1]}` are not valid types "
|
||||
f"in mutation `{mutation_name}`. "
|
||||
)
|
||||
|
||||
message += "Arguments to a mutation need to be either a Scalar type or an InputObjectType."
|
||||
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
def mutation(return_type, arguments=None, **kwargs):
|
||||
# TODO: validate input arguments
|
||||
if arguments is None:
|
||||
arguments = {}
|
||||
|
||||
|
@ -11,13 +35,23 @@ def mutation(return_type, arguments=None, **kwargs):
|
|||
name = kwargs.pop("name", None) or resolver_function.__name__
|
||||
description = kwargs.pop("description", None) or resolver_function.__doc__
|
||||
|
||||
invalid_arguments = []
|
||||
for argument_name, argument in arguments.items():
|
||||
if not (
|
||||
isinstance(argument, Scalar) or isinstance(argument, InputObjectType)
|
||||
):
|
||||
invalid_arguments.append(argument_name)
|
||||
|
||||
if len(invalid_arguments) > 0:
|
||||
raise MutationInvalidArgumentsError(name, invalid_arguments)
|
||||
|
||||
return Field(
|
||||
return_type,
|
||||
args=arguments,
|
||||
name=to_camel_case(name),
|
||||
resolver=resolver_function,
|
||||
description=description,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return decorate
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from textwrap import dedent
|
||||
|
||||
from graphene import String, ObjectType, Schema, Union, Field
|
||||
import pytest
|
||||
|
||||
from ..mutation import mutation
|
||||
from graphene import Boolean, Field, InputObjectType, ObjectType, Schema, String, Union
|
||||
|
||||
from ..mutation import mutation, MutationInvalidArgumentsError
|
||||
|
||||
|
||||
def test_mutation_basic():
|
||||
|
@ -160,3 +162,122 @@ def test_mutation_complex_return():
|
|||
}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_mutation_complex_input():
|
||||
class User(ObjectType):
|
||||
name = String(required=True)
|
||||
email = String(required=True)
|
||||
|
||||
class CreateUserSuccess(ObjectType):
|
||||
user = Field(User, required=True)
|
||||
|
||||
class CreateUserError(ObjectType):
|
||||
error_message = String(required=True)
|
||||
|
||||
class CreateUserOutput(Union):
|
||||
class Meta:
|
||||
types = [
|
||||
CreateUserSuccess,
|
||||
CreateUserError,
|
||||
]
|
||||
|
||||
class CreateUserInput(InputObjectType):
|
||||
name = String(required=True)
|
||||
email = String(required=True)
|
||||
|
||||
@mutation(
|
||||
CreateUserOutput,
|
||||
required=True,
|
||||
arguments={"user": CreateUserInput(required=True)},
|
||||
)
|
||||
def create_user(root, info, user):
|
||||
return CreateUserSuccess(user=User(**user))
|
||||
|
||||
class Query(ObjectType):
|
||||
a = String()
|
||||
|
||||
schema = Schema(query=Query, mutations=[create_user])
|
||||
result = schema.execute(
|
||||
"""
|
||||
mutation CreateUserMutation {
|
||||
createUser(user: { name: "Kate", email: "kate@example.com" }) {
|
||||
__typename
|
||||
... on CreateUserSuccess {
|
||||
user {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assert not result.errors
|
||||
assert result.data == {
|
||||
"createUser": {"__typename": "CreateUserSuccess", "user": {"name": "Kate"}}
|
||||
}
|
||||
|
||||
assert str(schema) == dedent(
|
||||
"""\
|
||||
type Query {
|
||||
a: String
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createUser(user: CreateUserInput!): CreateUserOutput!
|
||||
}
|
||||
|
||||
union CreateUserOutput = CreateUserSuccess | CreateUserError
|
||||
|
||||
type CreateUserSuccess {
|
||||
user: User!
|
||||
}
|
||||
|
||||
type User {
|
||||
name: String!
|
||||
email: String!
|
||||
}
|
||||
|
||||
type CreateUserError {
|
||||
errorMessage: String!
|
||||
}
|
||||
|
||||
input CreateUserInput {
|
||||
name: String!
|
||||
email: String!
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def test_raises_error_invalid_input():
|
||||
class User(ObjectType):
|
||||
name = String(required=True)
|
||||
email = String(required=True)
|
||||
|
||||
with pytest.raises(MutationInvalidArgumentsError) as validation_error:
|
||||
|
||||
@mutation(
|
||||
Boolean, required=True, arguments={"user": User},
|
||||
)
|
||||
def create_user(root, info, user):
|
||||
return True
|
||||
|
||||
assert str(validation_error.value) == (
|
||||
"Argument `user` is not a valid type in mutation `create_user`. "
|
||||
"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={"user": User, "user2": User},
|
||||
)
|
||||
def create_user2(root, info, user):
|
||||
return True
|
||||
|
||||
assert str(validation_error.value) == (
|
||||
"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."
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user