[bisq-network/bisq] Validate phone numbers (#3134)

battleofwizards notifications at github.com
Fri Aug 23 21:12:21 UTC 2019


battleofwizards commented on this pull request.

Big plus for the test cases and meaningful names!

> +    @Getter
+    private final String callingCode;
+    /**
+     * The normalized (digits only) representation of an international calling code.
+     */
+    private final String normalizedCallingCode;
+    /**
+     * Phone number in E.164 format.
+     */
+    @Nullable
+    @Getter
+    private String normalizedPhoneNumber;
+
+    // Hide no-arg constructor
+    private PhoneNumberValidator() {
+        this.isoCountryCode = null;

These attributes are guaranteed null. Assigning null is superflous.

> +
+import java.util.Map;
+
+import static java.util.Map.entry;
+
+final class CountryCallingCodes {
+    /**
+     * Immutable mapping of ISO 3166 alpha-2 country code to national dialing number.
+     * <p>
+     * In some regions, such as American Samoa ('AS'), there is only one area code, and
+     * it is included in the mapping as part of the calling code.
+     *
+     * @see {@link <a href="https://en.wikipedia.org/wiki/E.164">https://en.wikipedia.org/wiki/E.164</a>}
+     * @see {@link <a href="https://en.wikipedia.org/wiki/List_of_country_calling_codes">https://en.wikipedia.org/wiki/List_of_country_calling_codes</a>}
+     */
+    private final Map<String, String> callingCodeMap = Map.ofEntries(

This should be static unless there is specific reason against.

> +     */
+    @Nullable
+    @Getter
+    private String normalizedPhoneNumber;
+
+    // Hide no-arg constructor
+    private PhoneNumberValidator() {
+        this.isoCountryCode = null;
+        this.countryCallingCodes = null;
+        this.callingCode = null;
+        this.normalizedCallingCode = null;
+    }
+
+    public PhoneNumberValidator(String isoCountryCode) {
+        this.isoCountryCode = isoCountryCode;
+        this.countryCallingCodes = new CountryCallingCodes();

Why do we keep an instance here? The CountryCallingCodes is essentially a global immutable resource.

> +
+    public PhoneNumberValidator(String isoCountryCode) {
+        this.isoCountryCode = isoCountryCode;
+        this.countryCallingCodes = new CountryCallingCodes();
+        this.callingCode = countryCallingCodes.getCallingCode(isoCountryCode);
+        this.normalizedCallingCode = countryCallingCodes.getNormalizedCallingCode(isoCountryCode);
+    }
+
+
+    @Override
+    public ValidationResult validate(String input) {
+        normalizedPhoneNumber = null;
+        ValidationResult result = super.validate(input);
+        if (!result.isValid) {
+            return result;
+        }

I might be wrong here but it feels like the dominating convention in the project is to skip `{}` for single statement blocks.

> +    public PhoneNumberValidator(String isoCountryCode) {
+        this.isoCountryCode = isoCountryCode;
+        this.countryCallingCodes = new CountryCallingCodes();
+        this.callingCode = countryCallingCodes.getCallingCode(isoCountryCode);
+        this.normalizedCallingCode = countryCallingCodes.getNormalizedCallingCode(isoCountryCode);
+    }
+
+
+    @Override
+    public ValidationResult validate(String input) {
+        normalizedPhoneNumber = null;
+        ValidationResult result = super.validate(input);
+        if (!result.isValid) {
+            return result;
+        }
+        final String trimmedInput = input.trim();

A matter of personal taste but I suggest instead of making local variables `final` make your methods very tiny. Then there is no need for the `final` keyword boilerplate.

> +import static org.junit.Assert.assertTrue;
+
+public class PhoneNumberValidatorTest {
+    private PhoneNumberValidator validator;
+    private ValidationResult validationResult;
+
+    @Before
+    public void setup() {
+        Res.setup();
+    }
+
+    @Test
+    public void testNoInput() {
+        validator = new PhoneNumberValidator("AT");
+        validationResult = validator.validate("");
+        assertFalse("'' should not be a valid number in AT", validationResult.isValid);

It really helps with test readability to define custom assertions. In this case `assertInvalid`.

> +        this.callingCode = countryCallingCodes.getCallingCode(isoCountryCode);
+        this.normalizedCallingCode = countryCallingCodes.getNormalizedCallingCode(isoCountryCode);
+    }
+
+
+    @Override
+    public ValidationResult validate(String input) {
+        normalizedPhoneNumber = null;
+        ValidationResult result = super.validate(input);
+        if (!result.isValid) {
+            return result;
+        }
+        final String trimmedInput = input.trim();
+        boolean isCountryDialingCodeExplicit = trimmedInput.startsWith("+");
+        // remove non-digit chars:  plus sign, dashes, parens, hashes, slashes and white spaces
+        String pureNumber = input.replaceAll("[\\+\\s+()\\-\\−\\#\\\\]", "");

Instead of removing specific chars remove everything that is not a digit: `^\\d` (regexp not tested).

> +    public ValidationResult validate(String input) {
+        normalizedPhoneNumber = null;
+        ValidationResult result = super.validate(input);
+        if (!result.isValid) {
+            return result;
+        }
+        final String trimmedInput = input.trim();
+        boolean isCountryDialingCodeExplicit = trimmedInput.startsWith("+");
+        // remove non-digit chars:  plus sign, dashes, parens, hashes, slashes and white spaces
+        String pureNumber = input.replaceAll("[\\+\\s+()\\-\\−\\#\\\\]", "");
+        boolean hasValidCallingCodePrefix = pureNumber.startsWith(normalizedCallingCode);
+        boolean isCountryCallingCodeImplicit = !isCountryDialingCodeExplicit && hasValidCallingCodePrefix;
+
+        result = validateIsNumeric(input, pureNumber)
+                .and(validateHasSufficientDigits(input, pureNumber))
+                .and(validateIsTooLong(input, pureNumber))

Maybe `validateIsNotTooLong`? Other validations are positive.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/bisq-network/bisq/pull/3134#pullrequestreview-279207090
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.bisq.network/pipermail/bisq-github/attachments/20190823/72ffda32/attachment.html>


More information about the bisq-github mailing list