From 52bf6dabcd8a75fc8a291fd2746bd217aedebff2 Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Thu, 15 Feb 2018 13:34:06 +0000 Subject: [PATCH] The regex to validate uuids was not rejecting uuids with a space at the end. Switched to using a isinstance check on the string. Added an order by clause to dao_get_template_usage_stats_by_service, it was causing an itermitten failure in the tests. --- app/dao/stats_template_usage_by_month_dao.py | 4 +++- app/schema_validation/__init__.py | 7 ++++++ app/schema_validation/definitions.py | 2 +- .../test_notification_schemas.py | 24 +++++++++++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/dao/stats_template_usage_by_month_dao.py b/app/dao/stats_template_usage_by_month_dao.py index cc54e16c1..29aba1cdc 100644 --- a/app/dao/stats_template_usage_by_month_dao.py +++ b/app/dao/stats_template_usage_by_month_dao.py @@ -1,5 +1,5 @@ from notifications_utils.statsd_decorators import statsd -from sqlalchemy import or_, and_ +from sqlalchemy import or_, and_, desc from app import db from app.dao.dao_utils import transactional @@ -54,4 +54,6 @@ def dao_get_template_usage_stats_by_service(service_id, year): StatsTemplateUsageByMonth.year == year + 1 ) ) + ).order_by( + desc(StatsTemplateUsageByMonth.month) ).all() diff --git a/app/schema_validation/__init__.py b/app/schema_validation/__init__.py index 4a443391f..b17efec6d 100644 --- a/app/schema_validation/__init__.py +++ b/app/schema_validation/__init__.py @@ -1,5 +1,6 @@ import json from datetime import datetime, timedelta +from uuid import UUID from iso8601 import iso8601, ParseError from jsonschema import (Draft4Validator, ValidationError, FormatChecker) @@ -10,6 +11,12 @@ from notifications_utils.recipients import (validate_phone_number, validate_emai def validate(json_to_validate, schema): format_checker = FormatChecker() + @format_checker.checks("validate_uuid", raises=Exception) + def validate_uuid(instance): + if isinstance(instance, str): + UUID(instance) + return True + @format_checker.checks('phone_number', raises=InvalidPhoneError) def validate_schema_phone_number(instance): if isinstance(instance, str): diff --git a/app/schema_validation/definitions.py b/app/schema_validation/definitions.py index 26c638456..c576c6e0a 100644 --- a/app/schema_validation/definitions.py +++ b/app/schema_validation/definitions.py @@ -5,7 +5,7 @@ If the definition is specific to a version put it in a definition file in the ve uuid = { "type": "string", - "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$", + "format": "validate_uuid", "validationMessage": "is not a valid UUID", "code": "1001", # yet to be implemented "link": "link to our error documentation not yet implemented" diff --git a/tests/app/v2/notifications/test_notification_schemas.py b/tests/app/v2/notifications/test_notification_schemas.py index c51aea6d9..7506da990 100644 --- a/tests/app/v2/notifications/test_notification_schemas.py +++ b/tests/app/v2/notifications/test_notification_schemas.py @@ -115,6 +115,30 @@ def test_post_sms_schema_is_valid(input): assert validate(input, post_sms_request_schema) == input +@pytest.mark.parametrize("template_id", + ['2ebe4da8-17be-49fe-b02f-dff2760261a0' + "\n", + '2ebe4da8-17be-49fe-b02f-dff2760261a0' + " ", + '2ebe4da8-17be-49fe-b02f-dff2760261a0' + "\r", + "\t" + '2ebe4da8-17be-49fe-b02f-dff2760261a0', + '2ebe4da8-17be-49fe-b02f-dff2760261a0'[4:], + "bad_uuid" + ] + ) +def test_post_sms_json_schema_bad_uuid(template_id): + j = { + "template_id": template_id, + "phone_number": "07515111111" + } + with pytest.raises(ValidationError) as e: + validate(j, post_sms_request_schema) + error = json.loads(str(e.value)) + assert len(error.keys()) == 2 + assert error.get('status_code') == 400 + assert len(error.get('errors')) == 1 + assert {'error': 'ValidationError', + 'message': "template_id is not a valid UUID"} in error['errors'] + + def test_post_sms_json_schema_bad_uuid_and_missing_phone_number(): j = {"template_id": "notUUID"} with pytest.raises(ValidationError) as e: