@geekbears/gb-class-validators 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.prettierrc ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "bracketSpacing": true,
3
+ "singleQuote": true,
4
+ "printWidth": 120,
5
+ "trailingComma": "all",
6
+ "tabWidth": 4,
7
+ "semi": true,
8
+ "arrowParens": "avoid"
9
+ }
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # Geekbears Custom Class Validators
@@ -0,0 +1,7 @@
1
+ {
2
+ "transform": {
3
+ "^.+\\.(t|j)sx?$": "ts-jest"
4
+ },
5
+ "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
6
+ "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"]
7
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './validators';
package/lib/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./validators"), exports);
@@ -0,0 +1,2 @@
1
+ import { ValidationOptions } from 'class-validator';
2
+ export declare function ExactMatch(property: string, validationOptions?: ValidationOptions): (object: object, propertyName: string) => void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExactMatch = void 0;
4
+ const class_validator_1 = require("class-validator");
5
+ const lodash_1 = require("lodash");
6
+ function ExactMatch(property, validationOptions) {
7
+ return (object, propertyName) => {
8
+ (0, class_validator_1.registerDecorator)({
9
+ name: 'exactMatch',
10
+ target: object.constructor,
11
+ propertyName,
12
+ constraints: [property],
13
+ options: validationOptions,
14
+ validator: {
15
+ validate(value, args) {
16
+ const [relatedPropertyName] = args.constraints;
17
+ const relatedValue = args.object[relatedPropertyName];
18
+ return (0, lodash_1.isEqual)(value, relatedValue);
19
+ },
20
+ },
21
+ });
22
+ };
23
+ }
24
+ exports.ExactMatch = ExactMatch;
@@ -0,0 +1,3 @@
1
+ export * from './exact-match.validator';
2
+ export * from './is-included-in.validator';
3
+ export * from './phone.validator';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./exact-match.validator"), exports);
18
+ __exportStar(require("./is-included-in.validator"), exports);
19
+ __exportStar(require("./phone.validator"), exports);
@@ -0,0 +1,2 @@
1
+ import { ValidationOptions } from 'class-validator';
2
+ export declare function IsIncludedIn(property: string[], validationOptions?: ValidationOptions): (object: object, propertyName: string) => void;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IsIncludedIn = void 0;
4
+ const class_validator_1 = require("class-validator");
5
+ function IsIncludedIn(property, validationOptions) {
6
+ return (object, propertyName) => {
7
+ (0, class_validator_1.registerDecorator)({
8
+ name: 'isIncludedIn',
9
+ target: object.constructor,
10
+ propertyName,
11
+ constraints: [property],
12
+ options: validationOptions,
13
+ validator: {
14
+ validate(value, args) {
15
+ const [array] = args.constraints;
16
+ return array.includes(value);
17
+ },
18
+ },
19
+ });
20
+ };
21
+ }
22
+ exports.IsIncludedIn = IsIncludedIn;
@@ -0,0 +1,8 @@
1
+ import { ValidationOptions } from 'class-validator';
2
+ import { CountryCode } from 'libphonenumber-js';
3
+ interface IsGBPhoneParams {
4
+ dev: CountryCode[];
5
+ prod: CountryCode[];
6
+ }
7
+ export declare function IsGBPhoneNumber(params: IsGBPhoneParams, validationOptions?: ValidationOptions): (object: Record<string, any>, propertyName: string) => void;
8
+ export {};
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IsGBPhoneNumber = void 0;
4
+ const class_validator_1 = require("class-validator");
5
+ const libphonenumber_js_1 = require("libphonenumber-js");
6
+ const lodash_1 = require("lodash");
7
+ function IsGBPhoneNumber(params, validationOptions) {
8
+ return (object, propertyName) => {
9
+ (0, class_validator_1.registerDecorator)({
10
+ name: 'IsGBPhoneNumber',
11
+ target: object.constructor,
12
+ propertyName,
13
+ constraints: [[...params.dev], [...params.prod]],
14
+ options: validationOptions,
15
+ validator: {
16
+ validate(value, args) {
17
+ const [validDevRegions, validProdRegions] = args.constraints;
18
+ if (typeof value !== 'string')
19
+ return false;
20
+ const parsed = (0, libphonenumber_js_1.parsePhoneNumberFromString)(value);
21
+ if (!parsed)
22
+ return false;
23
+ if (process.env.NODE_ENV === 'production') {
24
+ return !(0, lodash_1.isEmpty)(validProdRegions) ? validProdRegions.includes(parsed.country) : true;
25
+ }
26
+ return !(0, lodash_1.isEmpty)(validDevRegions) ? validDevRegions.includes(parsed.country) : true;
27
+ },
28
+ },
29
+ });
30
+ };
31
+ }
32
+ exports.IsGBPhoneNumber = IsGBPhoneNumber;
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@geekbears/gb-class-validators",
3
+ "version": "0.0.10",
4
+ "description": "Geekbears custom validators using class-validator package.",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "scripts": {
8
+ "test": "jest --config jest-config.json",
9
+ "prebuild": "rimraf lib",
10
+ "build": "tsc",
11
+ "format": "prettier --write \"src/**/*.ts\"",
12
+ "lint": "tslint -p tsconfig.json",
13
+ "prepare": "npm run build",
14
+ "prepublishOnly": "npm test && npm run lint",
15
+ "preversion": "npm run lint",
16
+ "version": "npm run format && git add -A src",
17
+ "postversion": "git push"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+ssh://git@gitlab.com/geekbears/utilities/backend/gb-class-validators.git"
22
+ },
23
+ "keywords": [
24
+ "class-validators",
25
+ "validators",
26
+ "geekbears"
27
+ ],
28
+ "author": {
29
+ "email": "it@geekbears.com",
30
+ "name": "It Geekbears"
31
+ },
32
+ "contributors": [
33
+ {
34
+ "email": "bruno@geekbears.com",
35
+ "name": "Bruno Jardon"
36
+ },
37
+ {
38
+ "email": "ivan@geekbears.com",
39
+ "name": "Ivan Rangel"
40
+ }
41
+ ],
42
+ "license": "MIT",
43
+ "bugs": {
44
+ "url": "https://gitlab.com/geekbears/utilities/backend/gb-class-validators/issues"
45
+ },
46
+ "homepage": "https://gitlab.com/geekbears/utilities/backend/gb-class-validators#readme",
47
+ "devDependencies": {
48
+ "@types/jest": "^29.5.1",
49
+ "@types/lodash": "^4.14.194",
50
+ "@types/validator": "^13.7.16",
51
+ "jest": "^29.5.0",
52
+ "prettier": "^2.8.8",
53
+ "rimraf": "^5.0.0",
54
+ "ts-jest": "^29.1.0",
55
+ "tslint": "^6.1.3",
56
+ "tslint-config-prettier": "^1.18.0",
57
+ "typescript": "^5.0.4"
58
+ },
59
+ "dependencies": {
60
+ "class-validator": "^0.14.0",
61
+ "lodash": "^4.17.21"
62
+ }
63
+ }
@@ -0,0 +1,32 @@
1
+ import { Validator } from 'class-validator';
2
+ import { before } from 'lodash';
3
+ import { ExactMatch } from '../validators';
4
+
5
+ class MyClass {
6
+ @ExactMatch('lastName', {
7
+ message: '$property must be longer then $constraint1. Given value: $value',
8
+ })
9
+ firstName: string;
10
+ lastName: string;
11
+ }
12
+
13
+ describe('Exact Match Validator', () => {
14
+ let validator: Validator;
15
+
16
+ beforeEach(() => {
17
+ validator = new Validator();
18
+ });
19
+ test('should fail exact match verifications', async () => {
20
+ const model = new MyClass();
21
+ model.firstName = 'Name';
22
+ const errors = await validator.validate(model);
23
+ expect(errors.length).toEqual(1);
24
+ });
25
+ test('should pass match verifications', async () => {
26
+ const model = new MyClass();
27
+ model.firstName = 'Name';
28
+ model.lastName = 'Name';
29
+ const errors = await validator.validate(model);
30
+ expect(errors.length).toEqual(0);
31
+ });
32
+ });
@@ -0,0 +1,27 @@
1
+ import { Validator } from 'class-validator';
2
+ import { IsIncludedIn } from '../validators';
3
+
4
+ class MyClass {
5
+ @IsIncludedIn(['word'])
6
+ words: string;
7
+ }
8
+
9
+ describe('Exact Match Validator', () => {
10
+ let validator: Validator;
11
+
12
+ beforeEach(() => {
13
+ validator = new Validator();
14
+ });
15
+ test('should fail exact match verifications', async () => {
16
+ const model = new MyClass();
17
+ model.words = 'not included';
18
+ const errors = await validator.validate(model);
19
+ expect(errors.length).toEqual(1);
20
+ });
21
+ test('should pass match verifications', async () => {
22
+ const model = new MyClass();
23
+ model.words = 'word';
24
+ const errors = await validator.validate(model);
25
+ expect(errors.length).toEqual(0);
26
+ });
27
+ });
@@ -0,0 +1,100 @@
1
+ import { Validator } from 'class-validator';
2
+ import { IsGBPhoneNumber } from '../validators';
3
+
4
+ class TestClass {
5
+ @IsGBPhoneNumber({ dev: ['US'], prod: ['US'] })
6
+ phone: any; // Type is any to mimmick JSON requests behavior
7
+ }
8
+
9
+ describe('Phone Validator', () => {
10
+ let validator: Validator;
11
+
12
+ beforeEach(() => {
13
+ validator = new Validator();
14
+ });
15
+
16
+ describe('Env different from production', () => {
17
+ test('should fail when not a string', async () => {
18
+ const model = new TestClass();
19
+ model.phone = 98234;
20
+ const errors = await validator.validate(model);
21
+ expect(errors.length).toBeGreaterThan(0);
22
+ });
23
+
24
+ test('should fail when not providing an international code', async () => {
25
+ const model = new TestClass();
26
+ model.phone = '2025550115';
27
+ const errors = await validator.validate(model);
28
+ expect(errors.length).toBeGreaterThan(0);
29
+ });
30
+
31
+ test('should fail when providing an invalid phone number', async () => {
32
+ const model = new TestClass();
33
+ model.phone = '98234';
34
+ const errors = await validator.validate(model);
35
+ expect(errors.length).toBeGreaterThan(0);
36
+ });
37
+
38
+ test('should fail when providing a number from an invalid region', async () => {
39
+ const model = new TestClass();
40
+ model.phone = '+525522626391';
41
+ const errors = await validator.validate(model);
42
+ expect(errors.length).toBeGreaterThan(0);
43
+ });
44
+
45
+ test('should pass when providing a valid phone number', async () => {
46
+ const model = new TestClass();
47
+ model.phone = '+12025550115';
48
+ const errors = await validator.validate(model);
49
+ expect(errors.length).toEqual(0);
50
+ });
51
+ });
52
+
53
+ describe('Prod env', () => {
54
+ const ENV = process.env;
55
+
56
+ beforeEach(() => {
57
+ jest.resetModules();
58
+ process.env = { ...ENV, NODE_ENV: 'production' };
59
+ });
60
+
61
+ afterEach(() => {
62
+ process.env = ENV;
63
+ });
64
+
65
+ test('should fail when not a string', async () => {
66
+ const model = new TestClass();
67
+ model.phone = 98234;
68
+ const errors = await validator.validate(model);
69
+ expect(errors.length).toBeGreaterThan(0);
70
+ });
71
+
72
+ test('should fail when not providing an international code', async () => {
73
+ const model = new TestClass();
74
+ model.phone = '2025550115';
75
+ const errors = await validator.validate(model);
76
+ expect(errors.length).toBeGreaterThan(0);
77
+ });
78
+
79
+ test('should fail when providing an invalid phone number', async () => {
80
+ const model = new TestClass();
81
+ model.phone = '98234';
82
+ const errors = await validator.validate(model);
83
+ expect(errors.length).toBeGreaterThan(0);
84
+ });
85
+
86
+ test('should fail when providing a number from an invalid region', async () => {
87
+ const model = new TestClass();
88
+ model.phone = '+525522626391';
89
+ const errors = await validator.validate(model);
90
+ expect(errors.length).toBeGreaterThan(0);
91
+ });
92
+
93
+ test('should pass when providing a valid phone number', async () => {
94
+ const model = new TestClass();
95
+ model.phone = '+12025550115';
96
+ const errors = await validator.validate(model);
97
+ expect(errors.length).toEqual(0);
98
+ });
99
+ });
100
+ });
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './validators';
@@ -0,0 +1,21 @@
1
+ import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';
2
+ import { isEqual } from 'lodash';
3
+
4
+ export function ExactMatch(property: string, validationOptions?: ValidationOptions) {
5
+ return (object: object, propertyName: string) => {
6
+ registerDecorator({
7
+ name: 'exactMatch',
8
+ target: object.constructor,
9
+ propertyName,
10
+ constraints: [property],
11
+ options: validationOptions,
12
+ validator: {
13
+ validate(value: any, args: ValidationArguments) {
14
+ const [relatedPropertyName] = args.constraints;
15
+ const relatedValue = (args.object as any)[relatedPropertyName];
16
+ return isEqual(value, relatedValue);
17
+ },
18
+ },
19
+ });
20
+ };
21
+ }
@@ -0,0 +1,3 @@
1
+ export * from './exact-match.validator';
2
+ export * from './is-included-in.validator';
3
+ export * from './phone.validator';
@@ -0,0 +1,19 @@
1
+ import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';
2
+
3
+ export function IsIncludedIn(property: string[], validationOptions?: ValidationOptions) {
4
+ return (object: object, propertyName: string) => {
5
+ registerDecorator({
6
+ name: 'isIncludedIn',
7
+ target: object.constructor,
8
+ propertyName,
9
+ constraints: [property],
10
+ options: validationOptions,
11
+ validator: {
12
+ validate(value: string, args: ValidationArguments) {
13
+ const [array] = args.constraints as string[][];
14
+ return array.includes(value);
15
+ },
16
+ },
17
+ });
18
+ };
19
+ }
@@ -0,0 +1,37 @@
1
+ import { ValidationOptions, registerDecorator, ValidationArguments } from 'class-validator';
2
+ import { parsePhoneNumberFromString, CountryCode } from 'libphonenumber-js';
3
+ import { isEmpty } from 'lodash';
4
+
5
+ interface IsGBPhoneParams {
6
+ dev: CountryCode[];
7
+ prod: CountryCode[];
8
+ }
9
+
10
+ export function IsGBPhoneNumber(params: IsGBPhoneParams, validationOptions?: ValidationOptions) {
11
+ return (object: Record<string, any>, propertyName: string) => {
12
+ registerDecorator({
13
+ name: 'IsGBPhoneNumber',
14
+ target: object.constructor,
15
+ propertyName,
16
+ constraints: [[...params.dev], [...params.prod]],
17
+ options: validationOptions,
18
+ validator: {
19
+ validate(value: any, args: ValidationArguments) {
20
+ const [validDevRegions, validProdRegions] = args.constraints;
21
+
22
+ if (typeof value !== 'string') return false;
23
+
24
+ const parsed = parsePhoneNumberFromString(value);
25
+
26
+ if (!parsed) return false;
27
+
28
+ if (process.env.NODE_ENV === 'production') {
29
+ return !isEmpty(validProdRegions) ? validProdRegions.includes(parsed.country) : true;
30
+ }
31
+
32
+ return !isEmpty(validDevRegions) ? validDevRegions.includes(parsed.country) : true;
33
+ },
34
+ },
35
+ });
36
+ };
37
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "module": "commonjs",
5
+ "declaration": true,
6
+ "outDir": "./lib",
7
+ "strict": true,
8
+ "experimentalDecorators": true,
9
+ "strictPropertyInitialization": false
10
+ },
11
+ "include": ["src"],
12
+ "exclude": ["node_modules", "**/__tests__/*"]
13
+ }
package/tslint.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": ["tslint:recommended", "tslint-config-prettier"]
3
+ }