[863] DateTime, Date, and Time now accept datetime.datetime, datetime.date,… (#864)

* DateTime, Date, and Time now accept datetime.datetime, datetime.date, and datetime.time resp. inputs when used as variables

* Added tests and improved resilience against bad DateTime, Date, and Time inputs.

* Fixed string type checks too narrow for py2.7

* fixed some of pre-commit's complaints
This commit is contained in:
Andreas Sodeur 2019-03-31 12:57:34 +02:00 committed by Jonathan Kim
parent c5b2281e22
commit 7b6dae7fa3
2 changed files with 80 additions and 3 deletions

View File

@ -4,6 +4,7 @@ import datetime
from aniso8601 import parse_date, parse_datetime, parse_time from aniso8601 import parse_date, parse_datetime, parse_time
from graphql.language import ast from graphql.language import ast
from six import string_types
from .scalars import Scalar from .scalars import Scalar
@ -32,6 +33,9 @@ class Date(Scalar):
@staticmethod @staticmethod
def parse_value(value): def parse_value(value):
try: try:
if isinstance(value, datetime.date):
return value
elif isinstance(value, string_types):
return parse_date(value) return parse_date(value)
except ValueError: except ValueError:
return None return None
@ -59,6 +63,9 @@ class DateTime(Scalar):
@staticmethod @staticmethod
def parse_value(value): def parse_value(value):
try: try:
if isinstance(value, datetime.datetime):
return value
elif isinstance(value, string_types):
return parse_datetime(value) return parse_datetime(value)
except ValueError: except ValueError:
return None return None
@ -86,6 +93,9 @@ class Time(Scalar):
@classmethod @classmethod
def parse_value(cls, value): def parse_value(cls, value):
try: try:
if isinstance(value, datetime.time):
return value
elif isinstance(value, string_types):
return parse_time(value) return parse_time(value)
except ValueError: except ValueError:
return None return None

View File

@ -2,6 +2,7 @@ import datetime
import pytz import pytz
from graphql import GraphQLError from graphql import GraphQLError
import pytest
from ..datetime import Date, DateTime, Time from ..datetime import Date, DateTime, Time
from ..objecttype import ObjectType from ..objecttype import ObjectType
@ -88,6 +89,15 @@ def test_datetime_query_variable():
now = datetime.datetime.now().replace(tzinfo=pytz.utc) now = datetime.datetime.now().replace(tzinfo=pytz.utc)
isoformat = now.isoformat() isoformat = now.isoformat()
# test datetime variable provided as Python datetime
result = schema.execute(
"""query Test($date: DateTime){ datetime(in: $date) }""",
variables={"date": now},
)
assert not result.errors
assert result.data == {"datetime": isoformat}
# test datetime variable in string representation
result = schema.execute( result = schema.execute(
"""query Test($date: DateTime){ datetime(in: $date) }""", """query Test($date: DateTime){ datetime(in: $date) }""",
variables={"date": isoformat}, variables={"date": isoformat},
@ -100,6 +110,14 @@ def test_date_query_variable():
now = datetime.datetime.now().replace(tzinfo=pytz.utc).date() now = datetime.datetime.now().replace(tzinfo=pytz.utc).date()
isoformat = now.isoformat() isoformat = now.isoformat()
# test date variable provided as Python date
result = schema.execute(
"""query Test($date: Date){ date(in: $date) }""", variables={"date": now}
)
assert not result.errors
assert result.data == {"date": isoformat}
# test date variable in string representation
result = schema.execute( result = schema.execute(
"""query Test($date: Date){ date(in: $date) }""", variables={"date": isoformat} """query Test($date: Date){ date(in: $date) }""", variables={"date": isoformat}
) )
@ -112,8 +130,57 @@ def test_time_query_variable():
time = datetime.time(now.hour, now.minute, now.second, now.microsecond, now.tzinfo) time = datetime.time(now.hour, now.minute, now.second, now.microsecond, now.tzinfo)
isoformat = time.isoformat() isoformat = time.isoformat()
# test time variable provided as Python time
result = schema.execute(
"""query Test($time: Time){ time(at: $time) }""", variables={"time": time}
)
assert not result.errors
assert result.data == {"time": isoformat}
# test time variable in string representation
result = schema.execute( result = schema.execute(
"""query Test($time: Time){ time(at: $time) }""", variables={"time": isoformat} """query Test($time: Time){ time(at: $time) }""", variables={"time": isoformat}
) )
assert not result.errors assert not result.errors
assert result.data == {"time": isoformat} assert result.data == {"time": isoformat}
@pytest.mark.xfail(
reason="creating the error message fails when un-parsable object is not JSON serializable."
)
def test_bad_variables():
def _test_bad_variables(type, input):
result = schema.execute(
"""query Test($input: {}){{ {}(in: $input) }}""".format(type, type.lower()),
variables={"input": input},
)
assert len(result.errors) == 1
# when `input` is not JSON serializable formatting the error message in
# `graphql.utils.is_valid_value` line 79 fails with a TypeError
assert isinstance(result.errors[0], GraphQLError)
print(result.errors[0])
assert result.data is None
not_a_date = dict()
not_a_date_str = "Some string that's not a date"
today = datetime.date.today()
now = datetime.datetime.now().replace(tzinfo=pytz.utc)
time = datetime.time(now.hour, now.minute, now.second, now.microsecond, now.tzinfo)
bad_pairs = [
("DateTime", not_a_date),
("DateTime", not_a_date_str),
("DateTime", today),
("DateTime", time),
("Date", not_a_date),
("Date", not_a_date_str),
("Date", now),
("Date", time),
("Time", not_a_date),
("Time", not_a_date_str),
("Time", now),
("Time", today),
]
for type, input in bad_pairs:
_test_bad_variables(type, input)