Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove old phonenumber validation #5294

Merged
merged 4 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This file was automatically copied from notifications-utils@94.0.1
# This file was automatically copied from notifications-utils@95.0.0

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
Expand Down
3 changes: 2 additions & 1 deletion app/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@
get_lines_with_normalised_whitespace,
)
from notifications_utils.logging import flask as utils_logging
from notifications_utils.recipient_validation.phone_number import format_phone_number_human_readable
from notifications_utils.safe_string import make_string_safe_for_email_local_part, make_string_safe_for_id
from notifications_utils.sanitise_text import SanitiseASCII
from werkzeug.exceptions import HTTPException as WerkzeugHTTPException
from werkzeug.exceptions import abort
from werkzeug.local import LocalProxy

from app.formatters import format_phone_number_human_readable

# must be declared before rest of app is imported to satisfy circular import
# ruff: noqa: E402
memo_resetters: list[Callable] = []
Expand Down
13 changes: 11 additions & 2 deletions app/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from notifications_utils.field import Field
from notifications_utils.formatters import make_quotes_smart
from notifications_utils.formatters import nl2br as utils_nl2br
from notifications_utils.recipient_validation.phone_number import InvalidPhoneError, validate_phone_number
from notifications_utils.recipient_validation.phone_number import InvalidPhoneError, PhoneNumber
from notifications_utils.take import Take
from notifications_utils.timezones import utc_string_to_aware_gmt_datetime

Expand Down Expand Up @@ -139,7 +139,7 @@ def format_delta_days(date, numeric_prefix=""):

def valid_phone_number(phone_number):
try:
validate_phone_number(phone_number)
PhoneNumber(phone_number)
return True
except InvalidPhoneError:
return False
Expand Down Expand Up @@ -441,3 +441,12 @@ def extract_path_from_url(url):
def sentence_case(sentence):
first_word, rest_of_sentence = (sentence + " ").split(" ", 1)
return f"{first_word.title()} {rest_of_sentence}"[:-1]


def format_phone_number_human_readable(number):
try:
phone_number = PhoneNumber(number)
except InvalidPhoneError:
# if there was a validation error, we want to shortcut out here, but still display the number on the front end
return number
return phone_number.get_human_readable_format()
16 changes: 10 additions & 6 deletions app/main/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from notifications_utils.insensitive_dict import InsensitiveDict
from notifications_utils.recipient_validation.email_address import validate_email_address
from notifications_utils.recipient_validation.errors import InvalidEmailError, InvalidPhoneError
from notifications_utils.recipient_validation.phone_number import normalise_phone_number
from notifications_utils.recipient_validation.phone_number import PhoneNumber as PhoneNumberUtils
from notifications_utils.recipient_validation.postal_address import PostalAddress
from notifications_utils.safe_string import make_string_safe_for_email_local_part
from ordered_set import OrderedSet
Expand Down Expand Up @@ -1805,12 +1805,16 @@ def validate(self, *args, **kwargs):
# and disallow emergency 3-digit numbers
def valid_non_emergency_phone_number(self, num):
try:
normalised_number = normalise_phone_number(num.data)
PhoneNumberUtils(num.data, is_service_contact_number=True)
except InvalidPhoneError as e:
raise ValidationError("Enter a phone number in the correct format") from e

if normalised_number in {"999", "112"}:
raise ValidationError("Phone number cannot be an emergency number")
if e.code == InvalidPhoneError.Codes.UNSUPPORTED_EMERGENCY_NUMBER:
raise ValidationError(str(e)) from e
elif e.code == InvalidPhoneError.Codes.TOO_LONG:
# assume the number is an extension and return the number with minimal normalisation
return True

else:
raise ValidationError("Enter a phone number in the correct format") from e

return True

Expand Down
16 changes: 7 additions & 9 deletions app/main/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from notifications_utils.formatters import formatted_list
from notifications_utils.recipient_validation.email_address import validate_email_address
from notifications_utils.recipient_validation.errors import InvalidEmailError, InvalidPhoneError
from notifications_utils.recipient_validation.phone_number import PhoneNumber, validate_phone_number
from notifications_utils.recipient_validation.phone_number import PhoneNumber
from notifications_utils.sanitise_text import SanitiseSMS
from ordered_set import OrderedSet
from wtforms import ValidationError
Expand Down Expand Up @@ -95,19 +95,17 @@ def __init__(
InvalidPhoneError.Codes.NOT_A_UK_MOBILE: "%s does not look like a UK mobile number",
InvalidPhoneError.Codes.UNSUPPORTED_COUNTRY_CODE: "Country code for %s not found",
InvalidPhoneError.Codes.UNKNOWN_CHARACTER: "%s can only include: 0 1 2 3 4 5 6 7 8 9 ( ) + -",
InvalidPhoneError.Codes.INVALID_NUMBER: "%s is not valid – double check the phone number you entered",
}

def __call__(self, form, field):
try:
if field.data:
if self.allow_sms_to_uk_landlines:
number = PhoneNumber(field.data)
number.validate(
allow_international_number=self.allow_international_sms,
allow_uk_landline=self.allow_sms_to_uk_landlines,
)
else:
validate_phone_number(field.data, international=self.allow_international_sms)
number = PhoneNumber(field.data)
number.validate(
allow_international_number=self.allow_international_sms,
allow_uk_landline=self.allow_sms_to_uk_landlines,
)
except InvalidPhoneError as e:
error_message = str(e)
if hasattr(field, "error_summary_messages"):
Expand Down
2 changes: 1 addition & 1 deletion app/main/views/conversation.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from flask import jsonify, redirect, render_template, request, session, url_for
from flask_login import current_user
from notifications_python_client.errors import HTTPError
from notifications_utils.recipient_validation.phone_number import format_phone_number_human_readable
from notifications_utils.template import SMSPreviewTemplate

from app import current_service, notification_api_client, service_api_client
from app.formatters import format_phone_number_human_readable
from app.main import json_updates, main
from app.main.forms import SearchByNameForm
from app.models.notification import InboundSMSMessage, InboundSMSMessages, Notifications
Expand Down
3 changes: 1 addition & 2 deletions app/main/views/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from flask import Response, abort, jsonify, render_template, request, session, url_for
from flask_login import current_user
from notifications_utils.recipient_validation.phone_number import format_phone_number_human_readable
from werkzeug.utils import redirect

from app import (
Expand All @@ -17,7 +16,7 @@
)
from app.constants import MAX_NOTIFICATION_FOR_DOWNLOAD
from app.extensions import redis_client
from app.formatters import format_date_numeric, format_datetime_numeric
from app.formatters import format_date_numeric, format_datetime_numeric, format_phone_number_human_readable
from app.main import json_updates, main
from app.main.forms import SearchNotificationsForm
from app.models.notification import InboundSMSMessages, Notifications
Expand Down
7 changes: 1 addition & 6 deletions app/main/views/send.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,6 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
except HTTPError as e:
if e.status_code != 404:
raise

contents = s3download(service_id, upload_id)

template = current_service.get_template_with_user_permission_or_403(
Expand All @@ -625,7 +624,6 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
elif template.template_type == "sms":
template.sender = get_sms_sender_from_session()
template.show_sender = bool(template.sender)

recipients = RecipientCSV(
contents,
template=template,
Expand All @@ -642,8 +640,8 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
allow_international_sms=current_service.has_permission("international_sms"),
allow_sms_to_uk_landline=current_service.has_permission("sms_to_uk_landlines"),
allow_international_letters=current_service.has_permission("international_letters"),
should_validate_phone_number=False,
)

if request.args.get("from_test"):
# only happens if generating a letter preview test
back_link = url_for("main.send_one_off", service_id=service_id, template_id=template.id)
Expand All @@ -659,9 +657,7 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
template.values = recipients[preview_row - 2].recipient_and_personalisation
elif preview_row > 2:
abort(404)

original_file_name = get_csv_metadata(service_id, upload_id).get("original_file_name", "")

return {
"recipients": recipients,
"template": template,
Expand Down Expand Up @@ -699,7 +695,6 @@ def _check_messages(service_id, template_id, upload_id, preview_row):
def check_messages(service_id, template_id, upload_id, row_index=2):
data = _check_messages(service_id, template_id, upload_id, row_index)
data["allowed_file_extensions"] = Spreadsheet.ALLOWED_FILE_EXTENSIONS

if (
data["recipients"].too_many_rows
or not data["count_of_recipients"]
Expand Down
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ notifications-python-client==10.0.0
fido2==1.1.3

# Run `make bump-utils` to update to the latest version
notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@94.0.1
notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@95.0.0

govuk-frontend-jinja==3.4.0

Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,15 @@ mistune==0.8.4
# via notifications-utils
notifications-python-client==10.0.0
# via -r requirements.in
notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@d6311c3ab83b8225265277f781157c48f9318ae9
notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@e89dcc53c7f88ef23053d1efb2c00b8ef2ea377e
# via -r requirements.in
openpyxl==3.0.10
# via pyexcel-xlsx
ordered-set==4.1.0
# via notifications-utils
packaging==23.1
# via gunicorn
phonenumbers==8.13.52
phonenumbers==8.13.50
# via notifications-utils
pillow==10.3.0
# via -r requirements.in
Expand Down
6 changes: 4 additions & 2 deletions requirements_for_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ moto==4.0.9
# via -r requirements_for_test.in
notifications-python-client==10.0.0
# via -r requirements.txt
notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@d6311c3ab83b8225265277f781157c48f9318ae9
notifications-utils @ git+https://github.com/alphagov/notifications-utils.git@e89dcc53c7f88ef23053d1efb2c00b8ef2ea377e
# via -r requirements.txt
openpyxl==3.0.10
# via
Expand All @@ -184,7 +184,9 @@ packaging==23.1
# -r requirements.txt
# gunicorn
# pytest
phonenumbers==8.13.52
pathspec==0.12.1
# via black
phonenumbers==8.13.50
# via
# -r requirements.txt
# notifications-utils
Expand Down
2 changes: 1 addition & 1 deletion requirements_for_test_common.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This file was automatically copied from notifications-utils@94.0.1
# This file was automatically copied from notifications-utils@95.0.0

beautifulsoup4==4.12.3
pytest==8.3.4
Expand Down
2 changes: 1 addition & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# This file was automatically copied from notifications-utils@94.0.1
# This file was automatically copied from notifications-utils@95.0.0

exclude = [
"migrations/versions/",
Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ def inbound_sms_json():
(4, 5, "33(0)1 12345678"), # France
(5, 7, "1-202-555-0104"), # USA in one format
(6, 9, "+12025550104"), # USA in another format
(7, 9, "+68212345"), # Cook Islands
(7, 9, "+68223001"), # Cook Islands
)
],
}
Expand Down
15 changes: 14 additions & 1 deletion tests/app/main/forms/test_service_contact_details_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ def test_form_phone_number_allows_non_emergency_3_digit_numbers(notify_admin, sh

with notify_admin.test_request_context(method="POST", data=data):
form = ServiceContactDetailsForm()

if allowed:
assert form.validate_on_submit()
assert len(form.errors) == 0
Expand All @@ -122,3 +121,17 @@ def test_form_phone_number_allows_non_emergency_3_digit_numbers(notify_admin, sh
assert not form.validate_on_submit()
assert len(form.errors) == 1
assert form.errors["phone_number"] == ["Phone number cannot be an emergency number"]


@pytest.mark.parametrize(
"short_number, allowed",
(("01572 812241 7081", True),),
)
def test_form_phone_number_allows_non_emergency_numbers_with_extensions(notify_admin, short_number, allowed):
data = {"contact_details_type": "phone_number", "phone_number": short_number}

with notify_admin.test_request_context(method="POST", data=data):
form = ServiceContactDetailsForm()
assert form.validate_on_submit()
assert len(form.errors) == 0
assert form.errors == {}
2 changes: 1 addition & 1 deletion tests/app/main/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_invalid_list_of_white_list_email_domains(
def test_uk_mobile_number_validation_messages_match(mocker):
mock_field = _gen_mock_field("notanumber", error_summary_messages=[])
mocker.patch(
"app.main.validators.validate_phone_number",
"app.main.validators.PhoneNumber",
side_effect=InvalidPhoneError(code=InvalidPhoneError.Codes.UNKNOWN_CHARACTER),
)
with pytest.raises(ValidationError) as error:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4917,7 +4917,7 @@ def test_send_files_by_email_contact_details_prefills_the_form_with_the_existing
[
("url", "http://example.com/", "http://new-link.com/"),
("email_address", "[email protected]", "[email protected]"),
("phone_number", "0207 12345", "0207 56789"),
("phone_number", "020 3451 9002", "020 3451 9001"),
],
)
def test_send_files_by_email_contact_details_updates_contact_details_and_redirects_to_settings_page(
Expand Down
6 changes: 3 additions & 3 deletions tests/app/main/views/test_api_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,8 +558,8 @@ def test_should_update_guestlist(
data = {
"email_addresses-1": "[email protected]",
"email_addresses-3": "[email protected]",
"phone_numbers-0": "07900900000",
"phone_numbers-2": "+1800-555-555",
"phone_numbers-0": "07988057616",
"phone_numbers-2": "+1 202-555-0104",
}

client_request.post(
Expand All @@ -572,7 +572,7 @@ def test_should_update_guestlist(
SERVICE_ONE_ID,
{
"email_addresses": ["[email protected]", "[email protected]"],
"phone_numbers": ["07900900000", "+1800-555-555"],
"phone_numbers": ["07988057616", "+1 202-555-0104"],
},
)

Expand Down
2 changes: 1 addition & 1 deletion tests/app/main/views/test_code_not_received.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_should_render_correct_resend_template_for_pending_user(
"phone_number_to_register_with",
[
"+447700900460",
"+1800-555-555",
"+1 202-555-0104",
],
)
def test_should_resend_verify_code_and_update_mobile_for_pending_user(
Expand Down
4 changes: 2 additions & 2 deletions tests/app/main/views/test_conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ def test_get_user_phone_number_when_only_outbound_exists(notify_admin, mocker):
side_effect=HTTPError(response=Mock(status_code=404)),
)
mock_get_notification = mocker.patch(
"app.main.views.conversation.notification_api_client.get_notification", return_value={"to": "15550000000"}
"app.main.views.conversation.notification_api_client.get_notification", return_value={"to": "14157711401"}
)
assert get_user_number("service", "notification") == "+1 555-000-0000"
assert get_user_number("service", "notification") == "+1 415-771-1401"
mock_get_inbound_sms.assert_called_once_with("service", "notification")
mock_get_notification.assert_called_once_with("service", "notification")

Expand Down
4 changes: 2 additions & 2 deletions tests/app/main/views/test_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ def test_inbound_messages_shows_count_of_messages_when_there_are_no_messages(
"+33 1 12 34 56 78 message-5 5 hours ago",
"+1 202-555-0104 message-6 7 hours ago",
"+1 202-555-0104 message-7 9 hours ago",
"+682 12345 message-8 9 hours ago",
"+682 23 001 message-8 9 hours ago",
]
),
)
Expand Down Expand Up @@ -437,7 +437,7 @@ def test_download_inbox(
"+33 1 12 34 56 78,message-5,2016-07-01 08:59\r\n"
"+1 202-555-0104,message-6,2016-07-01 06:59\r\n"
"+1 202-555-0104,message-7,2016-07-01 04:59\r\n"
"+682 12345,message-8,2016-07-01 04:59\r\n"
"+682 23 001,message-8,2016-07-01 04:59\r\n"
)


Expand Down
2 changes: 1 addition & 1 deletion tests/app/main/views/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def test_logged_in_user_redirects_to_account(
"phone_number_to_register_with",
[
"+4407700900460",
"+1800-555-555",
"+1 202-555-0104",
],
)
@pytest.mark.parametrize(
Expand Down
Loading