Skip to content

Commit

Permalink
[863] DateTime, Date, and Time now accept datetime.datetime, datetime…
Browse files Browse the repository at this point in the history
….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
  • Loading branch information
asodeur authored and jkimbo committed Mar 31, 2019
1 parent c5b2281 commit 7b6dae7
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 3 deletions.
16 changes: 13 additions & 3 deletions graphene/types/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from aniso8601 import parse_date, parse_datetime, parse_time
from graphql.language import ast
from six import string_types

from .scalars import Scalar

Expand Down Expand Up @@ -32,7 +33,10 @@ def parse_literal(cls, node):
@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

Expand All @@ -59,7 +63,10 @@ def parse_literal(cls, node):
@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

Expand All @@ -86,6 +93,9 @@ def parse_literal(cls, node):
@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
67 changes: 67 additions & 0 deletions graphene/types/tests/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytz
from graphql import GraphQLError
import pytest

from ..datetime import Date, DateTime, Time
from ..objecttype import ObjectType
Expand Down Expand Up @@ -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},
Expand All @@ -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}
)
Expand All @@ -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)

0 comments on commit 7b6dae7

Please sign in to comment.