Repository: graphql-python/graphene
Pull request #679 (2018-02-27 00:03:04)

Made DateTime types return GraphQLError on fail
This change makes it so that when an incorrectly formatted date string gets passed to a Date / Time argument a GraphQLError is returned rather than a GraphQLLocatedError. Since Date / Time are types, their errors should not be in the same class as errors in your application. This is also inline with how other types work in graphene (graphene.Int, graphene.Float)

In our application, we hide all GraphQLLocatedErrors from the client as we don't wish to leak application information. As such this change is crucial for the users of our application to get any feedback about what they did wrong.


Patch:
diff --git a/graphene/types/datetime.py b/graphene/types/datetime.py
index 3b0e3f1..c3c78fb 100644
--- a/graphene/types/datetime.py
+++ b/graphene/types/datetime.py
@@ -38,7 +38,10 @@ class Date(Scalar):
 
     @staticmethod
     def parse_value(value):
-        return iso8601.parse_date(value).date()
+        try:
+            return iso8601.parse_date(value).date()
+        except iso8601.ParseError:
+            return None
 
 
 class DateTime(Scalar):
@@ -62,7 +65,10 @@ class DateTime(Scalar):
 
     @staticmethod
     def parse_value(value):
-        return iso8601.parse_date(value)
+        try:
+            return iso8601.parse_date(value)
+        except iso8601.ParseError:
+            return None
 
 
 class Time(Scalar):
@@ -87,5 +93,8 @@ class Time(Scalar):
 
     @classmethod
     def parse_value(cls, value):
-        dt = iso8601.parse_date('{}T{}'.format(cls.epoch_date, value))
-        return datetime.time(dt.hour, dt.minute, dt.second, dt.microsecond, dt.tzinfo)
+        try:
+            dt = iso8601.parse_date('{}T{}'.format(cls.epoch_date, value))
+            return datetime.time(dt.hour, dt.minute, dt.second, dt.microsecond, dt.tzinfo)
+        except iso8601.ParseError:
+            return None


Test patch:
diff --git a/graphene/types/tests/test_datetime.py b/graphene/types/tests/test_datetime.py
index bfe491a..b516e49 100644
--- a/graphene/types/tests/test_datetime.py
+++ b/graphene/types/tests/test_datetime.py
@@ -2,6 +2,8 @@ import datetime
 
 import pytz
 
+from graphql import GraphQLError
+
 from ..datetime import DateTime, Date, Time
 from ..objecttype import ObjectType
 from ..schema import Schema
@@ -34,7 +36,7 @@ def test_datetime_query():
     assert result.data == {'datetime': isoformat}
 
 
-def test_datetime_query():
+def test_date_query():
     now = datetime.datetime.now().replace(tzinfo=pytz.utc).date()
     isoformat = now.isoformat()
 
@@ -53,6 +55,32 @@ def test_time_query():
     assert not result.errors
     assert result.data == {'time': isoformat}
 
+def test_bad_datetime_query():
+    not_a_date = "Some string that's not a date"
+
+    result = schema.execute('''{ datetime(in: "%s") }''' % not_a_date)
+    
+    assert len(result.errors) == 1
+    assert isinstance(result.errors[0], GraphQLError)
+    assert result.data == None
+
+def test_bad_date_query():
+    not_a_date = "Some string that's not a date"
+    
+    result = schema.execute('''{ date(in: "%s") }''' % not_a_date)
+    
+    assert len(result.errors) == 1
+    assert isinstance(result.errors[0], GraphQLError)
+    assert result.data == None
+
+def test_bad_time_query():
+    not_a_date = "Some string that's not a date"
+
+    result = schema.execute('''{ time(at: "%s") }''' % not_a_date)
+
+    assert len(result.errors) == 1
+    assert isinstance(result.errors[0], GraphQLError)
+    assert result.data == None
 
 def test_datetime_query_variable():
     now = datetime.datetime.now().replace(tzinfo=pytz.utc)