tsoa-custom-decorators
Version:
Build swagger-compliant REST APIs using TypeScript and Node
158 lines (134 loc) • 4.99 kB
text/typescript
import * as moment from 'moment';
import * as validator from 'validator';
let models: any = null;
export function ValidateParam(schema: any, value: any, generatedModels: any, name = '') {
models = generatedModels;
if (value === undefined || value === null) {
if (schema.required) {
throw new InvalidRequestException(`'${name}' is a required ${schema.in} parameter.`);
} else {
return undefined;
}
}
switch (schema.typeName) {
case 'string':
return validateString(value, name);
case 'boolean':
return validateBool(value, name);
case 'integer':
case 'long':
return validateInt(value, name);
case 'float':
case 'double':
return validateFloat(value, name);
case 'enum':
return validateEnum(value, name, schema.enumMembers);
case 'array':
return validateArray(value, name, schema.array);
case 'date':
return validateDate(value, name);
case 'datetime':
return validateDateTime(value, name);
case 'buffer':
return validateBuffer(value, name);
default:
return validateModel(value, schema.typeName);
}
}
function validateInt(numberValue: string, name: string): number {
if (!validator.isInt(numberValue + '')) {
throw new InvalidRequestException(name + ' should be a valid integer.');
}
return validator.toInt(numberValue + '', 10);
}
function validateFloat(numberValue: string, name: string): number {
if (!validator.isFloat(numberValue + '')) {
throw new InvalidRequestException(name + ' should be a valid float.');
}
return validator.toFloat(numberValue + '');
}
function validateEnum(enumValue: string, name: string, members?: string[]): any {
if (!members) {
throw new InvalidRequestException(name + ' no member.');
}
const existValue = members.filter(m => m === enumValue);
if (!existValue || !enumValue.length || !existValue.length) {
throw new InvalidRequestException(name + ' should be one of the following; ' + members.join(', '));
}
return existValue[0];
}
function validateDate(dateValue: string, name: string): Date {
const regEx = /^\d{4}-\d{2}-\d{2}$/;
if (!dateValue.match(regEx)) {
throw new InvalidRequestException(name + ' should be a valid date, i.e. YYYY-MM-DD');
}
return new Date(dateValue);
}
function validateDateTime(datetimeValue: string, name: string): Date {
const validatedDate = moment(datetimeValue, moment.ISO_8601, true);
if (!validatedDate.isValid()) {
throw new InvalidRequestException(name + ' should be a valid ISO 8601 date, i.e. YYYY-MM-DDTHH:mm:ss');
}
return validatedDate.toDate();
}
function validateString(stringValue: string, name: string) {
if (typeof stringValue !== 'string') {
throw new InvalidRequestException(name + ' should be a valid string.');
}
return stringValue.toString();
}
function validateBool(boolValue: any, typeName: string): boolean {
if (boolValue === true || boolValue === false) { return boolValue; }
if (boolValue.toLowerCase() === 'true') { return true; }
if (boolValue.toLowerCase() === 'false') { return false; }
throw new InvalidRequestException(name + ' should be valid boolean value.');
}
function validateModel(modelValue: any, typeName: string): any {
const modelDefinition = models[typeName];
if (modelDefinition) {
if (modelDefinition.properties) {
Object.keys(modelDefinition.properties).forEach((key: string) => {
const property = modelDefinition.properties[key];
modelValue[key] = ValidateParam(property, modelValue[key], models, key);
});
}
if (modelDefinition.additionalProperties) {
Object.keys(modelValue).forEach((key: string) => {
let validatedValue = null;
for (const additionalProperty of modelDefinition.additionalProperties) {
try {
validatedValue = ValidateParam(additionalProperty, modelValue[key], models, key);
break;
} catch (err) {
continue;
}
}
if (validatedValue) {
modelValue[key] = validatedValue;
} else {
throw new Error(`No matching model found in additionalProperties to validate ${key}`);
}
});
}
}
return modelValue;
}
function validateArray(arrayValue: any[], name: string, schema?: any): any[] {
if (!schema) {
throw new InvalidRequestException(name + ' array invalid.');
}
return arrayValue.map(value => {
return ValidateParam(schema, value, models, undefined);
});
}
function validateBuffer(value: string, name: string) {
return new Buffer(value);
}
interface Exception extends Error {
status: number;
}
class InvalidRequestException implements Exception {
public status = 400;
public name = 'Invalid Request';
constructor(public message: string) { }
}