From ef7eff19790016c032371cac800f95654891fcb3 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 29 Jan 2020 14:53:49 -0400 Subject: [PATCH 1/4] Created email address scalar --- api/scalars/__init__.py | 0 api/scalars/email_address.py | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 api/scalars/__init__.py create mode 100644 api/scalars/email_address.py diff --git a/api/scalars/__init__.py b/api/scalars/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/scalars/email_address.py b/api/scalars/email_address.py new file mode 100644 index 0000000000..5c8c8936f1 --- /dev/null +++ b/api/scalars/email_address.py @@ -0,0 +1,39 @@ +import datetime +from re import compile +from graphene.types import Scalar +from graphql.language import ast +from graphql import GraphQLError + +EMAIL_ADDRESS_REGEX = compile('^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$') + + +class EmailAddress(Scalar): + '''A field whose value conforms to the standard internet email address format as specified in RFC822: https://www.w3.org/Protocols/rfc822/.''' + + @staticmethod + def serialize(value): + if not isinstance(value, str): + raise GraphQLError("Value is not string: " + str(type(value))) + + if not EMAIL_ADDRESS_REGEX.search(value): + raise GraphQLError("Value is not a valid email address: " + value) + + return value + + @staticmethod + def parse_value(value): + if not isinstance(value, str): + raise GraphQLError("Value is not string: " + str(type(value))) + + if not EMAIL_ADDRESS_REGEX.search(value): + raise GraphQLError("Value is not a valid email address: " + value) + + return value + + @staticmethod + def parse_literal(node): + if not isinstance(node, ast.StringValue): + raise GraphQLError("Can only validate strings as email addresses but got a: " + str(ast.Type)) + + if not EMAIL_ADDRESS_REGEX.search(node.value): + raise GraphQLError("Value is not a valid email address: " + node.value) \ No newline at end of file From 126ca179d1405cea38af5cf201fd582c5b6c492d Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 30 Jan 2020 08:43:13 -0400 Subject: [PATCH 2/4] Working on email scalar testing --- api/functions/error_messages.py | 8 ++++++ api/scalars/email_address.py | 24 ++++++++++++----- api/tests/test_email_address_scalar.py | 37 ++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 api/tests/test_email_address_scalar.py diff --git a/api/functions/error_messages.py b/api/functions/error_messages.py index 153e8ef5d6..e92936b9bc 100644 --- a/api/functions/error_messages.py +++ b/api/functions/error_messages.py @@ -20,3 +20,11 @@ def error_user_does_not_exist(): def error_invalid_credentials(): return str("Incorrect email or password") + + +def scalar_error_type(value_type, value): + return str("Value is not a valid " + value_type + ": " + value) + + +def scalar_error_only_types(value_types, expected_types, value): + return str("Can only validate " + value_types + " as " + expected_types + " but got a: " + str(type(value))) diff --git a/api/scalars/email_address.py b/api/scalars/email_address.py index 5c8c8936f1..6da88c8e79 100644 --- a/api/scalars/email_address.py +++ b/api/scalars/email_address.py @@ -4,7 +4,15 @@ from graphql.language import ast from graphql import GraphQLError -EMAIL_ADDRESS_REGEX = compile('^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$') +from functions.error_messages import * + +EMAIL_ADDRESS_REGEX = '''(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[ +\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[ +a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[ +0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[ +\x01-\x09\x0b\x0c\x0e-\x7f])+)\\])''' + +EMAIL_ADDRESS_REGEX = compile(EMAIL_ADDRESS_REGEX) class EmailAddress(Scalar): @@ -13,27 +21,29 @@ class EmailAddress(Scalar): @staticmethod def serialize(value): if not isinstance(value, str): - raise GraphQLError("Value is not string: " + str(type(value))) + raise GraphQLError(scalar_error_type("String", value)) if not EMAIL_ADDRESS_REGEX.search(value): - raise GraphQLError("Value is not a valid email address: " + value) + raise GraphQLError(scalar_error_type("email address", value)) return value @staticmethod def parse_value(value): if not isinstance(value, str): - raise GraphQLError("Value is not string: " + str(type(value))) + raise GraphQLError(scalar_error_type("String", value)) if not EMAIL_ADDRESS_REGEX.search(value): - raise GraphQLError("Value is not a valid email address: " + value) + raise GraphQLError(scalar_error_type("email address", value)) return value @staticmethod def parse_literal(node): if not isinstance(node, ast.StringValue): - raise GraphQLError("Can only validate strings as email addresses but got a: " + str(ast.Type)) + raise GraphQLError(scalar_error_only_types("strings", "email address", str(ast.Type))) if not EMAIL_ADDRESS_REGEX.search(node.value): - raise GraphQLError("Value is not a valid email address: " + node.value) \ No newline at end of file + raise GraphQLError(scalar_error_type("email address", node.value)) + + return node.value diff --git a/api/tests/test_email_address_scalar.py b/api/tests/test_email_address_scalar.py new file mode 100644 index 0000000000..6a073a88df --- /dev/null +++ b/api/tests/test_email_address_scalar.py @@ -0,0 +1,37 @@ +import sys +import os + +import pytest +from graphene.test import Client +from graphql import GraphQLScalarType +from graphql.language import ast +from graphql import GraphQLError + +import unittest + +# This is the only way I could get imports to work for unit testing. TODO: See if there is a better way! +PACKAGE_PARENT = '..' +SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__)))) +sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))) + +from scalars.email_address import * + + +class TestEmailAddressScalar(unittest.TestCase): + + def testValidEmailSerialize(self): + test_email = 'test.account@canada.ca' + assert EmailAddress.serialize(test_email) + + def testValidEmailParseValue(self): + test_email = "test.account@canada.ca" + assert EmailAddress.parse_value(test_email) + + def testValidEmailParseLiteral(self): + assert EmailAddress.parse_literal(ast.StringValue( + value="test.account@canada.ca" + )) + + def testInvalidEmailSerialize(self): + test_email = 'This Will Fail' + self.assertRaises(GraphQLError, EmailAddress.serialize(test_email), scalar_error_type("email address", test_email)) From 85e418b606efe286837d2ed4511cf2c2f4ac445d Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 30 Jan 2020 09:09:45 -0400 Subject: [PATCH 3/4] Finished testing for email scalar --- api/functions/error_messages.py | 4 +-- api/tests/test_email_address_scalar.py | 36 +++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/api/functions/error_messages.py b/api/functions/error_messages.py index e92936b9bc..643c0b296d 100644 --- a/api/functions/error_messages.py +++ b/api/functions/error_messages.py @@ -23,8 +23,8 @@ def error_invalid_credentials(): def scalar_error_type(value_type, value): - return str("Value is not a valid " + value_type + ": " + value) + return str("Value is not a valid " + str(value_type) + ": " + str(value)) def scalar_error_only_types(value_types, expected_types, value): - return str("Can only validate " + value_types + " as " + expected_types + " but got a: " + str(type(value))) + return str("Can only validate " + str(value_types) + " as " + str(expected_types) + " but got a: " + str(type(value))) diff --git a/api/tests/test_email_address_scalar.py b/api/tests/test_email_address_scalar.py index 6a073a88df..5f09c3a668 100644 --- a/api/tests/test_email_address_scalar.py +++ b/api/tests/test_email_address_scalar.py @@ -32,6 +32,36 @@ def testValidEmailParseLiteral(self): value="test.account@canada.ca" )) - def testInvalidEmailSerialize(self): - test_email = 'This Will Fail' - self.assertRaises(GraphQLError, EmailAddress.serialize(test_email), scalar_error_type("email address", test_email)) + def testInvalidEmailSerialize1(self): + test_value = 'This Will Fail' + with self.assertRaisesRegex(GraphQLError, scalar_error_type("email address", test_value)): + EmailAddress.serialize(test_value) + + def testInvalidEmailSerialize2(self): + test_value = 1234 + with self.assertRaisesRegex(GraphQLError, scalar_error_type("String", test_value)): + EmailAddress.serialize(test_value) + + def testInvalidEmailParseValue1(self): + test_value = 'This Will Fail' + with self.assertRaisesRegex(GraphQLError, scalar_error_type("email address", test_value)): + EmailAddress.parse_value(test_value) + + def testInvalidEmailParseValue2(self): + test_value = 1234 + with self.assertRaisesRegex(GraphQLError, scalar_error_type("String", test_value)): + EmailAddress.parse_value(test_value) + + def testInvalidEmailParseLiteral1(self): + test_value = ast.StringValue( + value='This Will Fail' + ) + with self.assertRaisesRegex(GraphQLError, scalar_error_type("email address", test_value.value)): + EmailAddress.parse_literal(test_value) + + def testInvalidEmailParseLiteral2(self): + test_value = ast.IntValue( + value="1234" + ) + with self.assertRaisesRegex(GraphQLError, scalar_error_only_types("strings", "email address", str(ast.Type))): + EmailAddress.parse_literal(test_value) From f0d8b52a3714de4cb76f14671cbabd85465eb8a2 Mon Sep 17 00:00:00 2001 From: nick Date: Thu, 30 Jan 2020 09:23:10 -0400 Subject: [PATCH 4/4] Fixed regex, renamed test functions --- api/scalars/email_address.py | 10 +++++----- api/tests/test_email_address_scalar.py | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/scalars/email_address.py b/api/scalars/email_address.py index 6da88c8e79..cac8af7355 100644 --- a/api/scalars/email_address.py +++ b/api/scalars/email_address.py @@ -6,11 +6,11 @@ from functions.error_messages import * -EMAIL_ADDRESS_REGEX = '''(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[ -\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[ -a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[ -0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[ -\x01-\x09\x0b\x0c\x0e-\x7f])+)\\])''' +EMAIL_ADDRESS_REGEX = r'''(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[ +\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[ +a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][ +0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[ +\x01-\x09\x0b\x0c\x0e-\x7f])+)\])''' EMAIL_ADDRESS_REGEX = compile(EMAIL_ADDRESS_REGEX) diff --git a/api/tests/test_email_address_scalar.py b/api/tests/test_email_address_scalar.py index 5f09c3a668..07fe8140b8 100644 --- a/api/tests/test_email_address_scalar.py +++ b/api/tests/test_email_address_scalar.py @@ -19,47 +19,47 @@ class TestEmailAddressScalar(unittest.TestCase): - def testValidEmailSerialize(self): + def test_valid_email_serialize(self): test_email = 'test.account@canada.ca' assert EmailAddress.serialize(test_email) - def testValidEmailParseValue(self): + def test_valid_email_parse_value(self): test_email = "test.account@canada.ca" assert EmailAddress.parse_value(test_email) - def testValidEmailParseLiteral(self): + def test_valid_email_parse_literal(self): assert EmailAddress.parse_literal(ast.StringValue( value="test.account@canada.ca" )) - def testInvalidEmailSerialize1(self): + def test_invalid_email_serialize_not_email(self): test_value = 'This Will Fail' with self.assertRaisesRegex(GraphQLError, scalar_error_type("email address", test_value)): EmailAddress.serialize(test_value) - def testInvalidEmailSerialize2(self): + def test_invalid_email_serialize_wrong_type(self): test_value = 1234 with self.assertRaisesRegex(GraphQLError, scalar_error_type("String", test_value)): EmailAddress.serialize(test_value) - def testInvalidEmailParseValue1(self): + def test_invalid_email_parse_value_not_email(self): test_value = 'This Will Fail' with self.assertRaisesRegex(GraphQLError, scalar_error_type("email address", test_value)): EmailAddress.parse_value(test_value) - def testInvalidEmailParseValue2(self): + def test_invalid_email_parse_value_wrong_type(self): test_value = 1234 with self.assertRaisesRegex(GraphQLError, scalar_error_type("String", test_value)): EmailAddress.parse_value(test_value) - def testInvalidEmailParseLiteral1(self): + def test_invalid_email_parse_literal_not_email(self): test_value = ast.StringValue( value='This Will Fail' ) with self.assertRaisesRegex(GraphQLError, scalar_error_type("email address", test_value.value)): EmailAddress.parse_literal(test_value) - def testInvalidEmailParseLiteral2(self): + def test_invalid_email_parse_literal_wrong_ast_type(self): test_value = ast.IntValue( value="1234" )