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.field import Field
|
||||||
|
from graphene.types.inputobjecttype import InputObjectType
|
||||||
|
from graphene.types.scalars import Scalar
|
||||||
from graphene.utils.str_converters import to_camel_case
|
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):
|
def mutation(return_type, arguments=None, **kwargs):
|
||||||
# TODO: validate input arguments
|
|
||||||
if arguments is None:
|
if arguments is None:
|
||||||
arguments = {}
|
arguments = {}
|
||||||
|
|
||||||
|
@ -11,13 +35,23 @@ def mutation(return_type, arguments=None, **kwargs):
|
||||||
name = kwargs.pop("name", None) or resolver_function.__name__
|
name = kwargs.pop("name", None) or resolver_function.__name__
|
||||||
description = kwargs.pop("description", None) or resolver_function.__doc__
|
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 Field(
|
||||||
return_type,
|
return_type,
|
||||||
args=arguments,
|
args=arguments,
|
||||||
name=to_camel_case(name),
|
name=to_camel_case(name),
|
||||||
resolver=resolver_function,
|
resolver=resolver_function,
|
||||||
description=description,
|
description=description,
|
||||||
**kwargs
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
return decorate
|
return decorate
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
from textwrap import dedent
|
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():
|
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