api: update phone drf field to handle E164 format (#69430)
gitea/authentic/pipeline/head Build started... Details

This commit is contained in:
Paul Marillonnet 2022-09-22 12:10:40 +02:00
parent a179cb1dc9
commit b3036b4cc0
3 changed files with 66 additions and 7 deletions

View File

@ -24,6 +24,7 @@ import string
import uuid
from itertools import chain
import phonenumbers
from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
@ -146,9 +147,24 @@ def get_title_choices():
return app_settings.A2_ATTRIBUTE_KIND_TITLE_CHOICES or DEFAULT_TITLE_CHOICES
validate_phone_number = RegexValidator(
r'^\+?\d{,20}$', message=_('Phone number can start with a + and must contain only digits.')
)
def validate_phone_number(value):
default_country = settings.PHONE_COUNTRY_CODES[settings.DEFAULT_COUNTRY_CODE]['lang']
try:
phonenumbers.parse(value)
except phonenumbers.NumberParseException:
try:
phonenumbers.parse(
value,
default_country,
)
except phonenumbers.NumberParseException:
raise ValidationError(
_(
'Phone number must be either in E.164 globally unique format or dialable from'
' {code} country code ({country}).'
).format(code=settings.DEFAULT_COUNTRY_CODE, country=default_country)
)
french_validate_phone_number = RegexValidator(
r'^[0][0-9]{9}$', message=_('A french phone number must start with a zero then another nine digits.')
@ -180,12 +196,23 @@ class PhoneNumberDRFField(serializers.CharField):
default_validators = [validate_phone_number]
def to_internal_value(self, data):
return clean_number(super().to_internal_value(data))
if isinstance(data, (list, tuple)):
data = data[0]
data = super().to_internal_value(clean_number(data))
default_country = settings.PHONE_COUNTRY_CODES[settings.DEFAULT_COUNTRY_CODE]['lang']
try:
pn = phonenumbers.parse(data)
except phonenumbers.NumberParseException:
pn = phonenumbers.parse(data, default_country)
return phonenumbers.format_number(pn, phonenumbers.PhoneNumberFormat.E164)
class FrenchPhoneNumberDRFField(PhoneNumberDRFField):
class FrenchPhoneNumberDRFField(serializers.CharField):
default_validators = [french_validate_phone_number]
def to_internal_value(self, data):
return super().to_internal_value(clean_number(data))
validate_fr_postcode = RegexValidator(r'^\d{5}$', message=_('The value must be a valid french postcode'))

View File

@ -2267,7 +2267,29 @@ def test_phone_normalization_ok(settings, app, admin):
}
resp = app.post_json('/api/users/', headers=headers, params=payload, status=201)
assert resp.json['extra_phone'] == '+33499985643'
assert User.objects.get(username='janedoe').attributes.extra_phone == '+33499985643'
user = User.objects.get(username='janedoe')
assert user.attributes.extra_phone == '+33499985643'
user.delete()
payload['extra_phone'] = ' + 334-99 98.56/433 '
resp = app.post_json('/api/users/', headers=headers, params=payload, status=201)
assert resp.json['extra_phone'] == '+334999856433'
user = User.objects.get(username='janedoe')
assert user.attributes.extra_phone == '+334999856433'
user.delete()
payload['extra_phone'] = ' 04-99 98.56/433 '
resp = app.post_json('/api/users/', headers=headers, params=payload, status=201)
assert resp.json['extra_phone'] == '+334999856433'
user = User.objects.get(username='janedoe')
assert user.attributes.extra_phone == '+334999856433'
user.delete()
payload['extra_phone'] = ''
resp = app.post_json('/api/users/', headers=headers, params=payload, status=201)
assert resp.json['extra_phone'] == ''
user = User.objects.get(username='janedoe')
assert user.attributes.extra_phone == ''
def test_phone_normalization_nok(settings, app, admin):
@ -2284,6 +2306,15 @@ def test_phone_normalization_nok(settings, app, admin):
payload['extra_phone'] = '1#2'
app.post_json('/api/users/', headers=headers, params=payload, status=400)
payload['extra_phone'] = '+33499985643343434343'
app.post_json('/api/users/', headers=headers, params=payload, status=400)
payload['extra_phone'] = '+334-99 98\\56/43'
app.post_json('/api/users/', headers=headers, params=payload, status=400)
payload['extra_phone'] = '+334'
app.post_json('/api/users/', headers=headers, params=payload, status=400)
def test_fr_phone_normalization_ok(settings, app, admin):
headers = basic_authorization_header(admin)

View File

@ -56,7 +56,8 @@ def test_phone_number_change_invalid_number(settings, app, simple_user):
resp = resp.form.submit()
assert (
'Phone number can start with a + and must contain only digits.' in resp.pyquery('div.error')[0].text
'Phone number must be either in E.164 globally unique format or dialable from'
in resp.pyquery('div.error')[0].text
)
resp.form['mobile_1'] = '612345678'