@kartotherian/input-validator
Version:
Functions to assert input correctness for Kartotherian
205 lines (191 loc) • 6.34 kB
JavaScript
;
let _ = require('underscore'),
qidx = require('quadtile-index'),
qs = require('querystring'),
urllib = require('url'),
Err = require('@kartotherian/err');
module.exports = checkType;
function getDefault(obj, field, mustHave) {
if (mustHave === true) {
throw new Err('Value %j is missing', field);
}
if (mustHave === false || mustHave === undefined) {
delete obj[field];
} else {
obj[field] = mustHave;
}
return false;
}
/**
* Utility method to check the type of the object's property
*/
function checkType(obj, field, expType, mustHave, min, max) {
let value = typeof (obj) === 'object' ? obj[field] : obj;
if (value === undefined) {
return getDefault(obj, field, mustHave);
}
// Try to convert value to expected type
let type = expType[0] === '[' ? Object.prototype.toString.call(value) : typeof value;
if (type === 'string') {
switch (expType) {
case 'number':
value = checkType.strToFloat(value);
type = typeof value;
break;
case 'integer':
case 'zoom':
obj[field] = value = checkType.strToInt(value);
type = typeof value;
break;
case 'boolean':
obj[field] = value = !!value;
type = typeof value;
break;
case 'string-array':
obj[field] = value = [value];
type = typeof value;
break;
case 'number-array':
value = checkType.strToFloat(value);
type = typeof value;
if (type === 'number') {
obj[field] = value = [value];
}
break;
}
} else if (type === 'number' && expType === 'number-array') {
obj[field] = value = [value];
type = typeof value;
}
// validate the type
switch (expType) {
case 'string-array':
if (!Array.isArray(value) ||
!_.all(value, v => typeof v === 'string' && v.length > 0)
) {
throw new Err('Invalid %s param: expecting a string or an array of strings', field);
}
break;
case 'number-array':
let isValid = Array.isArray(value);
if (isValid) {
value = _.map(value, v => {
v = checkType.strToFloat(v);
if (typeof v !== 'number') {
isValid = false;
}
return v;
});
}
if (!isValid) {
throw new Err('Invalid %s param: expecting a number or an array of numbers', field);
}
obj[field] = value;
break;
case 'array':
if (!Array.isArray(value)) {
throw new Err('Invalid %s param type %s given, was expecting an array',
field, type);
}
break;
case 'integer':
if (!Number.isInteger(value)) {
throw new Err('Invalid %s param type %s given, was expecting an integer',
field, type);
}
break;
case 'zoom':
if (!qidx.isValidZoom(value)) {
throw new Err('Invalid %s param - an integer zoom value was expected', field);
}
break;
default:
if (type !== expType) {
throw new Err('Invalid %s param type %s given, was expecting %s',
field, type, expType);
}
break;
}
// validate ranges
switch (expType) {
case 'number':
case 'integer':
case 'zoom':
if (min !== undefined && value < min) {
throw new Err('Invalid %s param - must be at least %d, but given %d',
field, min, value);
}
if (max !== undefined && value > max) {
throw new Err('Invalid %s param - must be at most %d, but given %d',
field, max, value);
}
break;
case 'string':
if (min !== undefined && value.length < min) {
throw new Err('Invalid %s param - the string must be at least %d symbols',
field, min);
}
break;
case 'boolean':
if (value === false) {
// convert false into undefined
delete obj[field];
return false;
}
break;
case 'string-array':
case 'number-array':
if (min !== undefined && value.length < min) {
throw new Err('Invalid %s param - it must have at least %d values, but given %d',
field, min, value.length);
}
if (max !== undefined && value.length > max) {
throw new Err('Invalid %s param - it must have at least %d values, but given %d',
field, max, value.length);
}
break;
}
return true;
}
/**
* Magical float regex found in http://stackoverflow.com/a/21664614/177275
* @type {RegExp}
*/
checkType.floatRe = /^-?\d+(?:[.,]\d*?)?$/;
/**
* Converts value to float if possible, or returns the original
*/
checkType.strToFloat = function strToFloat(value) {
if (typeof value === 'string' && checkType.floatRe.test(value)) {
return parseFloat(value);
}
return value;
};
/**
* Magical int regex
* @type {RegExp}
*/
const intRe = /^-?\d+$/;
/**
* Converts value to integer if possible, or returns the original
*/
checkType.strToInt = function strToInt(value) {
if (typeof value === 'string' && intRe.test(value)) {
return parseInt(value);
}
return value;
};
/**
* Parse and normalize URI, ensuring it returns an object with query object field
* @param uri
* @returns {*}
*/
checkType.normalizeUrl = function normalizeUrl(uri) {
if (typeof uri === 'string') {
uri = urllib.parse(uri, true);
} else if (typeof uri.query === 'string') {
uri.query = qs.parse(uri.query);
}
uri.query = uri.query || {};
return uri;
};