From 19035817a703220b3aa6b5f320447c50a53fdbb7 Mon Sep 17 00:00:00 2001 From: Sylar Hamwi Date: Thu, 21 Nov 2024 16:05:19 +0300 Subject: [PATCH 1/5] feat: added phonenumber type validator to IsPhoneNumber.ts --- src/decorator/string/IsPhoneNumber.ts | 38 +++++++++++++++++---------- src/utils/phone-number-type.ts | 15 +++++++++++ 2 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 src/utils/phone-number-type.ts diff --git a/src/decorator/string/IsPhoneNumber.ts b/src/decorator/string/IsPhoneNumber.ts index 65744a99ab..118537c0f6 100644 --- a/src/decorator/string/IsPhoneNumber.ts +++ b/src/decorator/string/IsPhoneNumber.ts @@ -1,6 +1,12 @@ +import { PhoneNumberType } from '../../utils/phone-number-type'; import { ValidationOptions } from '../ValidationOptions'; import { buildMessage, ValidateBy } from '../common/ValidateBy'; -import { parsePhoneNumber, CountryCode } from 'libphonenumber-js/max'; + +/* Changed import to /max for all metadata provided in the library in order to check phone number type + * since the (default = /min) always returns undefined in the .getType() + * reference https://www.npmjs.com/package/libphonenumber-js + */ +import { parsePhoneNumberFromString, CountryCode } from 'libphonenumber-js/max'; export const IS_PHONE_NUMBER = 'isPhoneNumber'; @@ -10,26 +16,30 @@ export const IS_PHONE_NUMBER = 'isPhoneNumber'; * * @param value the potential phone number string to test * @param region 2 characters uppercase country code (e.g. DE, US, CH) for country specific validation. + * @param acceptedNumbersTypes list of accepted number types (MOBILE, PAGER, etc...) if not provided check only MOBILE. * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. */ -export function isPhoneNumber(value: string, region?: CountryCode): boolean { - if (typeof value !== 'string' || value.trim() !== value) { - return false; - } - +export function isPhoneNumber( + value: string, + region?: CountryCode, + acceptedNumbersTypes?: Array +): boolean { try { - const phoneNumber = parsePhoneNumber(value, region); + // the list of all phone number types that are the output of .getType() method + let checkedNumberTypes = ['MOBILE']; - /** - * We fail the validation if the user provided a region code - * and it doesn't match with the country code of the parsed number. - **/ - if (region && phoneNumber.country !== region) { - return false; + // Checking if accepted types array is passed to override the default MOBILE number type + if (acceptedNumbersTypes) { + checkedNumberTypes = acceptedNumbersTypes; } - return phoneNumber.isValid(); + const phoneNum = parsePhoneNumberFromString(value, region); + + // number must be valid and is one of the phone types the function accepts (ALL TYPES PROVIDED IN phone-number-types.ts) + const result: boolean = !!phoneNum?.isValid() && !!checkedNumberTypes.some(item => item === phoneNum?.getType()); + return !!result; } catch (error) { + // logging? return false; } } diff --git a/src/utils/phone-number-type.ts b/src/utils/phone-number-type.ts new file mode 100644 index 0000000000..75372c4428 --- /dev/null +++ b/src/utils/phone-number-type.ts @@ -0,0 +1,15 @@ +/* + types of all possible return values of the .getType() in the phone number libphonenumber-js/max library +*/ +export type PhoneNumberType = + | 'FIXED_LINE_OR_MOBILE' + | 'MOBILE' + | 'FIXED_LINE' + | 'PREMIUM_RATE' + | 'TOLL_FREE' + | 'SHARED_COST' + | 'VOIP' + | 'PERSONAL_NUMBER' + | 'PAGER' + | 'UAN' + | 'VOICEMAIL'; From dc810ebc39f62425c9b55e39256102b978846c9c Mon Sep 17 00:00:00 2001 From: Sylar Hamwi Date: Thu, 21 Nov 2024 16:31:35 +0300 Subject: [PATCH 2/5] feat(develop): added option to decorator --- src/decorator/string/IsPhoneNumber.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/decorator/string/IsPhoneNumber.ts b/src/decorator/string/IsPhoneNumber.ts index 118537c0f6..c54c9d1932 100644 --- a/src/decorator/string/IsPhoneNumber.ts +++ b/src/decorator/string/IsPhoneNumber.ts @@ -49,15 +49,18 @@ export function isPhoneNumber( * the intl. calling code, if the calling code wont be provided then the region must be set. * * @param region 2 characters uppercase country code (e.g. DE, US, CH) for country specific validation. + * @param acceptedNumbersTypes list of accepted number types (MOBILE, PAGER, etc...) if not provided check only MOBILE. * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. */ -export function IsPhoneNumber(region?: CountryCode, validationOptions?: ValidationOptions): PropertyDecorator { +export function IsPhoneNumber(region?: CountryCode, validationOptions?: ValidationOptions, + acceptedNumbersTypes?: Array +): PropertyDecorator { return ValidateBy( { name: IS_PHONE_NUMBER, constraints: [region], validator: { - validate: (value, args): boolean => isPhoneNumber(value, args?.constraints[0]), + validate: (value, args): boolean => isPhoneNumber(value, args?.constraints[0], acceptedNumbersTypes), defaultMessage: buildMessage( eachPrefix => eachPrefix + '$property must be a valid phone number', validationOptions From d042012cebcb8bac1c6648ccf60df7ecb25304d0 Mon Sep 17 00:00:00 2001 From: Sylar Hamwi Date: Thu, 21 Nov 2024 16:48:50 +0300 Subject: [PATCH 3/5] fix: accept all valid numbers --- src/decorator/string/IsPhoneNumber.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/decorator/string/IsPhoneNumber.ts b/src/decorator/string/IsPhoneNumber.ts index c54c9d1932..c762f4f25f 100644 --- a/src/decorator/string/IsPhoneNumber.ts +++ b/src/decorator/string/IsPhoneNumber.ts @@ -16,7 +16,7 @@ export const IS_PHONE_NUMBER = 'isPhoneNumber'; * * @param value the potential phone number string to test * @param region 2 characters uppercase country code (e.g. DE, US, CH) for country specific validation. - * @param acceptedNumbersTypes list of accepted number types (MOBILE, PAGER, etc...) if not provided check only MOBILE. + * @param acceptedNumbersTypes list of accepted number types (MOBILE, PAGER, etc...) if not provided then accept all valid numbers. * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. */ export function isPhoneNumber( @@ -26,9 +26,21 @@ export function isPhoneNumber( ): boolean { try { // the list of all phone number types that are the output of .getType() method - let checkedNumberTypes = ['MOBILE']; + let checkedNumberTypes = [ + 'FIXED_LINE_OR_MOBILE', + 'MOBILE', + 'FIXED_LINE', + 'PREMIUM_RATE', + 'TOLL_FREE', + 'SHARED_COST', + 'VOIP', + 'PERSONAL_NUMBER', + 'PAGER', + 'UAN', + 'VOICEMAIL', + ]; - // Checking if accepted types array is passed to override the default MOBILE number type + // Checking if accepted types array is passed to override the default if (acceptedNumbersTypes) { checkedNumberTypes = acceptedNumbersTypes; } @@ -37,7 +49,7 @@ export function isPhoneNumber( // number must be valid and is one of the phone types the function accepts (ALL TYPES PROVIDED IN phone-number-types.ts) const result: boolean = !!phoneNum?.isValid() && !!checkedNumberTypes.some(item => item === phoneNum?.getType()); - return !!result; + return result; } catch (error) { // logging? return false; @@ -49,7 +61,7 @@ export function isPhoneNumber( * the intl. calling code, if the calling code wont be provided then the region must be set. * * @param region 2 characters uppercase country code (e.g. DE, US, CH) for country specific validation. - * @param acceptedNumbersTypes list of accepted number types (MOBILE, PAGER, etc...) if not provided check only MOBILE. + * @param acceptedNumbersTypes list of accepted number types (MOBILE, PAGER, etc...) if not provided then accept all valid phone numbers * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. */ export function IsPhoneNumber(region?: CountryCode, validationOptions?: ValidationOptions, From c4fdf142e607d707fef098ac4fde19328fa6886c Mon Sep 17 00:00:00 2001 From: Sylar Hamwi Date: Thu, 21 Nov 2024 17:11:58 +0300 Subject: [PATCH 4/5] fix: added error message to validation --- src/decorator/string/IsPhoneNumber.ts | 33 +++++++++++++-------------- src/utils/phone-number-type.ts | 14 ++++++++++++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/decorator/string/IsPhoneNumber.ts b/src/decorator/string/IsPhoneNumber.ts index c762f4f25f..cdbefc3277 100644 --- a/src/decorator/string/IsPhoneNumber.ts +++ b/src/decorator/string/IsPhoneNumber.ts @@ -1,4 +1,4 @@ -import { PhoneNumberType } from '../../utils/phone-number-type'; +import { allPhoneNumberTypes, PhoneNumberType } from '../../utils/phone-number-type'; import { ValidationOptions } from '../ValidationOptions'; import { buildMessage, ValidateBy } from '../common/ValidateBy'; @@ -26,19 +26,7 @@ export function isPhoneNumber( ): boolean { try { // the list of all phone number types that are the output of .getType() method - let checkedNumberTypes = [ - 'FIXED_LINE_OR_MOBILE', - 'MOBILE', - 'FIXED_LINE', - 'PREMIUM_RATE', - 'TOLL_FREE', - 'SHARED_COST', - 'VOIP', - 'PERSONAL_NUMBER', - 'PAGER', - 'UAN', - 'VOICEMAIL', - ]; + let checkedNumberTypes: Array = allPhoneNumberTypes; // Checking if accepted types array is passed to override the default if (acceptedNumbersTypes) { @@ -64,17 +52,28 @@ export function isPhoneNumber( * @param acceptedNumbersTypes list of accepted number types (MOBILE, PAGER, etc...) if not provided then accept all valid phone numbers * If text doesn't start with the international calling code (e.g. +41), then you must set this parameter. */ -export function IsPhoneNumber(region?: CountryCode, validationOptions?: ValidationOptions, +export function IsPhoneNumber( + region?: CountryCode, + validationOptions?: ValidationOptions, acceptedNumbersTypes?: Array ): PropertyDecorator { + // the list of all phone number types that are the output of .getType() method + let checkedNumberTypes: Array = allPhoneNumberTypes; + + // Checking if accepted types array is passed to override the default + if (acceptedNumbersTypes) { + checkedNumberTypes = acceptedNumbersTypes; + } + return ValidateBy( { name: IS_PHONE_NUMBER, constraints: [region], validator: { - validate: (value, args): boolean => isPhoneNumber(value, args?.constraints[0], acceptedNumbersTypes), + validate: (value, args): boolean => isPhoneNumber(value, args?.constraints[0], checkedNumberTypes), defaultMessage: buildMessage( - eachPrefix => eachPrefix + '$property must be a valid phone number', + eachPrefix => + eachPrefix + '$property must be a valid phone number of the following types' + checkedNumberTypes, validationOptions ), }, diff --git a/src/utils/phone-number-type.ts b/src/utils/phone-number-type.ts index 75372c4428..30f64bad9b 100644 --- a/src/utils/phone-number-type.ts +++ b/src/utils/phone-number-type.ts @@ -13,3 +13,17 @@ export type PhoneNumberType = | 'PAGER' | 'UAN' | 'VOICEMAIL'; + + export const allPhoneNumberTypes:Array = [ + 'FIXED_LINE_OR_MOBILE', + 'MOBILE', + 'FIXED_LINE', + 'PREMIUM_RATE', + 'TOLL_FREE', + 'SHARED_COST', + 'VOIP', + 'PERSONAL_NUMBER', + 'PAGER', + 'UAN', + 'VOICEMAIL', + ]; \ No newline at end of file From 33d0c816919645a135736c5ef69c97fdf5ae3b7b Mon Sep 17 00:00:00 2001 From: Sylar Hamwi Date: Thu, 21 Nov 2024 21:12:57 +0300 Subject: [PATCH 5/5] fix: lint on error message log --- src/decorator/string/IsPhoneNumber.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/decorator/string/IsPhoneNumber.ts b/src/decorator/string/IsPhoneNumber.ts index cdbefc3277..ddb21b9314 100644 --- a/src/decorator/string/IsPhoneNumber.ts +++ b/src/decorator/string/IsPhoneNumber.ts @@ -73,7 +73,9 @@ export function IsPhoneNumber( validate: (value, args): boolean => isPhoneNumber(value, args?.constraints[0], checkedNumberTypes), defaultMessage: buildMessage( eachPrefix => - eachPrefix + '$property must be a valid phone number of the following types' + checkedNumberTypes, + eachPrefix + + '$property must be a valid phone number and of the following types: ' + + checkedNumberTypes.toString(), validationOptions ), },