@intuitionrobotics/ts-common
Version:
157 lines • 6.47 kB
JavaScript
/*
* 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