diff --git a/README.md b/README.md index 85ef7b2fd9..c2434b069e 100644 --- a/README.md +++ b/README.md @@ -883,6 +883,7 @@ isBoolean(value); | `@IsMultibyte()` | Checks if the string contains one or more multibyte chars. | | `@IsNumberString(options?: IsNumericOptions)` | Checks if the string is numeric. | | `@IsSurrogatePair()` | Checks if the string contains any surrogate pairs chars. | +| `@IsTaxId()` | Checks if the string is a valid tax ID. Default locale is `en-US`. | `@IsUrl(options?: IsURLOptions)` | Checks if the string is a URL. | | `@IsMagnetURI()` | Checks if the string is a [magnet uri format](https://en.wikipedia.org/wiki/Magnet_URI_scheme). | | `@IsUUID(version?: "3"\|"4"\|"5"\|"all")` | Checks if the string is a UUID (version 3, 4, 5 or all ). | diff --git a/src/decorator/decorators.ts b/src/decorator/decorators.ts index 542c9cbb47..1ec8c455da 100644 --- a/src/decorator/decorators.ts +++ b/src/decorator/decorators.ts @@ -114,6 +114,7 @@ export * from './string/IsSemVer'; export * from './string/IsStrongPassword'; export * from './string/IsTimeZone'; export * from './string/IsBase58'; +export * from './string/is-tax-id'; // ------------------------------------------------------------------------- // Type checkers diff --git a/src/decorator/string/is-tax-id.ts b/src/decorator/string/is-tax-id.ts new file mode 100644 index 0000000000..950852f190 --- /dev/null +++ b/src/decorator/string/is-tax-id.ts @@ -0,0 +1,42 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isTaxIDValidator from 'validator/lib/isTaxID'; + +export const IS_TAX_ID = 'isTaxId'; + +/** + * Checks if the string is a valid tax ID. Default locale is `en-US`. + * If given value is not a string, then it returns false. + * + * Supported locales: bg-BG, cs-CZ, de-AT, de-DE, dk-DK, el-CY, el-GR, en-CA, + * en-IE, en-US, es-ES, et-EE, fi-FI, fr-BE, fr-FR, fr-LU, hr-HR, hu-HU, it-IT, + * lv-LV, mt-MT, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, sk-SK, sl-SI, sv-SE. + */ +export function isTaxId(value: unknown, locale?: string): boolean { + return typeof value === 'string' && isTaxIDValidator(value, locale || 'en-US'); +} + +/** + * Checks if the string is a valid tax ID. Default locale is `en-US`. + * If given value is not a string, then it returns false. + * + * Supported locales: bg-BG, cs-CZ, de-AT, de-DE, dk-DK, el-CY, el-GR, en-CA, + * en-IE, en-US, es-ES, et-EE, fi-FI, fr-BE, fr-FR, fr-LU, hr-HR, hu-HU, it-IT, + * lv-LV, mt-MT, nl-NL, pl-PL, pt-BR, pt-PT, ro-RO, sk-SK, sl-SI, sv-SE. + */ +export function IsTaxId(locale?: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_TAX_ID, + constraints: [locale], + validator: { + validate: (value, args): boolean => isTaxId(value, args?.constraints[0]), + defaultMessage: buildMessage( + eachPrefix => eachPrefix + '$property must be a Tax Identification Number', + validationOptions + ), + }, + }, + validationOptions + ); +} diff --git a/test/functional/validation-functions-and-decorators.spec.ts b/test/functional/validation-functions-and-decorators.spec.ts index c2826dd91e..a43cfd8c93 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -190,6 +190,8 @@ import { IsTimeZone, IsBase58, isBase58, + isTaxId, + IsTaxId, } from '../../src/decorator/decorators'; import { Validator } from '../../src/validation/Validator'; import { ValidatorOptions } from '../../src/validation/ValidatorOptions'; @@ -2304,6 +2306,39 @@ describe('IsByteLength', () => { }); }); +describe('IsTaxId', () => { + const constraint = 'bg-BG'; + const validValues = ['7501010010', '0101010012', '0111010010', '7521010014', '7541010019']; + const invalidValues = [null, undefined, '750101001', '75010100101', '75-01010/01 0', '7521320010', '7501010019']; + + class MyClass { + @IsTaxId(constraint) + someProperty: string; + } + + it('should not fail if validator.validate said that its valid', () => { + return checkValidValues(new MyClass(), validValues); + }); + + it('should fail if validator.validate said that its invalid', () => { + return checkInvalidValues(new MyClass(), invalidValues); + }); + + it('should not fail if method in validator said that its valid', () => { + validValues.forEach(value => expect(isTaxId(value, constraint)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isTaxId(value, constraint)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isTaxId'; + const message = 'someProperty must be a Tax Identification Number'; + checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); + describe('IsCreditCard', () => { const validValues = [ '375556917985515',