import { Schema, CheckType, CheckObject, CheckFunction, DateInstance, StringArray, NumberArray, BooleanArray, DateArray, OptionalString, OptionalNumber, OptionalBoolean, OptionalDate } from "./Schema"; import * as _ from "underscore"; import {Validation} from "@anyhowstep/validation"; export class FieldSetUtil { public static IsValidatorFunction (check : CheckType) : check is CheckFunction { return (check instanceof Function); } public static IsSchema (check : CheckType) : check is Schema { const fields = (check as any).fields; return (check instanceof Object) && fields != null && (fields instanceof Object); } public static Check (subName : string, value : any, check : CheckType) : Promise { if (FieldSetUtil.IsSchema(check)) { return FieldSetUtil.HasValidField(subName, value, check); } else if (check as any == String) { return Validation.IsString(subName, value) as any; } else if (check as any == Number) { return Validation.IsNumber(subName, value) as any; } else if (check as any == Boolean) { return Validation.IsBoolean(subName, value) as any; } else if (check as any == DateInstance) { return Validation.IsDate(subName, value) as any; } else if (check as any == StringArray) { return Validation.IsArrayOfString(subName, value) as any; } else if (check as any == NumberArray) { return Validation.IsArrayOfNumber(subName, value) as any; } else if (check as any == BooleanArray) { return Validation.IsArrayOfBoolean(subName, value) as any; } else if (check as any == DateArray) { return Validation.IsArrayOfDate(subName, value) as any; } else if (check as any == OptionalString) { return Validation.IsOptional(subName, value, Validation.IsString) as any; } else if (check as any == OptionalNumber) { return Validation.IsOptional(subName, value, Validation.IsNumber) as any; } else if (check as any == OptionalBoolean) { return Validation.IsOptional(subName, value, Validation.IsBoolean) as any; } else if (check as any == OptionalDate) { return Validation.IsOptional(subName, value, Validation.IsDate) as any; } else if (FieldSetUtil.IsValidatorFunction(check)) { //IsValidatorFunction() should be called last //Some of the above checks compare against functions return check(subName, value); } else { return Promise.reject(`Unknown check for ${name}`); } } public static IsCheckObject (check : CheckType | CheckObject) : check is CheckObject{ return check.hasOwnProperty("default") && check.hasOwnProperty("check"); } public static GetCheck (check : CheckType | CheckObject) : CheckType { return FieldSetUtil.IsCheckObject(check) ? check.check : check; } private static HasValid ( name : string, mixed : any, schema : Schema, has : (name : string, mixed : any, schema : Schema) => Promise ) { return has(name, mixed, schema) .then((target) => { let arr : Promise[] = []; for (let fieldName in target) { const field = schema.fields[fieldName]; const value = target[fieldName]; const subName = `${name}.${fieldName}`; if (field == null) { if (schema.allowExtraFields) { continue; } else { return Promise.reject(`Field ${fieldName} not allowed in ${name}`); } } arr.push(FieldSetUtil.Check( subName, value, FieldSetUtil.GetCheck(field) )); } return Promise.all(arr); }) .then(() => { if (schema.check == null) { return Promise.resolve(); } else { return schema.check(name, mixed); } }) .then(() => { return mixed; }); } public static HasField (name : string, mixed : any, schema : Schema) : Promise<{ [f in keyof T]? : any }> { if (mixed == null) { return Promise.reject(new Error(`${name} is null or undefined, having no fields`)); } if (!(mixed instanceof Object)) { return Promise.reject(new Error(`${name} is is not an object, having no fields`)); } const schemaFields = Object.getOwnPropertyNames(schema.fields); const targetFields = Object.getOwnPropertyNames(mixed); const extras = _.difference(targetFields, schemaFields); if (schema.allowExtraFields != true && extras.length > 0) { return Promise.reject(new Error(`${name} has extra fields ${extras.join(', ')}`)); } return Promise.resolve(mixed); } public static HasValidField (name : string, mixed : any, schema : Schema) : Promise { return FieldSetUtil.HasValid(name, mixed, schema, FieldSetUtil.HasField); } }