parameter-validator
Version:
Parameter validator makes it easy to verify that an object contains required, valid parameters.
424 lines (353 loc) • 19.7 kB
JavaScript
define(['exports'], function (exports) {
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
function _toConsumableArray(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
arr2[i] = arr[i];
}
return arr2;
} else {
return Array.from(arr);
}
}
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
var ParameterValidationError = exports.ParameterValidationError = function (_Error) {
_inherits(ParameterValidationError, _Error);
function ParameterValidationError(message) {
_classCallCheck(this, ParameterValidationError);
var _this = _possibleConstructorReturn(this, (ParameterValidationError.__proto__ || Object.getPrototypeOf(ParameterValidationError)).call(this, message));
_this.name = _this.constructor.name;
_this.message = message;
if (Error.captureStackTrace) {
Error.captureStackTrace(_this, _this.constructor.name);
}
return _this;
}
return ParameterValidationError;
}(Error);
var ParameterValidator = function () {
/**
* @param {object} [options]
* @param {function} [options.defaultValidation] - An optional alternate function to use as the default validation
* function instead of isDefined(). The function must accept a parameter
* value as an input and return a boolean indicating its validity.
*/
function ParameterValidator(options) {
_classCallCheck(this, ParameterValidator);
if (options) {
var defaultValidation = options.defaultValidation;
if (!(defaultValidation === undefined || typeof defaultValidation === 'function')) {
throw new ParameterValidationError('The optional defaultValidation parameter provided is not a function.');
}
this._defaultValidation = options.defaultValidation;
}
}
/**
* @param {Object} paramsProvided - The names and values of provided parameters
* @param {Array} paramRequirements - Each item in this array is interpretted in order as a validation rule.
* - If an item is a string, it's interpretted as the name of a parameter that must be in paramsProvided.
* - If an item is an Array, it's interpretted as an array of parameter names where at least one of the
* parameters in the Array must be in paramsProvided.
* - If an item is an Object, it's assumed that the object's only key is the name of a parameter to be validated
* and its corresponding value is a function that returns true if that parameter's value in paramsProvided is
* valid.
* @param {Object|null} [extractedParams] - This method returns an object containing the names and values of the validated parameters extracted.
* By default, it creates a new object and assigns the extracted parameters to it, but if you want this
* method to add the extracted params to an existing object (such as the class instance that internally
* invokes this method), you can supply that object as the extractedParams parameter.
*
* @param {Object} [options] - Additional options
* @param {string} [options.addPrefix] - Specifies a prefix that will be added to each param name before it's assigned to the
* extractedParams object. This is useful, for example, for prefixing property names with an underscore
* to indicate that they're private properties.
* @param {class} [options.errorClass] - Specifies a specific `Error` subclass to throw instead of the default `ParameterValidationError
* when invalid parameters are detected.
* @returns {Object} extractedParams - The names and values of the validated parameters extracted.
*
* @throws {ParameterValidationError} Indicates that one or more parameter validation rules failed.
*
* @example
* let parameterValidator = new ParameterValidator();
* parameterValidator.validate(params, ['requiredParam0', 'requiredParam1', ['eitherNeedThis', 'orThat'], {param3: (val) => val > 30}]);
*/
_createClass(ParameterValidator, [{
key: 'validate',
value: function validate(paramsProvided, paramRequirements, extractedParams) {
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
extractedParams = this._getExtractedParamsObject(extractedParams);
var ValidationErrorSubclass = this._getValidationErrorSubclass(options);
if (!paramsProvided) {
// If only I could use the ParameterValidator here...
throw new ValidationErrorSubclass('A params object is required.');
}
if (!Array.isArray(paramRequirements)) {
throw new Error('paramRequirements must be an array.');
}
var prefix = options.addPrefix || ''; // Optional prefix to be added to each parameter name.
if (typeof prefix !== 'string') {
throw new Error('addPrefix option must be a string if provided.');
}
var errors = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = paramRequirements[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var paramRequirement = _step.value;
if (Array.isArray(paramRequirement) && paramRequirement.length) {
var validationResult = this._performLogicalOrParamValidation(paramsProvided, paramRequirement);
this._assignProperties(extractedParams, validationResult.params, prefix);
errors.push.apply(errors, _toConsumableArray(validationResult.errors));
} else if ((typeof paramRequirement === 'undefined' ? 'undefined' : _typeof(paramRequirement)) === 'object') {
// paramRequirement is an object with one or more keys where each key is a parameter's name
// and its value is a validation function that returns true if the value is valid.
for (var paramName in paramRequirement) {
var validationFunction = paramRequirement[paramName],
_validationResult = this._executeValidationFunction(paramsProvided, paramName, validationFunction);
this._assignProperties(extractedParams, _validationResult.params, prefix);
errors.push.apply(errors, _toConsumableArray(_validationResult.errors));
}
} else if (typeof paramRequirement === 'string' && paramRequirement) {
// paramRequirement is a string specifying the name of a required parameter,
// So use the default validation function for validation.
var _validationResult2 = this._executeValidationFunction(paramsProvided, paramRequirement, this.defaultValidation);
this._assignProperties(extractedParams, _validationResult2.params, prefix);
errors.push.apply(errors, _toConsumableArray(_validationResult2.errors));
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (errors.length) {
var errorMessage = '';
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = errors[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var error = _step2.value;
errorMessage += error + ' ';
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
errorMessage = errorMessage.slice(0, -1);
throw new ValidationErrorSubclass(errorMessage);
}
return extractedParams;
}
}, {
key: 'validateAsync',
value: function validateAsync() {
var _this2 = this;
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return Promise.resolve().then(function () {
return _this2.validate.apply(_this2, args);
});
}
}, {
key: '_assignProperties',
value: function _assignProperties(targetObject, propertiesToAdd) {
var prefix = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
for (var propertyName in propertiesToAdd) {
targetObject[prefix + propertyName] = propertiesToAdd[propertyName];
}
}
}, {
key: '_getExtractedParamsObject',
value: function _getExtractedParamsObject(extractedParams) {
if ([null, undefined].includes(extractedParams)) {
// The client either didn't provide an extractedParams argument or explicitly set
// it to null, so just use a new object.
return {};
}
if (['object', 'function'].includes(typeof extractedParams === 'undefined' ? 'undefined' : _typeof(extractedParams))) {
// The client provided an existing object or function on which we'll set the extracted params.
return extractedParams;
}
throw new Error('Invalid value of \'' + extractedParams + '\' was provided for the extractedParams parameter.');
}
}, {
key: '_getValidationErrorSubclass',
value: function _getValidationErrorSubclass(options) {
if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) !== 'object' || options.errorClass === undefined) {
return ParameterValidationError;
}
var errorClass = options.errorClass;
// Verify that errorClass is Error or an Error subclass.
if (errorClass === Error || errorClass.prototype instanceof Error) {
return errorClass;
}
throw new Error('The errorClass provided was of type ' + (typeof errorClass === 'undefined' ? 'undefined' : _typeof(errorClass)) + ' and was not an Error subclass.');
}
}, {
key: '_performLogicalOrParamValidation',
value: function _performLogicalOrParamValidation(paramsProvided, paramNames) {
var extractedParams = {},
errors = [],
isValid = this.defaultValidation;
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = paramNames[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var paramName = _step3.value;
if (isValid(paramsProvided[paramName])) {
extractedParams[paramName] = paramsProvided[paramName];
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
if (!Object.keys(extractedParams).length) {
var errorMessage = 'One of the following parameters must be included: ';
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = paramNames[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
paramName = _step4.value;
errorMessage += '\'' + paramName + '\', ';
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
errorMessage = errorMessage.slice(0, -2) + '.';
errors.push(errorMessage);
}
return {
errors: errors,
params: extractedParams
};
}
}, {
key: '_executeValidationFunction',
value: function _executeValidationFunction(paramsProvided, paramName, validationFunction) {
var errors = [];
var extractedParams = {};
if (typeof validationFunction !== 'function') {
throw new Error('A paramRequirement value provided for the parameter ' + paramName + ' is not a function.');
}
if (validationFunction(paramsProvided[paramName]) === true) {
extractedParams[paramName] = paramsProvided[paramName];
} else {
errors.push('Invalid value of \'' + paramsProvided[paramName] + '\' was provided for parameter \'' + paramName + '\'.');
}
return {
errors: errors,
params: extractedParams
};
}
}, {
key: 'isDefined',
value: function isDefined(value) {
return value !== undefined;
}
}, {
key: 'defaultValidation',
get: function get() {
return this._defaultValidation || this.isDefined;
}
}]);
return ParameterValidator;
}();
exports.default = ParameterValidator;
// Also export `validate()` and `validateAsync` as standalone functions by creating a singleton instance.
var parameterValidator = new ParameterValidator();
var validate = exports.validate = parameterValidator.validate.bind(parameterValidator);
var validateAsync = exports.validateAsync = parameterValidator.validateAsync.bind(parameterValidator);
});