UNPKG

@intuitionrobotics/ts-common

Version:
157 lines 6.47 kB
/* * ts-common is the basic building blocks of our typescript projects * * Copyright (C) 2020 Intuition Robotics * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { BadImplementationException, CustomException } from "../core/exceptions.js"; import { __stringify, } from "../utils/tools.js"; import { _keys } from "../utils/object-tools.js"; import {} from "../utils/types.js"; import { currentTimeMillies, Day } from "../index.js"; export class ValidationException extends CustomException { path; input; constructor(debugMessage, path, input) { super(ValidationException, debugMessage); this.path = path; this.input = input; } } const assertMandatory = (mandatory, path, input) => { if (input !== undefined && input !== null) return; if (mandatory) throw new ValidationException(`Missing mandatory field: ${path}\n`, path, input); }; export const validateExists = (mandatory = true) => { return (path, input) => { assertMandatory(mandatory, path, input); }; }; export const validateType = (type, mandatory = true) => { return (path, input) => { assertMandatory(mandatory, path, input); const typeOfInput = typeof input; if (input && typeOfInput !== type) throw new ValidationException(`Got ${typeOfInput} when expecting ${type} for field: ${path}\n`, path, input); }; }; export const validateString = (mandatory = true) => { return validateType('string', mandatory); }; export const validateNumber = (mandatory = true) => { return validateType('number', mandatory); }; export const validateBoolean = (mandatory = true) => { return validateType('boolean', mandatory); }; export const validateObjectValues = (validator, mandatory = true) => (path, input) => { assertMandatory(mandatory, path, input); if (!input) return; for (const key of _keys(input)) { const inputValue = input[key]; if (typeof inputValue === "object") { const objectValidator = validateObjectValues(validator, mandatory); if (!objectValidator) return; return objectValidator(`${path}/${key}`, inputValue); } validate(inputValue, validator, `${path}/${key}`); } }; export const validateArray = (validator, mandatory = true) => (path, input) => { assertMandatory(mandatory, path, input); if (!input) return; const _input = input; for (let i = 0; i < _input.length; i++) { validate(_input[i], validator, `${path}/${i}`); } }; export const validateRegexp = (regexp, mandatory = true) => { return (path, input) => { assertMandatory(mandatory, path, input); if (!input) return; if (regexp.test(input)) return; throw new ValidationException(`Input is not valid:\n input: ${input}\n regexp: ${regexp}\n`, path, input); }; }; export const validateValue = (values, mandatory = true) => { return (path, input) => { assertMandatory(mandatory, path, input); if (!input) return; if (values.includes(input)) return; throw new ValidationException(`Input is not valid:\n input: ${input}\n options: ${__stringify(values)}\n`, path, input); }; }; export const validateRange = (ranges, mandatory = true) => { return (path, input) => { assertMandatory(mandatory, path, input); if (!input) return; for (const range of ranges) { if (range[0] <= input && input <= range[1]) return; } throw new ValidationException(`Input is not valid:\n input: ${input}\n regexp: ${__stringify(ranges)}\n`, path, input); }; }; export const validate = (instance, _validator, path = "") => { if (!_validator) return; if (typeof _validator === "function") { const validator = _validator; if (!validator) return; return validator(`${path}`, instance); } if (typeof _validator === "object") { if (!instance && _validator) throw new BadImplementationException(`Unexpect object at '${path}'\nif you want to ignore the validation of this object,\n add the following to your validator:\n {\n ...\n ${path}: undefined\n ...\n}\n`); const __validator = _validator; validateObject(__validator, instance, path); } }; export const validateObject = (__validator, instance, path = "") => { const validatorKeys = _keys(__validator); const instanceKeys = Object.keys(instance); for (const key of instanceKeys) { // @ts-expect-error TS struggles with this dynamic typing if (!validatorKeys.includes(key)) throw new BadImplementationException(`Unexpect key '${path}${key}'\nif you want to ignore the validation of this property,\n add the following to your validator:\n {\n ...\n ${key}: undefined\n ...\n}\nAvailable keys are ${validatorKeys.join()}`); } for (const key of validatorKeys) { const value = instance[key]; const validator = __validator[key]; validate(value, validator, `${path}/${key}`); } }; export const isTimestampValid = (time, range = { min: currentTimeMillies() - 1000 * Day, max: currentTimeMillies() + 1000 * Day }) => { return time >= range.min && time <= range.max; }; export const auditValidator = (range) => (_path, audit) => { timestampValidator(range)(_path, audit?.auditAt?.timestamp); }; export const timestampValidator = (range) => (_path, timestamp) => { if (!timestamp || !isTimestampValid(timestamp, range)) throw new ValidationException('Time is not proper', _path, timestamp); }; export const validateEmail = validateRegexp(/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/); //# sourceMappingURL=validator.js.map