Skip to content

Commit

Permalink
Merge pull request #17 from SeanLuis/features/added-new-validations-c…
Browse files Browse the repository at this point in the history
…ontains-and-alpha

feat(validation): implement Contains & Alpha decorator and validator
  • Loading branch information
SeanLuis authored Jun 30, 2024
2 parents 2ff01b0 + 1779c45 commit d409753
Show file tree
Hide file tree
Showing 17 changed files with 740 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/decorators/AlphaValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "reflect-metadata";
import { IAlphaValidationOptions } from '../interfaces';
import { addValidationMetadata } from "../metadata/AddValidationMetadata";
import { IValidationGroupOptions } from "../interfaces/IValidationGroupOptions";

/**
* Decorator function for validating if a string contains only letters.
* @param options - The options for alpha validation.
* @param groups - The groups options.
* @returns A decorator function that can be used to apply alpha validation to a property.
*/
export function Alpha(options: IAlphaValidationOptions, groups: IValidationGroupOptions = {}) {
return function(target: any, propertyName: string | symbol) {
addValidationMetadata(target, propertyName, { type: 'alpha', options, groups });
};
}
16 changes: 16 additions & 0 deletions src/decorators/ContainsValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import "reflect-metadata";
import { IContainsValidationOptions } from '../interfaces';
import { addValidationMetadata } from "../metadata/AddValidationMetadata";
import { IValidationGroupOptions } from "../interfaces";

/**
* Decorator function for validating if a string contains a specific seed.
* @param options - The options for contains validation.
* @param groups - The groups options.
* @returns A decorator function that can be used to apply contains validation to a property.
*/
export function Contains(options: IContainsValidationOptions, groups: IValidationGroupOptions = {}) {
return function(target: any, propertyName: string | symbol) {
addValidationMetadata(target, propertyName, { type: 'contains', options, groups });
};
}
2 changes: 2 additions & 0 deletions src/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export { Dependency } from "./DependencyValidator";
export { Accessors, Getter, Setter } from "./accessors";
export { Password } from "./PasswordValidator";
export { Security } from "./SecurityValidator";
export { Contains } from "./ContainsValidator";
export { Alpha } from "./AlphaValidator"
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
* @module Interfaces
*/
export {
IAlphaValidationOptions,
IArrayValidationOptions,
IChainValidationOptions,
IContainsValidationOptions,
IContextualValidationOptions,
ICustomValidationOptions,
IDateValidationOptions,
Expand Down Expand Up @@ -51,6 +53,8 @@ export {
validatePassword,
validateDependency,
validateSecurity,
validateContains,
validateAlpha
} from "./validators";

/**
Expand Down Expand Up @@ -79,6 +83,8 @@ export {
Setter,
Dependency,
Security,
Contains,
Alpha
} from "./decorators";

/**
Expand Down
13 changes: 13 additions & 0 deletions src/interfaces/IAlphaValidationOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IValidationOptionsBase } from "./IValidationOptionsBase";

/**
* The IAlphaValidationOptions interface represents the options for validating if a string contains only letters.
*
* @interface
* @property {string} [locale='en-US'] - Optional: The locale to use for validation. Defaults to 'en-US'.
* @property {string | RegExp} [ignore] - Optional: Characters to ignore during validation.
*/
export interface IAlphaValidationOptions extends IValidationOptionsBase {
locale?: string;
ignore?: string | RegExp;
}
15 changes: 15 additions & 0 deletions src/interfaces/IContainsValidationOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IValidationOptionsBase } from "./IValidationOptionsBase";

/**
* The IContainsValidationOptions interface represents the options for validating if a string contains a specific seed.
*
* @interface
* @property {string} seed - The seed string to look for within the main string. This is a required property.
* @property {boolean} [ignoreCase=false] - Optional: Whether to ignore case when comparing. Defaults to false.
* @property {number} [minOccurrences=1] - Optional: The minimum number of occurrences of the seed in the string. Defaults to 1.
*/
export interface IContainsValidationOptions extends IValidationOptionsBase {
seed: string;
ignoreCase?: boolean;
minOccurrences?: number;
}
2 changes: 2 additions & 0 deletions src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
export { IAlphaValidationOptions } from './IAlphaValidationOptions';
export { IArrayValidationOptions } from './IArrayValidationOptions';
export { IValidationGroupOptions } from './IValidationGroupOptions';
export { IChainValidationOptions } from './IChainValidationOptions';
export { IContainsValidationOptions } from './IContainsValidationOptions';
export { IContextualValidationOptions } from './IContextualValidationOptions';
export { ICustomValidationOptions } from './ICustomValidationOptions';
export { IDateValidationOptions } from './IDateValidationOptions';
Expand Down
218 changes: 218 additions & 0 deletions src/utils/common/Alpha.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/**
* Regular expressions for alpha validation by locale.
* Taken from: https://github.com/validatorjs/validator.js/blob/master/src/lib/alpha.js
*/
export const alpha: { [key: string]: RegExp } = {
"en-US": /^[A-Z]+$/i,
"az-AZ": /^[A-VXYZÇƏĞİıÖŞÜ]+$/i,
"bg-BG": /^[А-Я]+$/i,
"cs-CZ": /^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
"da-DK": /^[A-ZÆØÅ]+$/i,
"de-DE": /^[A-ZÄÖÜß]+$/i,
"el-GR": /^[Α-ώ]+$/i,
"es-ES": /^[A-ZÁÉÍÑÓÚÜ]+$/i,
"fa-IR": /^[ابپتثجچحخدذرزژسشصضطظعغفقکگلمنوهی]+$/i,
"fi-FI": /^[A-ZÅÄÖ]+$/i,
"fr-FR": /^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
"it-IT": /^[A-ZÀÉÈÌÎÓÒÙ]+$/i,
"ja-JP": /^[ぁ-んァ-ヶヲ-゚一-龠ー・。、]+$/i,
"nb-NO": /^[A-ZÆØÅ]+$/i,
"nl-NL": /^[A-ZÁÉËÏÓÖÜÚ]+$/i,
"nn-NO": /^[A-ZÆØÅ]+$/i,
"hu-HU": /^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
"pl-PL": /^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
"pt-PT": /^[A-ZÃÁÀÂÄÇÉÊËÍÏÕÓÔÖÚÜ]+$/i,
"ru-RU": /^[А-ЯЁ]+$/i,
"kk-KZ": /^[А-ЯЁ\u04D8\u04B0\u0406\u04A2\u0492\u04AE\u049A\u04E8\u04BA]+$/i,
"sl-SI": /^[A-ZČĆĐŠŽ]+$/i,
"sk-SK": /^[A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,
"sr-RS@latin": /^[A-ZČĆŽŠĐ]+$/i,
"sr-RS": /^[А-ЯЂЈЉЊЋЏ]+$/i,
"sv-SE": /^[A-ZÅÄÖ]+$/i,
"th-TH": /^[ก-๙\s]+$/i,
"tr-TR": /^[A-ZÇĞİıÖŞÜ]+$/i,
"uk-UA": /^[А-ЩЬЮЯЄIЇҐі]+$/i,
"ko-KR": /^[ㄱ-ㅎㅏ-ㅣ가-힣]*$/,
"ku-IQ": /^[ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆھەیێيطؤثآإأكضصةظذ]+$/i,
"vi-VN":
/^[A-ZÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĐÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸ]+$/i,
ar: /^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/,
he: /^[א-ת]+$/,
fa: /^['آاءأؤئبپتثجچحخدذرزژسشصضطظعغفقکگلمنوهةی']+$/i,
bn: /^['ঀঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻']+$/,
eo: /^[ABCĈD-GĜHĤIJĴK-PRSŜTUŬVZ]+$/i,
"hi-IN": /^[\u0900-\u0961]+[\u0972-\u097F]*$/i,
"si-LK": /^[\u0D80-\u0DFF]+$/,
};

/**
* Regular expressions for alphanumeric validation by locale.
* Taken from: https://github.com/validatorjs/validator.js/blob/master/src/lib/alpha.js
*/
export const alphanumeric: { [key: string]: RegExp } = {
"en-US": /^[0-9A-Z]+$/i,
"az-AZ": /^[0-9A-VXYZÇƏĞİıÖŞÜ]+$/i,
"bg-BG": /^[0-9А-Я]+$/i,
"cs-CZ": /^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,
"da-DK": /^[0-9A-ZÆØÅ]+$/i,
"de-DE": /^[0-9A-ZÄÖÜß]+$/i,
"el-GR": /^[0-9Α-ω]+$/i,
"es-ES": /^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,
"fi-FI": /^[0-9A-ZÅÄÖ]+$/i,
"fr-FR": /^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,
"it-IT": /^[0-9A-ZÀÉÈÌÎÓÒÙ]+$/i,
"ja-JP": /^[0-90-9ぁ-んァ-ヶヲ-゚一-龠ー・。、]+$/i,
"hu-HU": /^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,
"nb-NO": /^[0-9A-ZÆØÅ]+$/i,
"nl-NL": /^[0-9A-ZÁÉËÏÓÖÜÚ]+$/i,
"nn-NO": /^[0-9A-ZÆØÅ]+$/i,
"pl-PL": /^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,
"pt-PT": /^[0-9A-ZÃÁÀÂÄÇÉÊËÍÏÕÓÔÖÚÜ]+$/i,
"ru-RU": /^[0-9А-ЯЁ]+$/i,
"kk-KZ":
/^[0-9А-ЯЁ\u04D8\u04B0\u0406\u04A2\u0492\u04AE\u049A\u04E8\u04BA]+$/i,
"sl-SI": /^[0-9A-ZČĆĐŠŽ]+$/i,
"sk-SK": /^[0-9A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,
"sr-RS@latin": /^[0-9A-ZČĆŽŠĐ]+$/i,
"sr-RS": /^[0-9А-ЯЂЈЉЊЋЏ]+$/i,
"sv-SE": /^[0-9A-ZÅÄÖ]+$/i,
"th-TH": /^[ก-๙\s]+$/i,
"tr-TR": /^[0-9A-ZÇĞİıÖŞÜ]+$/i,
"uk-UA": /^[0-9А-ЩЬЮЯЄIЇҐі]+$/i,
"ko-KR": /^[0-9ㄱ-ㅎㅏ-ㅣ가-힣]*$/,
"ku-IQ": /^[٠١٢٣٤٥٦٧٨٩0-9ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆھەیێيطؤثآإأكضصةظذ]+$/i,
"vi-VN":
/^[0-9A-ZÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĐÈÉẸẺẼÊỀẾỆỂỄÌÍỊỈĨÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠÙÚỤỦŨƯỪỨỰỬỮỲÝỴỶỸ]+$/i,
ar: /^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/,
he: /^[0-9א-ת]+$/,
fa: /^['0-9آاءأؤئبپتثجچحخدذرزژسشصضطظعغفقکگلمنوهةی۱۲۳۴۵۶۷۸۹۰']+$/i,
bn: /^['ঀঁংঃঅআইঈউঊঋঌএঐওঔকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ়ঽািীুূৃৄেৈোৌ্ৎৗড়ঢ়য়ৠৡৢৣ০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻']+$/,
eo: /^[0-9ABCĈD-GĜHĤIJĴK-PRSŜTUŬVZ]+$/i,
"hi-IN": /^[\u0900-\u0963]+[\u0966-\u097F]*$/i,
"si-LK": /^[0-9\u0D80-\u0DFF]+$/,
};

/**
* Decimal marks for different locales.
* Taken from: https://github.com/validatorjs/validator.js/blob/master/src/lib/alpha.js
*/
export const decimal: { [key: string]: string } = {
"en-US": ".",
ar: "٫",
};

/**
* Assigning English locales to alpha, alphanumeric, and decimal.
* Taken from: https://github.com/validatorjs/validator.js/blob/master/src/lib/alpha.js
*/
export const englishLocales = ["AU", "GB", "HK", "IN", "NZ", "ZA", "ZM"];

for (let i = 0; i < englishLocales.length; i++) {
const locale = `en-${englishLocales[i]}`;
alpha[locale] = alpha["en-US"];
alphanumeric[locale] = alphanumeric["en-US"];
decimal[locale] = decimal["en-US"];
}

// Source: http://www.localeplanet.com/java/
export const arabicLocales = [
"AE",
"BH",
"DZ",
"EG",
"IQ",
"JO",
"KW",
"LB",
"LY",
"MA",
"QM",
"QA",
"SA",
"SD",
"SY",
"TN",
"YE",
];

for (let i = 0; i < arabicLocales.length; i++) {
const locale = `ar-${arabicLocales[i]}`;
alpha[locale] = alpha.ar;
alphanumeric[locale] = alphanumeric.ar;
decimal[locale] = decimal.ar;
}

export const farsiLocales = ["IR", "AF"];

for (let i = 0; i < farsiLocales.length; i++) {
const locale = `fa-${farsiLocales[i]}`;
alphanumeric[locale] = alphanumeric.fa;
decimal[locale] = decimal.ar;
}

export const bengaliLocales = ["BD", "IN"];

for (let i = 0; i < bengaliLocales.length; i++) {
const locale = `bn-${bengaliLocales[i]}`;
alpha[locale] = alpha.bn;
alphanumeric[locale] = alphanumeric.bn;
decimal[locale] = decimal["en-US"];
}

// Source: https://en.wikipedia.org/wiki/Decimal_mark
export const dotDecimal = ["ar-EG", "ar-LB", "ar-LY"];
export const commaDecimal = [
"bg-BG",
"cs-CZ",
"da-DK",
"de-DE",
"el-GR",
"en-ZM",
"eo",
"es-ES",
"fr-CA",
"fr-FR",
"id-ID",
"it-IT",
"ku-IQ",
"hi-IN",
"hu-HU",
"nb-NO",
"nn-NO",
"nl-NL",
"pl-PL",
"pt-PT",
"ru-RU",
"kk-KZ",
"si-LK",
"sl-SI",
"sr-RS@latin",
"sr-RS",
"sv-SE",
"tr-TR",
"uk-UA",
"vi-VN",
];

for (let i = 0; i < dotDecimal.length; i++) {
decimal[dotDecimal[i]] = decimal["en-US"];
}

for (let i = 0; i < commaDecimal.length; i++) {
decimal[commaDecimal[i]] = ",";
}

alpha["fr-CA"] = alpha["fr-FR"];
alphanumeric["fr-CA"] = alphanumeric["fr-FR"];

alpha["pt-BR"] = alpha["pt-PT"];
alphanumeric["pt-BR"] = alphanumeric["pt-PT"];
decimal["pt-BR"] = decimal["pt-PT"];

// see #862
alpha["pl-Pl"] = alpha["pl-PL"];
alphanumeric["pl-Pl"] = alphanumeric["pl-PL"];
decimal["pl-Pl"] = decimal["pl-PL"];

// see #1455
alpha["fa-AF"] = alpha.fa;
21 changes: 21 additions & 0 deletions src/utils/common/AssertString.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Asserts that the input is a string.
* Taken from: https://github.com/validatorjs/validator.js/blob/master/src/lib/util/assertString.js
*/
export const assertString = (input: any): void => {
type InvalidType = "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "null";
let invalidType: InvalidType = typeof input;

if (input === null) {
invalidType = 'null';
} else if (invalidType === 'object') {
invalidType = input.constructor.name as InvalidType;
}

const isString = typeof input === 'string' || input instanceof String;

if (!isString) {
throw new TypeError(`Expected a string but received a ${invalidType}`);
}
};

8 changes: 8 additions & 0 deletions src/utils/validations/ValidationUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {IValidationGroupOptions, IValidationResult} from "../../interfaces";

import {
validateAlpha,
validateArray,
validateContains,
validateContextual,
validateCustom,
validateDate,
Expand Down Expand Up @@ -90,6 +92,12 @@ export class ValidationUtils {
case 'security':
result = validateSecurity(obj[propertyName], validation.options, validation.groups);
break;
case 'contains':
result = validateContains(obj[propertyName], validation.options, validation.groups);
break;
case 'alpha':
result = validateAlpha(obj[propertyName], validation.options, validation.groups);
break;
default:
result = {isValid: false, errors: [`Validation type '${validation.type}' is not supported.`]};
break;
Expand Down
Loading

0 comments on commit d409753

Please sign in to comment.