From 098bae16fb3f68e5169414579806045d992d5ee0 Mon Sep 17 00:00:00 2001 From: Ballinette Date: Fri, 6 Oct 2023 17:48:58 +0200 Subject: [PATCH] Add IsIpRange validator (fix #727) --- README.md | 1 + src/decorator/decorators.ts | 1 + src/decorator/string/IsIPRange.ts | 35 ++++++++++ ...alidation-functions-and-decorators.spec.ts | 64 +++++++++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 src/decorator/string/IsIPRange.ts diff --git a/README.md b/README.md index 161f827a1d..4d1760dbba 100644 --- a/README.md +++ b/README.md @@ -860,6 +860,7 @@ isBoolean(value); | `@IsOctal()` | Checks if the string is a octal number. | | `@IsMACAddress(options?: IsMACAddressOptions)` | Checks if the string is a MAC Address. | | `@IsIP(version?: "4"\|"6")` | Checks if the string is an IP (version 4 or 6). | +| `@IsIPRange(version?: "4"\|"6")` | Checks if the string is an IP Range (version 4 or 6). | | `@IsPort()` | Checks if the string is a valid port number. | | `@IsISBN(version?: "10"\|"13")` | Checks if the string is an ISBN (version 10 or 13). | | `@IsEAN()` | Checks if the string is an if the string is an EAN (European Article Number). | diff --git a/src/decorator/decorators.ts b/src/decorator/decorators.ts index d449e9301a..329ce7c1be 100644 --- a/src/decorator/decorators.ts +++ b/src/decorator/decorators.ts @@ -64,6 +64,7 @@ export * from './string/IsHexColor'; export * from './string/IsHexadecimal'; export * from './string/IsMacAddress'; export * from './string/IsIP'; +export * from './string/IsIPRange'; export * from './string/IsPort'; export * from './string/IsISBN'; export * from './string/IsISIN'; diff --git a/src/decorator/string/IsIPRange.ts b/src/decorator/string/IsIPRange.ts new file mode 100644 index 0000000000..1439a6e74b --- /dev/null +++ b/src/decorator/string/IsIPRange.ts @@ -0,0 +1,35 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isIPRangeValidator from 'validator/lib/isIPRange'; + +export type IsIpRangeVersion = '4' | '6' | 4 | 6; + +export const IS_IP_RANGE = 'isIpRange'; + +/** + * Checks if the string is an IP Range (version 4 or 6). + * If given value is not a string, then it returns false. + */ +export function isIPRange(value: unknown, version?: IsIpRangeVersion): boolean { + /* eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion */ + const versionStr = version ? (`${version}` as '4' | '6') : undefined; + return typeof value === 'string' && isIPRangeValidator(value, versionStr); +} + +/** + * Checks if the string is an IP Range (version 4 or 6). + * If given value is not a string, then it returns false. + */ +export function IsIPRange(version?: IsIpRangeVersion, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_IP_RANGE, + constraints: [version], + validator: { + validate: (value, args): boolean => isIP(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be an ip range', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/test/functional/validation-functions-and-decorators.spec.ts b/test/functional/validation-functions-and-decorators.spec.ts index 5144a0db48..7f8cb49892 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -29,6 +29,7 @@ import { IsHexColor, IsHexadecimal, IsIP, + IsIPRange, IsISBN, IsISO8601, IsIn, @@ -108,6 +109,7 @@ import { isISBN, isISO8601, isIP, + isIPRange, isJSON, isJWT, isLowercase, @@ -2857,6 +2859,68 @@ describe('IsIP', () => { }); }); +describe('IsIPRange', () => { + const validValues = [ + '127.0.0.1/24', + '0.0.0.0/0', + '255.255.255.0/32', + '::/0', + '::/128', + '2001::/128', + '2001:800::/128', + '::ffff:127.0.0.1/128', + ]; + const invalidValues = [ + null, + undefined, + 'abc', + '127.200.230.1/35', + '127.200.230.1/-1', + '1.1.1.1/011', + '1.1.1/24.1', + '1.1.1.1/01', + '1.1.1.1/1.1', + '1.1.1.1/1.', + '1.1.1.1/1/1', + '1.1.1.1', + '::1', + '::1/164', + '2001::/240', + '2001::/-1', + '2001::/001', + '2001::/24.1', + '2001:db8:0000:1:1:1:1:1', + '::ffff:127.0.0.1', + ]; + + class MyClass { + @IsIP() + 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(isIPRange(value)).toBeTruthy()); + }); + + it('should fail if method in validator said that its invalid', () => { + invalidValues.forEach(value => expect(isIPRange(value)).toBeFalsy()); + }); + + it('should return error object with proper data', () => { + const validationType = 'isIpRange'; + const message = 'someProperty must be an ip range'; + return checkReturnedError(new MyClass(), invalidValues, validationType, message); + }); +}); + describe('IsISBN version 10', () => { const validValues = [ '3836221195',