Created
September 26, 2024 00:21
-
-
Save OrderAndCh4oS/b87785652e2bfaec74be022831079f3f to your computer and use it in GitHub Desktop.
Validation
This file contains hidden or 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
| interface IValidationSchema { | |
| [property: string]: (IValidateProperty | IValidateArray | IValidateObject) | |
| } | |
| interface IValidateArray { | |
| required: boolean | |
| type: 'array', | |
| schema: IValidationSchema | |
| } | |
| interface IValidateObject { | |
| required: boolean | |
| type: 'object', | |
| schema: IValidationSchema | |
| } | |
| interface IValidateProperty { | |
| required: boolean | |
| validators?: IValidator[] | |
| } | |
| interface IValidator { | |
| (property: string, data: any, pushError: (error: IValidationErrorMessage) => void): void | |
| } | |
| interface IValidationErrorMessage { | |
| property: string; | |
| error: string | IValidationErrorMessage[]; | |
| } | |
| function validator( | |
| data: any, | |
| validationSchema: IValidationSchema, | |
| errors: IValidationErrorMessage[] = [], | |
| index?: number | |
| ) { | |
| const pushError = (error: IValidationErrorMessage) => { | |
| const property: string = typeof index === 'number' | |
| ? `${error.property}.${index}` | |
| : error.property; | |
| errors.push({ ...error, property }) | |
| } | |
| const isObject = (obj: any) => Object(obj) === obj && !Array.isArray(obj); | |
| for (const [property, validate] of Object.entries(validationSchema)) { | |
| if (validate.required && !(property in data)) { | |
| pushError({property, error: 'Is required'}); | |
| continue; | |
| } | |
| if (!(property in data)) continue; | |
| if ('validators' in validate && validate.validators) { | |
| for (const validation of validate.validators) { | |
| validation(property, data, pushError); | |
| } | |
| continue | |
| } | |
| if ('schema' in validate && validate.type === 'object') { | |
| if (!isObject(data[property])) { | |
| pushError({ property, error: 'Is not an object' }); | |
| continue; | |
| } | |
| const nextErrors: IValidationErrorMessage[] = [] | |
| validator(data[property], validate.schema, nextErrors) | |
| if(nextErrors.length) pushError({property, error: nextErrors}); | |
| continue; | |
| } | |
| if ('schema' in validate && validate.type === 'array') { | |
| if (!Array.isArray(data[property])) { | |
| pushError({ property, error: 'Is not an array' }); | |
| continue; | |
| } | |
| let i = 0; | |
| for (const obj of data[property]) { | |
| const nextErrors: IValidationErrorMessage[] = [] | |
| validator(obj, validate.schema, nextErrors, i); | |
| if(nextErrors.length) pushError({property, error: nextErrors}); | |
| i++; | |
| } | |
| } | |
| } | |
| const allowedKeys = Object.keys(validationSchema); | |
| for (const property of Object.keys(data)) { | |
| if (!allowedKeys.find((ak) => ak === property)) { | |
| pushError({ property, error: 'Is not allowed' }); | |
| } | |
| } | |
| return errors; | |
| } | |
| const isType = (_type: string): IValidator => (property, data, pushError) => { | |
| if (typeof data[property] !== _type) pushError({ property, error: `Is not ${_type}` }); | |
| } | |
| const isString: IValidator = isType('string'); | |
| const isNumber: IValidator = isType('number'); | |
| const isTypeOrNull = (_type: string): IValidator => (property, data, pushError) => { | |
| if (data[property] === null || typeof data[property] !== _type) pushError({ property, error: `Is not ${_type} or null` }); | |
| } | |
| const isOneOfType = (types: string[]): IValidator => (property, data, pushError) => { | |
| if (typeof data[property] in types) pushError({ property, error: `Is not one of ${types.join(', ')}` }); | |
| } | |
| const isArray: IValidator = (property, data, pushError) => { | |
| if (Array.isArray(data[property])) pushError({ property, error: `Is not an array` }); | |
| } | |
| const isObject: IValidator = (property, data, pushError) => { | |
| if (!(Object(data[property]) === data[property] && !Array.isArray(data[property]))) pushError({ property, error: `Is not an object` }); | |
| } | |
| const minValue = (value: number): IValidator => (property, data, pushError) => { | |
| if (data[property] < value) pushError({ property, error: 'Is less than min value' }); | |
| } | |
| const maxValue = (value: number): IValidator => (property, data, pushError) => { | |
| if (data[property] > value) pushError({ property, error: 'Is greater than max value' }); | |
| } | |
| const minLength = (value: number): IValidator => (property, data, pushError) => { | |
| if (data[property].length < value) pushError({ property, error: 'Is too short' }); | |
| } | |
| const maxLength = (value: number): IValidator => (property, data, pushError) => { | |
| if (data[property].length > value) pushError({ property, error: 'Is too long' }); | |
| } | |
| /** | |
| * Test one validators | |
| */ | |
| const demoSchemaOne: IValidationSchema = { | |
| required: { | |
| required: true | |
| }, | |
| lowNum: { | |
| required: true, | |
| validators: [isNumber, minValue(3), maxValue(10)] | |
| }, | |
| highNum: { | |
| required: true, | |
| validators: [isNumber, minValue(3), maxValue(10)] | |
| }, | |
| notNum: { | |
| required: true, | |
| validators: [isNumber, minValue(3), maxValue(10)] | |
| }, | |
| shortStr: { | |
| required: true, | |
| validators: [isString, minLength(3), maxLength(10)] | |
| }, | |
| longStr: { | |
| required: true, | |
| validators: [isString, minLength(3), maxLength(10)] | |
| }, | |
| notStr: { | |
| required: true, | |
| validators: [isString, minLength(3), maxLength(10)] | |
| }, | |
| shortArr: { | |
| required: true, | |
| validators: [isArray, minLength(3), maxLength(10)] | |
| }, | |
| longArr: { | |
| required: true, | |
| validators: [isArray, minLength(3), maxLength(5)] | |
| }, | |
| notObj: { | |
| required: true, | |
| validators: [isObject] | |
| } | |
| } | |
| const demoDataOne = { | |
| lowNum: 1, | |
| highNum: 11, | |
| notNum: 'a', | |
| shortStr: 'aa', | |
| longStr: 'aaaaaaaaaaaaa', | |
| notStr: 1, | |
| shortArr: [1, 2], | |
| longArr: [1, 2, 3, 4, 5, 6], | |
| notObj: 10, | |
| notAllowed: 'oh no' | |
| } | |
| const testOne = validator(demoDataOne, demoSchemaOne); | |
| console.log(testOne); | |
| console.log('isValid:', testOne.length === 0); | |
| /** | |
| * Test two prop + arr | |
| */ | |
| const demoSchemaTwo: IValidationSchema = { | |
| unit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| }, | |
| arr: { | |
| required: true, | |
| type: 'array', | |
| schema: { | |
| arrUnit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| } | |
| } | |
| } | |
| } | |
| const demoDataTwo = { | |
| unit: 11, | |
| arr: [{ arrUnit: 1 }, { arrUnit: 74 }] | |
| } | |
| const testTwo = validator(demoDataTwo, demoSchemaTwo); | |
| console.log(JSON.stringify(testTwo)); | |
| console.log('isValid:', testTwo.length === 0); | |
| /** | |
| * Test three prop + obj | |
| */ | |
| const demoSchemaThree: IValidationSchema = { | |
| unit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| }, | |
| obj: { | |
| required: true, | |
| type: 'object', | |
| schema: { | |
| objUnit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| } | |
| } | |
| } | |
| } | |
| const demoDataThree = { | |
| unit: 11, | |
| obj: { objUnit: 1 }, | |
| somethingElse: 'blah' | |
| } | |
| const testThree = validator(demoDataThree, demoSchemaThree); | |
| console.log(JSON.stringify(testThree)); | |
| console.log('isValid:', testThree.length === 0); | |
| /** | |
| * Test four prop + obj + arr VALID | |
| */ | |
| const demoSchemaFour: IValidationSchema = { | |
| unit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| }, | |
| obj: { | |
| required: true, | |
| type: 'object', | |
| schema: { | |
| objUnit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| } | |
| } | |
| }, | |
| arr: { | |
| required: true, | |
| type: 'array', | |
| schema: { | |
| arrUnit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| } | |
| } | |
| } | |
| } | |
| const demoDataFour = { | |
| unit: 10, | |
| obj: { objUnit: 4 }, | |
| arr: [{ arrUnit: 3 }, { arrUnit: 6 }], | |
| } | |
| console.log('isValid:', validator(demoDataFour, demoSchemaFour).length === 0); | |
| /** | |
| * Test five required props | |
| */ | |
| const demoSchemaFive: IValidationSchema = { | |
| unit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| }, | |
| unitTwo: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| }, | |
| obj: { | |
| required: true, | |
| type: 'object', | |
| schema: { | |
| objUnit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| } | |
| } | |
| }, | |
| arr: { | |
| required: true, | |
| type: 'array', | |
| schema: { | |
| arrUnit: { | |
| required: true, | |
| validators: [minValue(3), maxValue(10)] | |
| } | |
| } | |
| } | |
| } | |
| const demoDataFive = { | |
| } | |
| const testFive = validator(demoDataFive, demoSchemaFive); | |
| console.log(JSON.stringify(testFive)); | |
| console.log('isValid:', testFive.length === 0); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment