Typescript migration:
* validation/validator.js
This commit is contained in:
parent
9e29eb924e
commit
5c0b5abf73
|
@ -17,7 +17,7 @@ export function normalizeMac (mac: string): string {
|
|||
return macParts.join(':');
|
||||
}
|
||||
|
||||
export function parseInt (str: string): number | undefined {
|
||||
export function parseInteger (str: string): number | undefined {
|
||||
const parsed = _.parseInt(str, 10);
|
||||
return parsed.toString() === str ? parsed : undefined;
|
||||
}
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
const _ = require('lodash')
|
||||
|
||||
const Strings = require('../utils/strings')
|
||||
const Logger = require('../logger')
|
||||
|
||||
// TODO: sanitize input for further processing as specified by constraints (correct types, trimming, etc.)
|
||||
|
||||
function isValidBoolean(value) {
|
||||
return _.isBoolean(value) || value === 'true' || value === 'false';
|
||||
}
|
||||
|
||||
function isValidNumber(constraint, value) {
|
||||
if (_.isString(value)) {
|
||||
value = Strings.parseInt(value);
|
||||
}
|
||||
|
||||
if (!_.isNumber(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_.isNaN(value) || !_.isFinite(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_.isNumber(constraint.min) && value < constraint.min) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_.isNumber(constraint.max) && value > constraint.max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isValidEnum(constraint, value) {
|
||||
if (!_.isString(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _.indexOf(constraint.allowed, value) >= 0;
|
||||
}
|
||||
|
||||
function isValidString(constraint, value) {
|
||||
if (!_.isString(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const trimmed = value.trim();
|
||||
return (trimmed === '' && constraint.optional) || trimmed.match(constraint.regex);
|
||||
}
|
||||
|
||||
function isValid(constraint, acceptUndefined, value) {
|
||||
if (value === undefined) {
|
||||
return acceptUndefined || constraint.optional;
|
||||
}
|
||||
|
||||
switch (constraint.type) {
|
||||
case 'boolean':
|
||||
return isValidBoolean(value);
|
||||
|
||||
case 'number':
|
||||
return isValidNumber(constraint, value);
|
||||
|
||||
case 'enum':
|
||||
return isValidEnum(constraint, value);
|
||||
|
||||
case 'string':
|
||||
return isValidString(constraint, value);
|
||||
}
|
||||
|
||||
Logger.tag('validation').error('No validation method for constraint type: {}', constraint.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
function areValid(constraints, acceptUndefined, values) {
|
||||
const fields = Object.keys(constraints);
|
||||
for (let i = 0; i < fields.length; i ++) {
|
||||
const field = fields[i];
|
||||
if (!isValid(constraints[field], acceptUndefined, values[field])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
forConstraint (constraint, acceptUndefined) {
|
||||
return _.partial(isValid, constraint, acceptUndefined);
|
||||
},
|
||||
forConstraints (constraints, acceptUndefined) {
|
||||
return _.partial(areValid, constraints, acceptUndefined);
|
||||
}
|
||||
}
|
121
server/validation/validator.ts
Normal file
121
server/validation/validator.ts
Normal file
|
@ -0,0 +1,121 @@
|
|||
import _ from "lodash";
|
||||
|
||||
import {parseInteger} from "../utils/strings";
|
||||
import Logger from "../logger";
|
||||
|
||||
interface Constraint {
|
||||
type: string,
|
||||
|
||||
optional?: boolean,
|
||||
|
||||
allowed?: string[],
|
||||
|
||||
min?: number,
|
||||
max?: number,
|
||||
|
||||
regex?: RegExp,
|
||||
}
|
||||
|
||||
type Constraints = {[key: string]: Constraint};
|
||||
type Values = {[key: string]: any};
|
||||
|
||||
// TODO: sanitize input for further processing as specified by constraints (correct types, trimming, etc.)
|
||||
|
||||
function isValidBoolean(value: any): boolean {
|
||||
return _.isBoolean(value) || value === 'true' || value === 'false';
|
||||
}
|
||||
|
||||
function isValidNumber(constraint: Constraint, value: any): boolean {
|
||||
if (_.isString(value)) {
|
||||
value = parseInteger(value);
|
||||
}
|
||||
|
||||
if (!_.isNumber(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_.isNaN(value) || !_.isFinite(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_.isNumber(constraint.min) && value < constraint.min) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// noinspection RedundantIfStatementJS
|
||||
if (_.isNumber(constraint.max) && value > constraint.max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isValidEnum(constraint: Constraint, value: any): boolean {
|
||||
if (!_.isString(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _.indexOf(constraint.allowed, value) >= 0;
|
||||
}
|
||||
|
||||
function isValidString(constraint: Constraint, value: any): boolean {
|
||||
if (!constraint.regex) {
|
||||
throw new Error("String constraints must have regex set: " + constraint);
|
||||
}
|
||||
|
||||
if (!_.isString(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const trimmed = value.trim();
|
||||
return (trimmed === '' && constraint.optional) || constraint.regex.test(trimmed);
|
||||
}
|
||||
|
||||
function isValid(constraint: Constraint, acceptUndefined: boolean, value: any): boolean {
|
||||
if (value === undefined) {
|
||||
return acceptUndefined || constraint.optional === true;
|
||||
}
|
||||
|
||||
switch (constraint.type) {
|
||||
case 'boolean':
|
||||
return isValidBoolean(value);
|
||||
|
||||
case 'number':
|
||||
return isValidNumber(constraint, value);
|
||||
|
||||
case 'enum':
|
||||
return isValidEnum(constraint, value);
|
||||
|
||||
case 'string':
|
||||
return isValidString(constraint, value);
|
||||
}
|
||||
|
||||
Logger.tag('validation').error('No validation method for constraint type: {}', constraint.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
function areValid(constraints: Constraints, acceptUndefined: boolean, values: Values): boolean {
|
||||
const fields = new Set(Object.keys(constraints));
|
||||
for (const field of fields) {
|
||||
if (!isValid(constraints[field], acceptUndefined, values[field])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const field of Object.keys(values)) {
|
||||
if (!fields.has(field)) {
|
||||
Logger.tag('validation').error('Validation failed: No constraint for field: {}', field);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function forConstraint (constraint: Constraint, acceptUndefined: boolean): (value: any) => boolean {
|
||||
return ((value: any): boolean => isValid(constraint, acceptUndefined, value));
|
||||
}
|
||||
|
||||
export function forConstraints (constraints: Constraints, acceptUndefined: boolean): (values: Values) => boolean {
|
||||
return ((values: Values): boolean => areValid(constraints, acceptUndefined, values));
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
var constraints = {
|
||||
id:{
|
||||
type: 'string',
|
||||
regex: /^[1-9][0-9]*/,
|
||||
regex: /^[1-9][0-9]*$/,
|
||||
optional: false
|
||||
},
|
||||
token:{
|
||||
|
|
Loading…
Reference in a new issue