mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 09:36:44 +03:00
fix: input with invalid types should raise an error
This commit is contained in:
parent
efe4b89015
commit
3bdc67c6ae
|
@ -270,7 +270,7 @@ The following is an example for creating a DateTime scalar:
|
||||||
return dt.isoformat()
|
return dt.isoformat()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_literal(node):
|
def parse_literal(node, _variables=None):
|
||||||
if isinstance(node, ast.StringValue):
|
if isinstance(node, ast.StringValue):
|
||||||
return datetime.datetime.strptime(
|
return datetime.datetime.strptime(
|
||||||
node.value, "%Y-%m-%dT%H:%M:%S.%f")
|
node.value, "%Y-%m-%dT%H:%M:%S.%f")
|
||||||
|
|
|
@ -24,8 +24,8 @@ from ...types.uuid import UUID
|
||||||
(ID, "1"),
|
(ID, "1"),
|
||||||
(DateTime, '"2022-02-02T11:11:11"'),
|
(DateTime, '"2022-02-02T11:11:11"'),
|
||||||
(UUID, '"cbebbc62-758e-4f75-a890-bc73b5017d81"'),
|
(UUID, '"cbebbc62-758e-4f75-a890-bc73b5017d81"'),
|
||||||
(Decimal, "1.1"),
|
(Decimal, '"1.1"'),
|
||||||
(JSONString, '{key:"foo",value:"bar"}'),
|
(JSONString, '"{\\"key\\":\\"foo\\",\\"value\\":\\"bar\\"}"'),
|
||||||
(Base64, '"Q2hlbG8gd29ycmxkCg=="'),
|
(Base64, '"Q2hlbG8gd29ycmxkCg=="'),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import absolute_import
|
||||||
|
|
||||||
from decimal import Decimal as _Decimal
|
from decimal import Decimal as _Decimal
|
||||||
|
|
||||||
|
from graphql import Undefined
|
||||||
from graphql.language.ast import StringValueNode, IntValueNode
|
from graphql.language.ast import StringValueNode, IntValueNode
|
||||||
|
|
||||||
from .scalars import Scalar
|
from .scalars import Scalar
|
||||||
|
@ -25,10 +26,11 @@ class Decimal(Scalar):
|
||||||
def parse_literal(cls, node, _variables=None):
|
def parse_literal(cls, node, _variables=None):
|
||||||
if isinstance(node, (StringValueNode, IntValueNode)):
|
if isinstance(node, (StringValueNode, IntValueNode)):
|
||||||
return cls.parse_value(node.value)
|
return cls.parse_value(node.value)
|
||||||
|
return Undefined
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_value(value):
|
def parse_value(value):
|
||||||
try:
|
try:
|
||||||
return _Decimal(value)
|
return _Decimal(value)
|
||||||
except ValueError:
|
except Exception:
|
||||||
return None
|
return Undefined
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import absolute_import
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from graphql import Undefined
|
||||||
from graphql.language.ast import StringValueNode
|
from graphql.language.ast import StringValueNode
|
||||||
|
|
||||||
from .scalars import Scalar
|
from .scalars import Scalar
|
||||||
|
@ -22,7 +23,11 @@ class JSONString(Scalar):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_literal(node, _variables=None):
|
def parse_literal(node, _variables=None):
|
||||||
if isinstance(node, StringValueNode):
|
if isinstance(node, StringValueNode):
|
||||||
return json.loads(node.value)
|
try:
|
||||||
|
return json.loads(node.value)
|
||||||
|
except Exception as error:
|
||||||
|
raise ValueError(f"Badly formed JSONString: {str(error)}")
|
||||||
|
return Undefined
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_value(value):
|
def parse_value(value):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from graphql import Undefined
|
||||||
from graphql.language.ast import (
|
from graphql.language.ast import (
|
||||||
BooleanValueNode,
|
BooleanValueNode,
|
||||||
FloatValueNode,
|
FloatValueNode,
|
||||||
|
@ -67,9 +68,10 @@ class Int(Scalar):
|
||||||
try:
|
try:
|
||||||
num = int(float(value))
|
num = int(float(value))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return Undefined
|
||||||
if MIN_INT <= num <= MAX_INT:
|
if MIN_INT <= num <= MAX_INT:
|
||||||
return num
|
return num
|
||||||
|
return Undefined
|
||||||
|
|
||||||
serialize = coerce_int
|
serialize = coerce_int
|
||||||
parse_value = coerce_int
|
parse_value = coerce_int
|
||||||
|
@ -80,6 +82,7 @@ class Int(Scalar):
|
||||||
num = int(ast.value)
|
num = int(ast.value)
|
||||||
if MIN_INT <= num <= MAX_INT:
|
if MIN_INT <= num <= MAX_INT:
|
||||||
return num
|
return num
|
||||||
|
return Undefined
|
||||||
|
|
||||||
|
|
||||||
class BigInt(Scalar):
|
class BigInt(Scalar):
|
||||||
|
@ -97,7 +100,7 @@ class BigInt(Scalar):
|
||||||
try:
|
try:
|
||||||
num = int(float(value))
|
num = int(float(value))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return Undefined
|
||||||
return num
|
return num
|
||||||
|
|
||||||
serialize = coerce_int
|
serialize = coerce_int
|
||||||
|
@ -107,6 +110,7 @@ class BigInt(Scalar):
|
||||||
def parse_literal(ast, _variables=None):
|
def parse_literal(ast, _variables=None):
|
||||||
if isinstance(ast, IntValueNode):
|
if isinstance(ast, IntValueNode):
|
||||||
return int(ast.value)
|
return int(ast.value)
|
||||||
|
return Undefined
|
||||||
|
|
||||||
|
|
||||||
class Float(Scalar):
|
class Float(Scalar):
|
||||||
|
@ -122,7 +126,7 @@ class Float(Scalar):
|
||||||
try:
|
try:
|
||||||
return float(value)
|
return float(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return Undefined
|
||||||
|
|
||||||
serialize = coerce_float
|
serialize = coerce_float
|
||||||
parse_value = coerce_float
|
parse_value = coerce_float
|
||||||
|
@ -131,6 +135,7 @@ class Float(Scalar):
|
||||||
def parse_literal(ast, _variables=None):
|
def parse_literal(ast, _variables=None):
|
||||||
if isinstance(ast, (FloatValueNode, IntValueNode)):
|
if isinstance(ast, (FloatValueNode, IntValueNode)):
|
||||||
return float(ast.value)
|
return float(ast.value)
|
||||||
|
return Undefined
|
||||||
|
|
||||||
|
|
||||||
class String(Scalar):
|
class String(Scalar):
|
||||||
|
@ -153,6 +158,7 @@ class String(Scalar):
|
||||||
def parse_literal(ast, _variables=None):
|
def parse_literal(ast, _variables=None):
|
||||||
if isinstance(ast, StringValueNode):
|
if isinstance(ast, StringValueNode):
|
||||||
return ast.value
|
return ast.value
|
||||||
|
return Undefined
|
||||||
|
|
||||||
|
|
||||||
class Boolean(Scalar):
|
class Boolean(Scalar):
|
||||||
|
@ -167,6 +173,7 @@ class Boolean(Scalar):
|
||||||
def parse_literal(ast, _variables=None):
|
def parse_literal(ast, _variables=None):
|
||||||
if isinstance(ast, BooleanValueNode):
|
if isinstance(ast, BooleanValueNode):
|
||||||
return ast.value
|
return ast.value
|
||||||
|
return Undefined
|
||||||
|
|
||||||
|
|
||||||
class ID(Scalar):
|
class ID(Scalar):
|
||||||
|
@ -185,3 +192,4 @@ class ID(Scalar):
|
||||||
def parse_literal(ast, _variables=None):
|
def parse_literal(ast, _variables=None):
|
||||||
if isinstance(ast, (StringValueNode, IntValueNode)):
|
if isinstance(ast, (StringValueNode, IntValueNode)):
|
||||||
return ast.value
|
return ast.value
|
||||||
|
return Undefined
|
||||||
|
|
|
@ -39,8 +39,25 @@ def test_bad_decimal_query():
|
||||||
not_a_decimal = "Nobody expects the Spanish Inquisition!"
|
not_a_decimal = "Nobody expects the Spanish Inquisition!"
|
||||||
|
|
||||||
result = schema.execute("""{ decimal(input: "%s") }""" % not_a_decimal)
|
result = schema.execute("""{ decimal(input: "%s") }""" % not_a_decimal)
|
||||||
|
assert result.errors
|
||||||
assert len(result.errors) == 1
|
assert len(result.errors) == 1
|
||||||
assert result.data is None
|
assert result.data is None
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== "Expected value of type 'Decimal', found \"Nobody expects the Spanish Inquisition!\"."
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("{ decimal(input: true) }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.data is None
|
||||||
|
assert result.errors[0].message == "Expected value of type 'Decimal', found true."
|
||||||
|
|
||||||
|
result = schema.execute("{ decimal(input: 1.2) }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.data is None
|
||||||
|
assert result.errors[0].message == "Expected value of type 'Decimal', found 1.2."
|
||||||
|
|
||||||
|
|
||||||
def test_decimal_string_query_integer():
|
def test_decimal_string_query_integer():
|
||||||
|
|
|
@ -21,6 +21,10 @@ def test_jsonstring_query():
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {"json": json_value}
|
assert result.data == {"json": json_value}
|
||||||
|
|
||||||
|
result = schema.execute("""{ json(input: "{}") }""")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"json": "{}"}
|
||||||
|
|
||||||
|
|
||||||
def test_jsonstring_query_variable():
|
def test_jsonstring_query_variable():
|
||||||
json_value = '{"key": "value"}'
|
json_value = '{"key": "value"}'
|
||||||
|
@ -31,3 +35,51 @@ def test_jsonstring_query_variable():
|
||||||
)
|
)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {"json": json_value}
|
assert result.data == {"json": json_value}
|
||||||
|
|
||||||
|
|
||||||
|
def test_jsonstring_optional_uuid_input():
|
||||||
|
"""
|
||||||
|
Test that we can provide a null value to an optional input
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ json(input: null) }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"json": None}
|
||||||
|
|
||||||
|
|
||||||
|
def test_jsonstring_invalid_query():
|
||||||
|
"""
|
||||||
|
Test that if an invalid type is provided we get an error
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ json(input: 1) }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.errors[0].message == "Expected value of type 'JSONString', found 1."
|
||||||
|
|
||||||
|
result = schema.execute("{ json(input: {}) }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.errors[0].message == "Expected value of type 'JSONString', found {}."
|
||||||
|
|
||||||
|
result = schema.execute('{ json(input: "a") }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.errors[0].message == (
|
||||||
|
"Expected value of type 'JSONString', found \"a\"; "
|
||||||
|
"Badly formed JSONString: Expecting value: line 1 column 1 (char 0)"
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("""{ json(input: "{\\'key\\': 0}") }""")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== "Syntax Error: Invalid character escape sequence: '\\''."
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("""{ json(input: "{\\"key\\": 0,}") }""")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.errors[0].message == (
|
||||||
|
'Expected value of type \'JSONString\', found "{\\"key\\": 0,}"; '
|
||||||
|
"Badly formed JSONString: Expecting property name enclosed in double quotes: line 1 column 11 (char 10)"
|
||||||
|
)
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
from ..scalars import Scalar, Int, BigInt
|
from ..objecttype import ObjectType, Field
|
||||||
|
from ..scalars import Scalar, Int, BigInt, Float, String, Boolean
|
||||||
|
from ..schema import Schema
|
||||||
|
from graphql import Undefined
|
||||||
from graphql.language.ast import IntValueNode
|
from graphql.language.ast import IntValueNode
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,19 +14,295 @@ def test_scalar():
|
||||||
|
|
||||||
|
|
||||||
def test_ints():
|
def test_ints():
|
||||||
assert Int.parse_value(2**31 - 1) is not None
|
assert Int.parse_value(2**31 - 1) is not Undefined
|
||||||
assert Int.parse_value("2.0") is not None
|
assert Int.parse_value("2.0") == 2
|
||||||
assert Int.parse_value(2**31) is None
|
assert Int.parse_value(2**31) is Undefined
|
||||||
|
|
||||||
assert Int.parse_literal(IntValueNode(value=str(2**31 - 1))) == 2**31 - 1
|
assert Int.parse_literal(IntValueNode(value=str(2**31 - 1))) == 2**31 - 1
|
||||||
assert Int.parse_literal(IntValueNode(value=str(2**31))) is None
|
assert Int.parse_literal(IntValueNode(value=str(2**31))) is Undefined
|
||||||
|
|
||||||
assert Int.parse_value(-(2**31)) is not None
|
assert Int.parse_value(-(2**31)) is not Undefined
|
||||||
assert Int.parse_value(-(2**31) - 1) is None
|
assert Int.parse_value(-(2**31) - 1) is Undefined
|
||||||
|
|
||||||
assert BigInt.parse_value(2**31) is not None
|
assert BigInt.parse_value(2**31) is not Undefined
|
||||||
assert BigInt.parse_value("2.0") is not None
|
assert BigInt.parse_value("2.0") == 2
|
||||||
assert BigInt.parse_value(-(2**31) - 1) is not None
|
assert BigInt.parse_value(-(2**31) - 1) is not Undefined
|
||||||
|
|
||||||
assert BigInt.parse_literal(IntValueNode(value=str(2**31 - 1))) == 2**31 - 1
|
assert BigInt.parse_literal(IntValueNode(value=str(2**31 - 1))) == 2**31 - 1
|
||||||
assert BigInt.parse_literal(IntValueNode(value=str(2**31))) == 2**31
|
assert BigInt.parse_literal(IntValueNode(value=str(2**31))) == 2**31
|
||||||
|
|
||||||
|
|
||||||
|
def return_input(_parent, _info, input):
|
||||||
|
return input
|
||||||
|
|
||||||
|
|
||||||
|
class Optional(ObjectType):
|
||||||
|
int = Int(input=Int(), resolver=return_input)
|
||||||
|
big_int = BigInt(input=BigInt(), resolver=return_input)
|
||||||
|
float = Float(input=Float(), resolver=return_input)
|
||||||
|
bool = Boolean(input=Boolean(), resolver=return_input)
|
||||||
|
string = String(input=String(), resolver=return_input)
|
||||||
|
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
optional = Field(Optional)
|
||||||
|
|
||||||
|
def resolve_optional(self, info):
|
||||||
|
return Optional()
|
||||||
|
|
||||||
|
def resolve_required(self, info, input):
|
||||||
|
return input
|
||||||
|
|
||||||
|
|
||||||
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
|
||||||
|
class TestInt:
|
||||||
|
def test_query(self):
|
||||||
|
"""
|
||||||
|
Test that a normal query works.
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { int(input: 20) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"int": 20}}
|
||||||
|
|
||||||
|
def test_optional_input(self):
|
||||||
|
"""
|
||||||
|
Test that we can provide a null value to an optional input
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { int(input: null) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"int": None}}
|
||||||
|
|
||||||
|
def test_invalid_input(self):
|
||||||
|
"""
|
||||||
|
Test that if an invalid type is provided we get an error
|
||||||
|
"""
|
||||||
|
result = schema.execute('{ optional { int(input: "20") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == 'Int cannot represent non-integer value: "20"'
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute('{ optional { int(input: "a") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.errors[0].message == 'Int cannot represent non-integer value: "a"'
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { int(input: true) } }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == "Int cannot represent non-integer value: true"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBigInt:
|
||||||
|
def test_query(self):
|
||||||
|
"""
|
||||||
|
Test that a normal query works.
|
||||||
|
"""
|
||||||
|
value = 2**31
|
||||||
|
result = schema.execute("{ optional { bigInt(input: %s) } }" % value)
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"bigInt": value}}
|
||||||
|
|
||||||
|
def test_optional_input(self):
|
||||||
|
"""
|
||||||
|
Test that we can provide a null value to an optional input
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { bigInt(input: null) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"bigInt": None}}
|
||||||
|
|
||||||
|
def test_invalid_input(self):
|
||||||
|
"""
|
||||||
|
Test that if an invalid type is provided we get an error
|
||||||
|
"""
|
||||||
|
result = schema.execute('{ optional { bigInt(input: "20") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == "Expected value of type 'BigInt', found \"20\"."
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute('{ optional { bigInt(input: "a") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == "Expected value of type 'BigInt', found \"a\"."
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { bigInt(input: true) } }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == "Expected value of type 'BigInt', found true."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestFloat:
|
||||||
|
def test_query(self):
|
||||||
|
"""
|
||||||
|
Test that a normal query works.
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { float(input: 20) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"float": 20.0}}
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { float(input: 20.2) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"float": 20.2}}
|
||||||
|
|
||||||
|
def test_optional_input(self):
|
||||||
|
"""
|
||||||
|
Test that we can provide a null value to an optional input
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { float(input: null) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"float": None}}
|
||||||
|
|
||||||
|
def test_invalid_input(self):
|
||||||
|
"""
|
||||||
|
Test that if an invalid type is provided we get an error
|
||||||
|
"""
|
||||||
|
result = schema.execute('{ optional { float(input: "20") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == 'Float cannot represent non numeric value: "20"'
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute('{ optional { float(input: "a") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == 'Float cannot represent non numeric value: "a"'
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { float(input: true) } }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == "Float cannot represent non numeric value: true"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestBoolean:
|
||||||
|
def test_query(self):
|
||||||
|
"""
|
||||||
|
Test that a normal query works.
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { bool(input: true) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"bool": True}}
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { bool(input: false) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"bool": False}}
|
||||||
|
|
||||||
|
def test_optional_input(self):
|
||||||
|
"""
|
||||||
|
Test that we can provide a null value to an optional input
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { bool(input: null) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"bool": None}}
|
||||||
|
|
||||||
|
def test_invalid_input(self):
|
||||||
|
"""
|
||||||
|
Test that if an invalid type is provided we get an error
|
||||||
|
"""
|
||||||
|
result = schema.execute('{ optional { bool(input: "True") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== 'Boolean cannot represent a non boolean value: "True"'
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute('{ optional { bool(input: "true") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== 'Boolean cannot represent a non boolean value: "true"'
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute('{ optional { bool(input: "a") } }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== 'Boolean cannot represent a non boolean value: "a"'
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { bool(input: 1) } }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== "Boolean cannot represent a non boolean value: 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { bool(input: 0) } }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== "Boolean cannot represent a non boolean value: 0"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestString:
|
||||||
|
def test_query(self):
|
||||||
|
"""
|
||||||
|
Test that a normal query works.
|
||||||
|
"""
|
||||||
|
result = schema.execute('{ optional { string(input: "something something") } }')
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"string": "something something"}}
|
||||||
|
|
||||||
|
result = schema.execute('{ optional { string(input: "True") } }')
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"string": "True"}}
|
||||||
|
|
||||||
|
result = schema.execute('{ optional { string(input: "0") } }')
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"string": "0"}}
|
||||||
|
|
||||||
|
def test_optional_input(self):
|
||||||
|
"""
|
||||||
|
Test that we can provide a null value to an optional input
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { string(input: null) } }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"optional": {"string": None}}
|
||||||
|
|
||||||
|
def test_invalid_input(self):
|
||||||
|
"""
|
||||||
|
Test that if an invalid type is provided we get an error
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ optional { string(input: 1) } }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message == "String cannot represent a non string value: 1"
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { string(input: 3.2) } }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== "String cannot represent a non string value: 3.2"
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("{ optional { string(input: true) } }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== "String cannot represent a non string value: true"
|
||||||
|
)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from graphql import Undefined
|
||||||
from ..scalars import Boolean, Float, Int, String
|
from ..scalars import Boolean, Float, Int, String
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,12 +10,12 @@ def test_serializes_output_int():
|
||||||
assert Int.serialize(1.1) == 1
|
assert Int.serialize(1.1) == 1
|
||||||
assert Int.serialize(-1.1) == -1
|
assert Int.serialize(-1.1) == -1
|
||||||
assert Int.serialize(1e5) == 100000
|
assert Int.serialize(1e5) == 100000
|
||||||
assert Int.serialize(9876504321) is None
|
assert Int.serialize(9876504321) is Undefined
|
||||||
assert Int.serialize(-9876504321) is None
|
assert Int.serialize(-9876504321) is Undefined
|
||||||
assert Int.serialize(1e100) is None
|
assert Int.serialize(1e100) is Undefined
|
||||||
assert Int.serialize(-1e100) is None
|
assert Int.serialize(-1e100) is Undefined
|
||||||
assert Int.serialize("-1.1") == -1
|
assert Int.serialize("-1.1") == -1
|
||||||
assert Int.serialize("one") is None
|
assert Int.serialize("one") is Undefined
|
||||||
assert Int.serialize(False) == 0
|
assert Int.serialize(False) == 0
|
||||||
assert Int.serialize(True) == 1
|
assert Int.serialize(True) == 1
|
||||||
|
|
||||||
|
@ -27,7 +28,7 @@ def test_serializes_output_float():
|
||||||
assert Float.serialize(1.1) == 1.1
|
assert Float.serialize(1.1) == 1.1
|
||||||
assert Float.serialize(-1.1) == -1.1
|
assert Float.serialize(-1.1) == -1.1
|
||||||
assert Float.serialize("-1.1") == -1.1
|
assert Float.serialize("-1.1") == -1.1
|
||||||
assert Float.serialize("one") is None
|
assert Float.serialize("one") is Undefined
|
||||||
assert Float.serialize(False) == 0
|
assert Float.serialize(False) == 0
|
||||||
assert Float.serialize(True) == 1
|
assert Float.serialize(True) == 1
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
from ..objecttype import ObjectType
|
from ..objecttype import ObjectType
|
||||||
from ..schema import Schema
|
from ..schema import Schema
|
||||||
from ..uuid import UUID
|
from ..uuid import UUID
|
||||||
|
from ..structures import NonNull
|
||||||
|
|
||||||
|
|
||||||
class Query(ObjectType):
|
class Query(ObjectType):
|
||||||
uuid = UUID(input=UUID())
|
uuid = UUID(input=UUID())
|
||||||
|
required_uuid = UUID(input=NonNull(UUID), required=True)
|
||||||
|
|
||||||
def resolve_uuid(self, info, input):
|
def resolve_uuid(self, info, input):
|
||||||
return input
|
return input
|
||||||
|
|
||||||
|
def resolve_required_uuid(self, info, input):
|
||||||
|
return input
|
||||||
|
|
||||||
|
|
||||||
schema = Schema(query=Query)
|
schema = Schema(query=Query)
|
||||||
|
|
||||||
|
@ -29,3 +34,35 @@ def test_uuidstring_query_variable():
|
||||||
)
|
)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {"uuid": uuid_value}
|
assert result.data == {"uuid": uuid_value}
|
||||||
|
|
||||||
|
|
||||||
|
def test_uuidstring_optional_uuid_input():
|
||||||
|
"""
|
||||||
|
Test that we can provide a null value to an optional input
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ uuid(input: null) }")
|
||||||
|
assert not result.errors
|
||||||
|
assert result.data == {"uuid": None}
|
||||||
|
|
||||||
|
|
||||||
|
def test_uuidstring_invalid_query():
|
||||||
|
"""
|
||||||
|
Test that if an invalid type is provided we get an error
|
||||||
|
"""
|
||||||
|
result = schema.execute("{ uuid(input: 1) }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.errors[0].message == "Expected value of type 'UUID', found 1."
|
||||||
|
|
||||||
|
result = schema.execute('{ uuid(input: "a") }')
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert (
|
||||||
|
result.errors[0].message
|
||||||
|
== "Expected value of type 'UUID', found \"a\"; badly formed hexadecimal UUID string"
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute("{ requiredUuid(input: null) }")
|
||||||
|
assert result.errors
|
||||||
|
assert len(result.errors) == 1
|
||||||
|
assert result.errors[0].message == "Expected value of type 'UUID!', found null."
|
||||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import absolute_import
|
||||||
from uuid import UUID as _UUID
|
from uuid import UUID as _UUID
|
||||||
|
|
||||||
from graphql.language.ast import StringValueNode
|
from graphql.language.ast import StringValueNode
|
||||||
|
from graphql import Undefined
|
||||||
|
|
||||||
from .scalars import Scalar
|
from .scalars import Scalar
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ class UUID(Scalar):
|
||||||
def parse_literal(node, _variables=None):
|
def parse_literal(node, _variables=None):
|
||||||
if isinstance(node, StringValueNode):
|
if isinstance(node, StringValueNode):
|
||||||
return _UUID(node.value)
|
return _UUID(node.value)
|
||||||
|
return Undefined
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_value(value):
|
def parse_value(value):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user