Skip to content

Commit

Permalink
Adds utility validator: PhoneNumberValidator
Browse files Browse the repository at this point in the history
  • Loading branch information
rithviknishad committed Aug 3, 2023
1 parent 19b63d2 commit 0baa10f
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 29 deletions.
29 changes: 0 additions & 29 deletions care/facility/tests/test_phone_number_validator.py

This file was deleted.

54 changes: 54 additions & 0 deletions care/utils/models/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jsonschema
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.utils.deconstruct import deconstructible


Expand Down Expand Up @@ -41,3 +42,56 @@ def _extract_errors(

message = str(error).replace("\n\n", ": ").replace("\n", "")
container.append(ValidationError(message))


@deconstructible
class PhoneNumberValidator(RegexValidator):
"""
Any one of the specified types of phone numbers are considered valid.
Allowed types:
- `mobile` (Indian XOR International)
- `indian_mobile` (Indian only)
- `international_mobile` (International only)
- `landline` (Indian only)
- `support` (Indian only)
Example usage:
```
field = models.CharField(
validators=[PhoneNumberValidator(types=("mobile", "landline", "support"))])
)
```
"""

indian_mobile_number_regex = r"^(?=^\+91)(^\+91\d{10}$)"
international_mobile_number_regex = r"^(?!^\+91)(^\+\d{1,3}\d{8,14}$)"
landline_number_regex = r"^\+91[2-9]\d{7,9}$"
support_number_regex = r"^(1800|1860)\d{6,7}$"

regex_map = {
"indian_mobile": indian_mobile_number_regex,
"international_mobile": international_mobile_number_regex,
"mobile": rf"{indian_mobile_number_regex}|{international_mobile_number_regex}",
"landline": landline_number_regex,
"support": support_number_regex,
}

def __init__(self, types: Iterable[str], *args, **kwargs):
if not isinstance(types, Iterable) or isinstance(types, str) or len(types) == 0:
raise ValueError("The `types` argument must be a non-empty iterable.")

self.types = types
self.message = f"Invalid phone number. Must be one of the following types: {', '.join(self.types)}. Received: %(value)s"
self.code = "invalid_phone_number"

self.regex = r"|".join([self.regex_map[type] for type in self.types])
super().__init__(*args, **kwargs)

def __eq__(self, other):
return isinstance(other, PhoneNumberValidator) and self.types == other.types


mobile_validator = PhoneNumberValidator(types=("mobile",))
mobile_or_landline_number_validator = PhoneNumberValidator(types=("mobile", "landline"))
131 changes: 131 additions & 0 deletions care/utils/tests/test_phone_number_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
from django.core.exceptions import ValidationError
from django.test import TestCase

from care.utils.models.validators import PhoneNumberValidator


class PhoneNumberValidatorTests(TestCase):
mobile_validator = PhoneNumberValidator(types=("mobile",))
indian_mobile_validator = PhoneNumberValidator(types=("indian_mobile",))
international_mobile_validator = PhoneNumberValidator(
types=("international_mobile",)
)
landline_validator = PhoneNumberValidator(types=("landline",))
support_validator = PhoneNumberValidator(types=("support",))

valid_indian_mobile_numbers = [
"+919876543210",
]

valid_international_mobile_numbers = [
"+44712345678",
"+447123456789",
"+4471234567890",
"+44712345678901",
"+447123456789012",
"+4471234567890123",
"+44712345678901234",
]

valid_landline_numbers = [
"+914902626488",
]

valid_support_numbers = [
"1800123456",
"18001234567",
"1860123456",
"18601234567",
]

invalid_indian_mobile_numbers = [
*valid_support_numbers,
*valid_international_mobile_numbers,
"9876543210",
"98765432101",
"987654321a",
"+9198765432100",
"+91 9876543210",
"98765 43210",
"98765-43210",
]

invalid_international_mobile_numbers = [
*valid_support_numbers,
*valid_indian_mobile_numbers,
"4471234567",
"447123456789012345",
"+447123456789012345",
"+44 7123456789012345",
"4471234567890123456",
"+4471234567890123456",
"+44 71234567890123456",
]

invalid_landline_numbers = [
*valid_support_numbers,
"4902626488",
"02226543210",
"022 26543210",
"022-26543210",
]

invalid_support_numbers = [
*valid_indian_mobile_numbers,
*valid_international_mobile_numbers,
*valid_landline_numbers,
"180012345",
"180012345678",
"186012345",
"186012345678",
]

def test_valid_mobile_numbers(self):
for number in (
self.valid_indian_mobile_numbers + self.valid_international_mobile_numbers
):
self.assertIsNone(self.mobile_validator(number), msg=f"Failed for {number}")

def test_valid_indian_mobile_numbers(self):
for number in self.valid_indian_mobile_numbers:
self.assertIsNone(
self.indian_mobile_validator(number), msg=f"Failed for {number}"
)

def test_valid_international_mobile_numbers(self):
for number in self.valid_international_mobile_numbers:
self.assertIsNone(
self.international_mobile_validator(number), msg=f"Failed for {number}"
)

def test_valid_landline_numbers(self):
for number in self.valid_landline_numbers:
self.assertIsNone(
self.landline_validator(number), msg=f"Failed for {number}"
)

def test_valid_support_numbers(self):
for number in self.valid_support_numbers:
self.assertIsNone(
self.support_validator(number), msg=f"Failed for {number}"
)

def test_invalid_indian_mobile_numbers(self):
for number in self.invalid_indian_mobile_numbers:
with self.assertRaises(ValidationError, msg=f"Failed for {number}"):
self.indian_mobile_validator(number)

def test_invalid_international_mobile_numbers(self):
for number in self.invalid_international_mobile_numbers:
with self.assertRaises(ValidationError, msg=f"Failed for {number}"):
self.international_mobile_validator(number)

def test_invalid_landline_numbers(self):
for number in self.invalid_landline_numbers:
with self.assertRaises(ValidationError, msg=f"Failed for {number}"):
self.landline_validator(number)

def test_invalid_support_numbers(self):
for number in self.invalid_support_numbers:
with self.assertRaises(ValidationError, msg=f"Failed for {number}"):
self.support_validator(number)

0 comments on commit 0baa10f

Please sign in to comment.