From 7b6dae7fa3bf8d2e066e92e730c8a9740656a448 Mon Sep 17 00:00:00 2001 From: Andreas Sodeur <38456167+asodeur@users.noreply.github.com> Date: Sun, 31 Mar 2019 12:57:34 +0200 Subject: [PATCH] =?UTF-8?q?[863]=20DateTime,=20Date,=20and=20Time=20now=20?= =?UTF-8?q?accept=20datetime.datetime,=20datetime.date,=E2=80=A6=20(#864)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- graphene/types/datetime.py | 16 +++++-- graphene/types/tests/test_datetime.py | 67 +++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/graphene/types/datetime.py b/graphene/types/datetime.py index 739032b0..3519d76d 100644 --- a/graphene/types/datetime.py +++ b/graphene/types/datetime.py @@ -4,6 +4,7 @@ import datetime from aniso8601 import parse_date, parse_datetime, parse_time from graphql.language import ast +from six import string_types from .scalars import Scalar @@ -32,7 +33,10 @@ class Date(Scalar): @staticmethod def parse_value(value): try: - return parse_date(value) + if isinstance(value, datetime.date): + return value + elif isinstance(value, string_types): + return parse_date(value) except ValueError: return None @@ -59,7 +63,10 @@ class DateTime(Scalar): @staticmethod def parse_value(value): try: - return parse_datetime(value) + if isinstance(value, datetime.datetime): + return value + elif isinstance(value, string_types): + return parse_datetime(value) except ValueError: return None @@ -86,6 +93,9 @@ class Time(Scalar): @classmethod def parse_value(cls, value): try: - return parse_time(value) + if isinstance(value, datetime.time): + return value + elif isinstance(value, string_types): + return parse_time(value) except ValueError: return None diff --git a/graphene/types/tests/test_datetime.py b/graphene/types/tests/test_datetime.py index 98e5e7ab..0d9ee114 100644 --- a/graphene/types/tests/test_datetime.py +++ b/graphene/types/tests/test_datetime.py @@ -2,6 +2,7 @@ import datetime import pytz from graphql import GraphQLError +import pytest from ..datetime import Date, DateTime, Time from ..objecttype import ObjectType @@ -88,6 +89,15 @@ def test_datetime_query_variable(): now = datetime.datetime.now().replace(tzinfo=pytz.utc) 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( """query Test($date: DateTime){ datetime(in: $date) }""", variables={"date": isoformat}, @@ -100,6 +110,14 @@ def test_date_query_variable(): now = datetime.datetime.now().replace(tzinfo=pytz.utc).date() 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( """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) 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( """query Test($time: Time){ time(at: $time) }""", variables={"time": isoformat} ) assert not result.errors 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)