This repository has been archived by the owner on Dec 23, 2021. It is now read-only.
forked from jaystack/odata-v4-server
-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
383 additions
and
172 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,187 @@ | ||
import { buildMessage, Contains, IsBoolean, IsDate, IsDateString, isDateString, IsDefined, IsEmail, IsEmpty, IsEnum, IsInt, IsNumber, IsNumberString, IsOptional, IsPhoneNumber, IsString, IsUrl, IsUUID, MaxLength, MinLength, ValidateBy, ValidationOptions } from 'class-validator'; | ||
import { isClass } from '@newdash/inject/lib/utils'; | ||
import { ODataMethod } from '@odata/parser'; | ||
import 'reflect-metadata'; | ||
import { EColumnOptions } from './odata'; | ||
|
||
const KEY_PROP_CONSTRAINT = 'entity:constraint_information'; | ||
|
||
/** | ||
* Checks if value is defined (!== undefined, !== null). | ||
*/ | ||
export function isDefined(value: any): boolean { | ||
return value !== undefined && value !== null; | ||
export interface ConstraintOption { | ||
presence?: { allowEmpty?: boolean, message?: string }; | ||
type?: 'array' | 'integer' | 'number' | 'string' | 'date' | 'boolean'; | ||
/** | ||
* The inclusion validator is useful for validating input from a dropdown for example. | ||
* It checks that the given value exists in the list given by the `within` option. | ||
*/ | ||
inclusion?: any[]; | ||
/** | ||
* The exclusion validator is useful for restriction certain values. | ||
* It checks that the given value is not in the list given by the within option. | ||
*/ | ||
exclusion?: any[]; | ||
/** | ||
* The format validator will validate a value against a regular expression of your chosing. | ||
*/ | ||
format?: { | ||
pattern?: RegExp; | ||
message?: string; | ||
}; | ||
length?: { | ||
minimum?: number; | ||
maximum?: number; | ||
is?: number; | ||
|
||
notValid?: string; | ||
wrongLength?: string; | ||
tooLong?: string; | ||
tooShort?: string; | ||
}; | ||
numericality?: { | ||
greaterThan?: number; | ||
greaterThanOrEqualTo?: number; | ||
lessThan?: number; | ||
divisibleBy?: number; | ||
onlyInteger?: boolean; | ||
strict?: boolean; | ||
odd?: boolean; | ||
even?: boolean; | ||
|
||
notValid?: string; | ||
notInteger?: string; | ||
notGreaterThan?: string; | ||
notGreaterThanOrEqualTo?: string; | ||
notEqualTo?: string; | ||
notLessThan?: string; | ||
notLessThanOrEqualTo?: string; | ||
notDivisibleBy?: string; | ||
notOdd?: string; | ||
notEven?: string; | ||
}, | ||
email?: { message?: string }, | ||
/** | ||
* This datetime validator can be used to validate dates and times. | ||
* Since date parsing in javascript is very poor some additional work is required to make this work. | ||
*/ | ||
datetime?: { | ||
/** | ||
* The date cannot be before this time. | ||
* This argument will be parsed using the parse function, just like the value. | ||
* The default error must be no earlier than %{date} | ||
*/ | ||
earliest?: string; | ||
latest?: string; | ||
/** | ||
* If true, only dates (not datetimes) will be allowed. | ||
* The default error is must be a valid date | ||
*/ | ||
dateOnly?: boolean; | ||
} | ||
} | ||
|
||
export function IsDateOrDateString(validationOptions?: ValidationOptions): PropertyDecorator { | ||
return ValidateBy( | ||
{ | ||
name: 'IS_DATE_OR_DATE_STRING', | ||
validator: { | ||
validate: (value): boolean => isDateString(value) || value instanceof Date, | ||
defaultMessage: buildMessage( | ||
(eachPrefix) => `${eachPrefix}$property should be date object or ISO date string`, | ||
validationOptions | ||
) | ||
} | ||
}, | ||
validationOptions | ||
); | ||
export function Validate(validateOptions: ConstraintOption): PropertyDecorator { | ||
return function (target, propertyKey) { | ||
Reflect.defineMetadata(KEY_PROP_CONSTRAINT, validateOptions, target, propertyKey); | ||
}; | ||
} | ||
|
||
export function getValidateOptions(target, propertyKey): ConstraintOption { | ||
if (isClass(target)) { | ||
return Reflect.getMetadata(KEY_PROP_CONSTRAINT, target.prototype, propertyKey); | ||
} | ||
return Reflect.getMetadata(KEY_PROP_CONSTRAINT, target, propertyKey); | ||
} | ||
|
||
export const Assert = { | ||
IsDefined, | ||
NotNull: IsDefined, | ||
IsString, | ||
IsNumber, | ||
IsInt, | ||
IsEnum, | ||
IsPhoneNumber, | ||
IsUrl, | ||
IsOptional, | ||
IsDate, | ||
IsDateString, | ||
IsBoolean, | ||
IsNumberString, | ||
IsDateOrDateString, | ||
IsEmail, | ||
IsEmpty, | ||
Contains, | ||
IsUUID, | ||
MaxLength, | ||
MinLength | ||
}; | ||
const UUID_REGEX = /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i; | ||
const ISO_DATE_FORMAT = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/; | ||
|
||
export function columnToValidateRule( | ||
options: EColumnOptions, | ||
method: ODataMethod | ||
): ConstraintOption { | ||
|
||
const cOption: ConstraintOption = {}; | ||
|
||
if (options?.nullable == true || options?.default !== undefined || options?.generated) { | ||
// no required | ||
} | ||
else if (ODataMethod.POST == method) { | ||
cOption.presence = {}; // mandatory | ||
} | ||
|
||
switch (options.type) { | ||
case 'decimal': case 'dec': case 'float': case 'float4': case 'float8': | ||
cOption.type = 'string'; | ||
cOption.format = { | ||
pattern: /^\d*\.?\d*$/, | ||
message: 'invalid numeric string.' | ||
}; | ||
break; | ||
case 'date': | ||
case 'nvarchar': | ||
case 'nvarchar2': | ||
case 'varchar': | ||
case 'varchar2': | ||
case 'char': | ||
case 'text': | ||
case String: | ||
cOption.type = 'string'; | ||
if (options?.length) { | ||
cOption.length = { maximum: options.length as number }; | ||
} | ||
if (options.generated === 'uuid') { | ||
cOption.type = 'string'; | ||
cOption.format = { | ||
pattern: UUID_REGEX, | ||
message: 'invalid uuid string.' | ||
}; | ||
} | ||
break; | ||
case 'uuid': | ||
cOption.type = 'string'; | ||
cOption.format = { | ||
pattern: UUID_REGEX, | ||
message: 'invalid uuid string.' | ||
}; | ||
break; | ||
case 'datetime': case 'datetime2': case 'datetimeoffset': case Date: | ||
cOption.type = 'string'; | ||
cOption.format = { | ||
pattern: ISO_DATE_FORMAT, | ||
message: 'invalid datetime string, only support ISO format.' | ||
}; | ||
break; | ||
case 'int': | ||
case 'integer': | ||
case 'int2': | ||
case 'int4': | ||
case 'int8': | ||
case 'int64': | ||
case 'bigint': | ||
case Number: | ||
if (options.reflectType == Date) { | ||
cOption.type = 'string'; | ||
cOption.format = { | ||
pattern: ISO_DATE_FORMAT, | ||
message: 'invalid datetime string, only support ISO format.' | ||
}; | ||
} | ||
else { | ||
cOption.type = 'number'; | ||
if (options?.length) { | ||
cOption.length = { maximum: options.length as number }; | ||
} | ||
cOption.numericality = { | ||
onlyInteger: true | ||
}; | ||
} | ||
|
||
break; | ||
case 'bool': case 'boolean': | ||
cOption.type = 'boolean'; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
|
||
return cOption; | ||
} |
Oops, something went wrong.