Merge pull request #7252 from cparke2/phone-number-validator-required-length-option

New option in PhoneNumberValidator to enforce exact number of digits
This commit is contained in:
HenrikJannsen 2024-09-27 16:07:50 +07:00 committed by GitHub
commit f37344bdc6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 155 additions and 6 deletions

View File

@ -4624,6 +4624,7 @@ validation.phone.missingCountryCode=Need two letter country code to validate pho
validation.phone.invalidCharacters=Phone number {0} contains invalid characters
validation.phone.insufficientDigits=There are not enough digits in {0} to be a valid phone number
validation.phone.tooManyDigits=There are too many digits in {0} to be a valid phone number
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Country dialing code for number {0} is invalid for country {1}. \
The correct dialing code is {2}.
validation.invalidAddressList=Must be comma separated list of valid addresses

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=K ověření telefonního čísla je potřeb
validation.phone.invalidCharacters=Telefonní číslo {0} obsahuje neplatné znaky
validation.phone.insufficientDigits=V čísle {0} není dostatek číslic, aby mohlo být platné telefonní číslo
validation.phone.tooManyDigits=V čísle {0} je příliš mnoho číslic, než aby mohlo být platné telefonní číslo
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Telefonní předvolba země pro číslo {0} je pro zemi {1} neplatná. Správné předčíslí je {2}.
validation.invalidAddressList=Seznam platných adres musí být oddělený čárkami
validation.capitual.invalidFormat=Musíte zadat CAP kód ve formátu: CAP-XXXXXX (6 alfanumerických znaků)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Es wird eine zweistellige Ländervorwahl ben
validation.phone.invalidCharacters=Telefonnummer {0} enthält ungültige Zeichen
validation.phone.insufficientDigits=Das ist keine gültige Telefonnummer. Sie habe nicht genügend Stellen angegeben.
validation.phone.tooManyDigits=Es sind zu viele Ziffern in {0} um eine gültige Telefonnummer zu sein.
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Die Ländervorwahl in der Nummer {0} ist für das Land {1} ungültig. Die richtige Vorwahl ist {2}.
validation.invalidAddressList=Muss eine kommagetrennte Liste der gültigen Adressen sein
validation.capitual.invalidFormat=Sollte ein gültiger CAP Code im Format: CAP-XXXXXX (6 alphanumerische Charakter sein)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Se necesitan dos letras de código de país
validation.phone.invalidCharacters=Número de teléfono {0} tiene caracteres inválidos
validation.phone.insufficientDigits=No hay suficientes dígitos en {0} para ser un número válido de teléfono
validation.phone.tooManyDigits=Hay demasiados dígitos en {0} para ser un número de teléfono válido.
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=El código de país para el número {0} es inválido para el país {1}. El código de país correcto es {2}.
validation.invalidAddressList=La lista de direcciones válidas debe ser separada por coma
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Need two letter country code to validate pho
validation.phone.invalidCharacters=Phone number {0} contains invalid characters
validation.phone.insufficientDigits=There are not enough digits in {0} to be a valid phone number
validation.phone.tooManyDigits=There are too many digits in {0} to be a valid phone number
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Country dialing code for number {0} is invalid for country {1}. The correct dialing code is {2}.
validation.invalidAddressList=Must be comma separated list of valid addresses
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Un code pays à deux lettres est nécessaire
validation.phone.invalidCharacters=Le numéro de téléphone {0} contient des caractères invalides.
validation.phone.insufficientDigits=Il n'y a pas assez de chiffres dans {0} pour être un numéro de téléphone valide
validation.phone.tooManyDigits=Il y a trop de chiffres dans {0} pour être un numéro de téléphone valide
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=L'indicatif de pays du numéro {0} est invalide pour le pays {1}. Le bon indicatif est {2}.
validation.invalidAddressList=Doit être une liste dadresses valide séparée par des virgules
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=È necessario un codice paese di due lettere
validation.phone.invalidCharacters=Il numero di telefono {0} contiene caratteri non validi
validation.phone.insufficientDigits=There are not enough digits in {0} to be a valid phone number
validation.phone.tooManyDigits=There are too many digits in {0} to be a valid phone number
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Country dialing code for number {0} is invalid for country {1}. The correct dialing code is {2}.
validation.invalidAddressList=Deve essere un elenco separato da virgole di indirizzi validi
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=電話番号を検証するのに2文字国
validation.phone.invalidCharacters=電話番号 {0} には無効な文字が含まれている
validation.phone.insufficientDigits={0} には桁数が不十分で有効電話番号になりません
validation.phone.tooManyDigits={0} には桁数が多過ぎて有効電話番号になりません
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=電話番号 {0} の国番号は国の {1} にとって間違っています。正しい国番号は {2} です。
validation.invalidAddressList=有効アドレスのコンマ区切りリストでなければなりません
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Potrzeba dwuliterowego kodu krajowego aby zw
validation.phone.invalidCharacters=Numer telefonu {0} zawiera nieprawidłowe znaki
validation.phone.insufficientDigits=Nie ma wystarczająco dużo cyfr w {0} aby to był właściwy numer telefonu
validation.phone.tooManyDigits=Jest zbyt dużo cyfr w {0} aby to był właściwy numer telefonu
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Numer kierunkowy dla {0} jest nieprawidłowy dla kraju {1}. Prawidłowy numer kierunkowy to {2}.
validation.invalidAddressList=Musi być przecinek oddzielony listą poprawnych adresów
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Precisa do código do país com duas letras
validation.phone.invalidCharacters=O número de telefone {0} contém caracteres inválidos.
validation.phone.insufficientDigits=Não há dígitos suficientes em {0} para ser um número de telefone válido.
validation.phone.tooManyDigits=Há muitos dígitos em {0} para ser um número de telefone válido.
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=O código de discagem do país no número {0} é inválido para o país {1}. O código de discagem correto é {2}.
validation.invalidAddressList=Precisa ser uma lista delimitada por vírgulas de endereços válidos
validation.capitual.invalidFormat=Deve ser um código CAP válido no formato: CAP-XXXXXX (6 caracteres alfanuméricos)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=É preciso o código do país de duas letras
validation.phone.invalidCharacters=O número de telfone {0} contém carácteres inválidos
validation.phone.insufficientDigits=There are not enough digits in {0} to be a valid phone number
validation.phone.tooManyDigits=There are too many digits in {0} to be a valid phone number
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Country dialing code for number {0} is invalid for country {1}. The correct dialing code is {2}.
validation.invalidAddressList=Deve ser um lista de endereços válidos separados por vírgulas
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Need two letter country code to validate pho
validation.phone.invalidCharacters=Phone number {0} contains invalid characters
validation.phone.insufficientDigits=There are not enough digits in {0} to be a valid phone number
validation.phone.tooManyDigits=There are too many digits in {0} to be a valid phone number
validation.phone.incorrectLength=Поле должно содержать {0} символов
validation.phone.invalidDialingCode=Country dialing code for number {0} is invalid for country {1}. The correct dialing code is {2}.
validation.invalidAddressList=Must be comma separated list of valid addresses
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Need two letter country code to validate pho
validation.phone.invalidCharacters=Phone number {0} contains invalid characters
validation.phone.insufficientDigits=There are not enough digits in {0} to be a valid phone number
validation.phone.tooManyDigits=There are too many digits in {0} to be a valid phone number
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Country dialing code for number {0} is invalid for country {1}. The correct dialing code is {2}.
validation.invalidAddressList=Must be comma separated list of valid addresses
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=Need two letter country code to validate pho
validation.phone.invalidCharacters=Phone number {0} contains invalid characters
validation.phone.insufficientDigits=There are not enough digits in {0} to be a valid phone number
validation.phone.tooManyDigits=There are too many digits in {0} to be a valid phone number
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=Country dialing code for number {0} is invalid for country {1}. The correct dialing code is {2}.
validation.invalidAddressList=Must be comma separated list of valid addresses
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=需要两个字母的国家或地区代码
validation.phone.invalidCharacters=电话号码 {0} 包含无效字符
validation.phone.insufficientDigits={0} 中没有足够的数字作为有效的电话号码
validation.phone.tooManyDigits={0} 中的数字太多,不是有效的电话号码
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=数字 {0} 中的国际拨号代码对于 {1} 无效。正确的拨号号码是 {2} 。
validation.invalidAddressList=使用逗号分隔有效地址列表
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -3535,6 +3535,7 @@ validation.phone.missingCountryCode=需要兩個字母的國家或地區代碼
validation.phone.invalidCharacters=電話號碼 {0} 包含無效字符
validation.phone.insufficientDigits={0} 中沒有足夠的數字作為有效的電話號碼
validation.phone.tooManyDigits={0} 中的數字太多,不是有效的電話號碼
validation.phone.incorrectLength=The field must contain {0} numbers
validation.phone.invalidDialingCode=數字 {0} 中的國際撥號代碼對於 {1} 無效。正確的撥號號碼是 {2} 。
validation.invalidAddressList=使用逗號分隔有效地址列表
validation.capitual.invalidFormat=Must be a valid CAP code of format: CAP-XXXXXX (6 alphanumeric characters)

View File

@ -0,0 +1,47 @@
package bisq.desktop.util.validation;
import java.util.Map;
import static java.util.Map.entry;
final class PhoneNumberRequiredLengths {
/**
* Immutable mapping of ISO 3166 alpha-2 country code to the required length of the phone number (in digits, exluding country code prefix).
*
* Used by PhoneNumberValidator.java
* If an entry is not present in the map, the requiredLength option will not be used in the validation.
*/
private final static Map<String, Integer> NUMBER_LENGTH_MAP = Map.ofEntries(
entry("AG", 7 ), // CountryCode: "1-268"
entry("AI", 7 ), // CountryCode: "1-264"
entry("AS", 7 ), // CountryCode: "1-684"
entry("BB", 7 ), // CountryCode: "1-246",
entry("BM", 7 ), // CountryCode: "1-441"
entry("BS", 7 ), // CountryCode: "1-242"
entry("CA", 10), // CountryCode: "1"
entry("DM", 7 ), // CountryCode: "1-767"
entry("DO", 10), // CountryCode: "1" (DO has three area codes 809,829,849; let user define hers)
entry("GD", 7 ), // CountryCode: "1-473"
entry("GU", 7 ), // CountryCode: "1-671"
entry("JM", 7 ), // CountryCode: "1-876"
entry("KN", 7 ), // CountryCode: "1-869"
entry("KY", 7 ), // CountryCode: "1-345"
entry("KZ", 10), // CountryCode: "7"
entry("LC", 7 ), // CountryCode: "1-758"
entry("MP", 7 ), // CountryCode: "1-670"
entry("MS", 7 ), // CountryCode: "1-664"
entry("PR", 10), // CountryCode: "1"
entry("RU", 10), // CountryCode: "7"
entry("SX", 7 ), // CountryCode: "1-721"
entry("TC", 7 ), // CountryCode: "1-649"
entry("TT", 7 ), // CountryCode: "1-868"
entry("US", 10), // CountryCode: "1"
entry("VC", 7 ), // CountryCode: "1-784"
entry("VG", 7 ), // CountryCode: "1-284"
entry("VI", 7 ) // CountryCode: "1-340"
);
static Integer getRequiredLength(String isoCountryCode) {
return NUMBER_LENGTH_MAP.get(isoCountryCode);
}
}

View File

@ -7,6 +7,8 @@ import lombok.Getter;
import javax.annotation.Nullable;
import java.util.Optional;
/**
* Performs lenient validation of international phone numbers, and transforms given
* input numbers into E.164 international form. The E.164 normalized phone number
@ -40,6 +42,11 @@ public class PhoneNumberValidator extends InputValidator {
@Nullable
@Getter
private String normalizedPhoneNumber;
/**
* Required length of phone number (excluding country code).
*/
@Getter
private Optional<Integer> requiredLength;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructors
@ -49,11 +56,12 @@ public class PhoneNumberValidator extends InputValidator {
// but isoCountryCode must be set before validation.
public PhoneNumberValidator() {
}
public PhoneNumberValidator(String isoCountryCode) {
this.isoCountryCode = isoCountryCode;
this.callingCode = CountryCallingCodes.getCallingCode(isoCountryCode);
this.normalizedCallingCode = CountryCallingCodes.getNormalizedCallingCode(isoCountryCode);
this.requiredLength = Optional.ofNullable(PhoneNumberRequiredLengths.getRequiredLength(isoCountryCode));
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -95,6 +103,14 @@ public class PhoneNumberValidator extends InputValidator {
} else {
normalizedPhoneNumber = "+" + getCallingCode() + pureNumber;
}
// If the 'requiredLength' was set, there's still one more check to apply on the normalizedNumber itself
if (requiredLength.isPresent()) {
result = validateRequiredLength(normalizedPhoneNumber, normalizedCallingCode);
if (!result.isValid) {
normalizedPhoneNumber = null;
}
}
}
return result;
}
@ -161,4 +177,14 @@ public class PhoneNumberValidator extends InputValidator {
return new ValidationResult(false, Res.get("validation.invalidInput", t.getMessage()));
}
}
private ValidationResult validateRequiredLength(String normalizedPhoneNumber, String normalizedCallingCode) {
try {
return ((normalizedPhoneNumber.length() - normalizedCallingCode.length() - 1) == requiredLength.get())
? new ValidationResult(true)
: new ValidationResult(false, Res.get("validation.phone.incorrectLength", requiredLength.get()));
} catch (Throwable t) {
return new ValidationResult(false, Res.get("validation.invalidInput", t.getMessage()));
}
}
}

View File

@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class PhoneNumberValidatorTest {
@ -284,16 +285,75 @@ public class PhoneNumberValidatorTest {
validator = new PhoneNumberValidator("US");
assertEquals(validator.getCallingCode(), "1");
validationResult = validator.validate("1 (1) 253 0000");
assertTrue(validationResult.isValid);
assertEquals("+112530000", validator.getNormalizedPhoneNumber());
assertFalse(validationResult.isValid);
assertNull(validator.getNormalizedPhoneNumber());
validationResult = validator.validate("1-120-253 0000");
assertTrue(validationResult.isValid);
assertNull(validationResult.errorMessage);
assertEquals("+11202530000", validator.getNormalizedPhoneNumber());
validationResult = validator.validate("(120) 253 0000");
assertTrue(validationResult.isValid);
// TODO validator incorrectly treats input as if it were +1 (202) 53-0000
/// assertEquals("+1202530000", validator.getNormalizedPhoneNumber());
assertFalse(validationResult.isValid);
assertNull(validator.getNormalizedPhoneNumber());
}
@Test
public void testPhoneNumberLength() {
// Construct the validator to be used in these test cases
validator = new PhoneNumberValidator("US"); // requiredLength=10 to pass these tests
// Most important, does the validator work for correct phone numbers?
validationResult = validator.validate("+1 512 888 0150");
assertTrue(validationResult.isValid, "+1 512 888 0150 should be a valid number in US");
assertNull(validationResult.errorMessage);
assertNotNull(validator.getNormalizedPhoneNumber());
assertEquals(validator.getNormalizedPhoneNumber(), "+15128880150");
// If no country code provided by user, normalized number should have it added
validationResult = validator.validate("5128880150");
assertTrue(validationResult.isValid, "5128880150 should be a valid number in US");
assertNull(validationResult.errorMessage);
assertNotNull(validator.getNormalizedPhoneNumber());
assertEquals(validator.getNormalizedPhoneNumber(), "+15128880150");
// If no phone number too short, there's a message for that
validationResult = validator.validate("+15121");
assertFalse(validationResult.isValid, "+15121 should be too short");
assertNull(validator.getNormalizedPhoneNumber());
assertEquals(Res.get("validation.phone.insufficientDigits", "+15121"), validationResult.errorMessage);
// If no phone number too long, there's a message for that
validationResult = validator.validate("51288801505128880150");
assertFalse(validationResult.isValid, "51288801505128880150 should be too long");
assertNull(validator.getNormalizedPhoneNumber());
assertEquals(Res.get("validation.phone.tooManyDigits", "51288801505128880150"), validationResult.errorMessage);
// If phone number not exactly the requiredLength, there's a message for that too
validationResult = validator.validate("+1 888 123 456");
assertFalse(validationResult.isValid, "+1 888 123 456 should not be a valid number in US");
assertNull(validator.getNormalizedPhoneNumber());
assertEquals(Res.get("validation.phone.incorrectLength", validator.getRequiredLength().get()), validationResult.errorMessage);
}
@Test
public void testVariablePhoneNumberLengthCountry() {
// Construct the validator to be used in these test cases
validator = new PhoneNumberValidator("KP"); // requiredLength not defined for this country (North Korea +850)
assertFalse(validator.getRequiredLength().isPresent()); // If this fails, find another country to test with
// If phone number requiredLength is not defined, it is considered okay if length in the range 4-12
validationResult = validator.validate("12345678");
assertTrue(validationResult.isValid, "12345678 should be a considered a valid number");
assertNull(validationResult.errorMessage);
assertNotNull(validator.getNormalizedPhoneNumber());
assertEquals(validator.getNormalizedPhoneNumber(), "+85012345678");
validationResult = validator.validate("12345678901234");
assertTrue(validationResult.isValid, "12345678901234 should be a considered a valid number");
assertNull(validationResult.errorMessage);
assertNotNull(validator.getNormalizedPhoneNumber());
assertEquals(validator.getNormalizedPhoneNumber(), "+85012345678901234");
}
}