Skip to content

Commit

Permalink
Smaller bundle - kickoff
Browse files Browse the repository at this point in the history
  • Loading branch information
Christoph Linder committed Sep 13, 2018
1 parent 064458a commit e09c252
Show file tree
Hide file tree
Showing 29 changed files with 395 additions and 343 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "class-validator",
"private": true,
"version": "0.9.1",
"version": "0.9.1-rc1",
"description": "Class-based validation with Typescript / ES6 / ES5 using decorators or validation schemas. Supports both node.js and browser",
"license": "MIT",
"readmeFilename": "README.md",
Expand Down
4 changes: 2 additions & 2 deletions sample/sample4-custom-validator/CustomTextLength.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ValidatorConstraintInterface} from "../../src/validation/ValidatorConstraintInterface";
import {ValidatorConstraint} from "../../src/decorator/decorators";
import {ValidatorConstraint} from "../../src/decorator/ValidatorConstraint";

@ValidatorConstraint()
export class CustomTextLength implements ValidatorConstraintInterface {
Expand All @@ -8,4 +8,4 @@ export class CustomTextLength implements ValidatorConstraintInterface {
return text.length > 1 && text.length < 10;
}

}
}
5 changes: 2 additions & 3 deletions sample/sample4-custom-validator/Post.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {Contains, IsInt, MinLength, MaxLength, IsEmail, IsFQDN, IsDate, IsNotEmpty, ArrayNotEmpty, ArrayMinSize, ArrayMaxSize} from "../../src/decorator/decorators";
import {Validate} from "../../src/decorator/decorators";
import {CustomTextLength} from "./CustomTextLength";
import {Validate} from "../../src/decorator/Validate";

export class Post {

Expand All @@ -9,4 +8,4 @@ export class Post {
})
title: string;

}
}
4 changes: 2 additions & 2 deletions sample/sample6-custom-decorator/IsLongerThan.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {registerDecorator} from "../../src/index";
import {ValidationOptions} from "../../src/decorator/ValidationOptions";
import {ValidatorConstraintInterface} from "../../src/validation/ValidatorConstraintInterface";
import {ValidatorConstraint} from "../../src/decorator/decorators";
import {ValidatorConstraint} from "../../src/decorator/ValidatorConstraint";
import {ValidationArguments} from "../../src/validation/ValidationArguments";

export function IsLongerThan(property: string, validationOptions?: ValidationOptions) {
Expand All @@ -27,4 +27,4 @@ export class IsLongerThanConstraint implements ValidatorConstraintInterface {
value.length > relatedValue.length;
}

}
}
25 changes: 25 additions & 0 deletions src/decorator/Equals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {ValidationOptions} from "./ValidationOptions";
import {buildMessage, ValidateBy} from "./ValidateBy";

/**
* Checks if value matches ("===") the comparison.
*/
export function equals(value: any, comparison: any): boolean {
return value === comparison;
}

/**
* Checks if the value match ("===") the comparison.
*/
export function Equals(comparison: any, validationOptions?: ValidationOptions) {
return ValidateBy({
name: "equals",
validate: (value, args) => equals(value, args.constraints[0]),
constraints: [comparison],
defaultMessage: buildMessage((eachPrefix) => eachPrefix + "$property must be equal to $constraint1", validationOptions),

},
validationOptions
);
}

28 changes: 28 additions & 0 deletions src/decorator/IsBoolean.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {ValidationOptions} from "./ValidationOptions";
import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs";
import {getFromContainer, MetadataStorage, ValidationTypes} from "..";
import {ValidationMetadata} from "../metadata/ValidationMetadata";
import {buildMessage, ValidateBy} from "./ValidateBy";
import {equals} from "./Equals";

/**
* Checks if a given value is a real boolean.
*/
export function isBoolean(value: any): boolean {
return value instanceof Boolean || typeof value === "boolean";
}


/**
* Checks if a value is a boolean.
*/
export function IsBoolean(validationOptions?: ValidationOptions) {
return ValidateBy({
name: "isBoolean",
validate: (value, args) => isBoolean(value),
defaultMessage: buildMessage((eachPrefix) => eachPrefix + "$property must be a boolean value", validationOptions),

},
validationOptions
);
}
25 changes: 25 additions & 0 deletions src/decorator/IsEmpty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {ValidationOptions} from "./ValidationOptions";
import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs";
import {getFromContainer, MetadataStorage, ValidationTypes} from "..";
import {ValidationMetadata} from "../metadata/ValidationMetadata";
import {buildMessage, ValidateBy} from "./ValidateBy";
import {equals} from "./Equals";

/**
* Checks if given value is empty (=== '', === null, === undefined).
*/
export function isEmpty(value: any): boolean {
return value === "" || value === null || value === undefined;
}

/**
* Checks if given value is empty (=== '', === null, === undefined).
*/
export function IsEmpty(validationOptions?: ValidationOptions) {
return ValidateBy({
name: "isEmpty",
validate: (value, args) => isEmpty(value),
defaultMessage: buildMessage((eachPrefix) => eachPrefix + "$property must be empty", validationOptions),

}, validationOptions);
}
30 changes: 30 additions & 0 deletions src/decorator/IsIn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {ValidationOptions} from "./ValidationOptions";
import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs";
import {getFromContainer, MetadataStorage, ValidationTypes} from "..";
import {ValidationMetadata} from "../metadata/ValidationMetadata";
import {buildMessage, ValidateBy} from "./ValidateBy";
import {equals} from "./Equals";

/**
* Checks if given value is in a array of allowed values.
*/
export function isIn(value: any, possibleValues: any[]): boolean {
return !(possibleValues instanceof Array) || possibleValues.some(possibleValue => possibleValue === value);
}

/**
* Checks if value is in a array of allowed values.
*/
export function IsIn(values: any[], validationOptions?: ValidationOptions) {
return ValidateBy({
name: "isIn",
constraints: [values],
validate: (value, args) => isIn(value, args.constraints[0]),
defaultMessage: buildMessage(
(eachPrefix) => eachPrefix + "$property must be one of the following values: $constraint1",
validationOptions),

},
validationOptions
);
}
25 changes: 25 additions & 0 deletions src/decorator/IsNotEmpty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {ValidationOptions} from "./ValidationOptions";
import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs";
import {getFromContainer, MetadataStorage, ValidationTypes} from "..";
import {ValidationMetadata} from "../metadata/ValidationMetadata";
import {buildMessage, ValidateBy} from "./ValidateBy";
import {isEmpty} from "./IsEmpty";

/**
* Checks if given value is not empty (!== '', !== null, !== undefined).
*/
export function isNotEmpty(value: any): boolean {
return value !== "" && value !== null && value !== undefined;
}

/**
* Checks if given value is not empty (!== '', !== null, !== undefined).
*/
export function IsNotEmpty(validationOptions?: ValidationOptions) {
return ValidateBy({
name: "isNotEmpty",
validate: (value, args) => isNotEmpty(value),
defaultMessage: buildMessage((eachPrefix) => eachPrefix + "$property should not be empty", validationOptions),

}, validationOptions);
}
30 changes: 30 additions & 0 deletions src/decorator/IsNotIn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {ValidationOptions} from "./ValidationOptions";
import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs";
import {getFromContainer, MetadataStorage, ValidationTypes} from "..";
import {ValidationMetadata} from "../metadata/ValidationMetadata";
import {buildMessage, ValidateBy} from "./ValidateBy";
import {equals} from "./Equals";

/**
* Checks if given value not in a array of allowed values.
*/
export function isNotIn(value: any, possibleValues: any[]): boolean {
return !(possibleValues instanceof Array) || !possibleValues.some(possibleValue => possibleValue === value);
}

/**
* Checks if value is not in a array of disallowed values.
*/
export function IsNotIn(values: any[], validationOptions?: ValidationOptions) {
return ValidateBy({
name: "isNotIn",
validate: (value, args) => isNotIn(value, args.constraints[0]),
constraints: [values],
defaultMessage: buildMessage(
(eachPrefix) => eachPrefix + "$property should not be one of the following values: $constraint1",
validationOptions),

},
validationOptions
);
}
45 changes: 45 additions & 0 deletions src/decorator/IsPhoneNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {ValidationOptions} from "./ValidationOptions";
import {buildMessage, ValidateBy} from "./ValidateBy";

import * as libphonenumber from "google-libphonenumber";

const phoneUtil = libphonenumber.PhoneNumberUtil.getInstance();

/**
* Checks if the string is a valid phone number.
* @param value the potential phone number string to test
* @param {string} region 2 characters uppercase country code (e.g. DE, US, CH).
* If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region.
* See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]{@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33}
*/
export function isPhoneNumber(value: string, region: string | null): boolean {
try {
const phoneNum = phoneUtil.parseAndKeepRawInput(value, region);
const result = phoneUtil.isValidNumber(phoneNum);
return result;
} catch (error) {
// logging?
return false;
}
}


/**
* Checks if the string is a valid phone number.
* @param region 2 characters uppercase country code (e.g. DE, US, CH).
* If users must enter the intl. prefix (e.g. +41), then you may pass "ZZ" or null as region.
* See [google-libphonenumber, metadata.js:countryCodeToRegionCodeMap on github]{@link https://github.com/ruimarinho/google-libphonenumber/blob/1e46138878cff479aafe2ce62175c6c49cb58720/src/metadata.js#L33}
*/
export function IsPhoneNumber(region: string | null, validationOptions?: ValidationOptions) {
return ValidateBy({
name: "isPhoneNumber",
constraints: [region],
validate: (value, args) => isPhoneNumber(value, args.constraints[0]),
defaultMessage: buildMessage(
(eachPrefix) => eachPrefix + "$property must be a valid phone number",
validationOptions),

},
validationOptions
);
}
25 changes: 25 additions & 0 deletions src/decorator/NotEquals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {ValidationOptions} from "./ValidationOptions";
import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs";
import {getFromContainer, MetadataStorage, ValidationTypes} from "..";
import {ValidationMetadata} from "../metadata/ValidationMetadata";
import {buildMessage, ValidateBy} from "./ValidateBy";

/**
* Checks if value does not match ("!==") the comparison.
*/
export function notEquals(value: any, comparison: any): boolean {
return value !== comparison;
}

/**
* Checks if the value does not match ("!==") the comparison.
*/
export function NotEquals(comparison: any, validationOptions?: ValidationOptions) {
return ValidateBy({
name: "notEquals",
constraints: [comparison],
validate: (value, args) => notEquals(value, args.constraints[0]),
defaultMessage: buildMessage((eachPrefix) => eachPrefix + "$property should not be equal to $constraint1", validationOptions)
},
validationOptions);
}
24 changes: 24 additions & 0 deletions src/decorator/Validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {ValidationOptions} from "./ValidationOptions";
import {ValidationMetadataArgs} from "../metadata/ValidationMetadataArgs";
import {getFromContainer, MetadataStorage, ValidationTypes} from "..";
import {ValidationMetadata} from "../metadata/ValidationMetadata";

/**
* Performs validation based on the given custom validation class.
* Validation class must be decorated with ValidatorConstraint decorator.
*/
export function Validate(constraintClass: Function, validationOptions?: ValidationOptions): Function;
export function Validate(constraintClass: Function, constraints?: any[], validationOptions?: ValidationOptions): Function;
export function Validate(constraintClass: Function, constraintsOrValidationOptions?: any[] | ValidationOptions, maybeValidationOptions?: ValidationOptions): Function {
return function (object: Object, propertyName: string) {
const args: ValidationMetadataArgs = {
type: ValidationTypes.CUSTOM_VALIDATION,
target: object.constructor,
propertyName: propertyName,
constraintCls: constraintClass,
constraints: constraintsOrValidationOptions instanceof Array ? constraintsOrValidationOptions as any[] : undefined,
validationOptions: !(constraintsOrValidationOptions instanceof Array) ? constraintsOrValidationOptions as ValidationOptions : maybeValidationOptions
};
getFromContainer(MetadataStorage).addValidationMetadata(new ValidationMetadata(args));
};
}
31 changes: 31 additions & 0 deletions src/decorator/ValidateBy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// -------------------------------------------------------------------------
import {ValidationFunction} from "./ValidationFunction";
import {ValidationOptions} from "./ValidationOptions";
import {registerDecorator} from "../register-decorator";
import {ValidationArguments, ValidatorConstraintInterface} from "..";

export function buildMessage(impl: (eachPrefix: string) => string, validationOptions?: ValidationOptions):
(validationArguments?: ValidationArguments) => string {
return () => {
const eachPrefix = validationOptions && validationOptions.each
? "each value in "
: "";
return impl(eachPrefix);
};
}

export function ValidateBy(validator: ValidationFunction, validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: validator.name,
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: validator.constraints,
validator: <ValidatorConstraintInterface> {
validate: validator.validate,
defaultMessage: validator.defaultMessage
}
});
};
}
9 changes: 9 additions & 0 deletions src/decorator/ValidationFunction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {ValidationArguments} from "../index";

export interface ValidationFunction {
name: string;
constraints?: any[];
validate: (value: any, validationArguments?: ValidationArguments) => Promise<boolean>|boolean;
defaultMessage: (validationArguments?: ValidationArguments) => string;
async?: boolean;
}
20 changes: 20 additions & 0 deletions src/decorator/ValidatorConstraint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {ConstraintMetadata} from "../metadata/ConstraintMetadata";
import {getFromContainer} from "../container";
import {MetadataStorage} from "..";

/**
* Registers custom validator class.
*/
export function ValidatorConstraint(options?: { name?: string, async?: boolean }) {
return function (target: Function) {
const isAsync = options && options.async ? true : false;
let name = options && options.name ? options.name : "";
if (!name) {
name = (target as any).name;
if (!name) // generate name if it was not given
name = name.replace(/\.?([A-Z]+)/g, (x, y) => "_" + y.toLowerCase()).replace(/^_/, "");
}
const metadata = new ConstraintMetadata(target, name, isAsync);
getFromContainer(MetadataStorage).addConstraintMetadata(metadata);
};
}

0 comments on commit e09c252

Please sign in to comment.