UNPKG

@boomerang-io/utils

Version:

A library of reusable utilities and hooks for React webapps on the Boomerang platform.

244 lines (222 loc) 8.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.transform = transform; exports.transformAll = transformAll; exports.transformObject = transformObject; var yup = _interopRequireWildcard(require("yup")); var _customValidators = require("./customValidators"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } class ValidationError { constructor(message) { this.message = message; } } /** * Searches for {substring} in {string}. * If found, returns the {string}, sliced after substring. * * @param {string} string - String to be sliced. * @param {string} substring - String to search for. * @returns {string|null} Null if no match found. */ function getSubString(string, substring) { if (!string) return null; const testedIndex = string.indexOf(substring); if (testedIndex > -1) { return string.slice(testedIndex + substring.length); } return null; } /** * Returns a function from yup, by passing in a function name from our schema. * @param {string} functionName - The string to search for a function. * @param {Object} previousInstance - Object from previous validator result or yup itself. * @returns {Function} Either the found yup function or the default validator. */ function getYupFunction(functionName) { let previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; // Make sure we're dealing with a string if (functionName instanceof Array) { functionName = functionName[0]; } // Attempt to retrieve any custom validators first const customValidator = (0, _customValidators.getCustomValidator)(functionName); if (customValidator) { return customValidator; } const yupName = getSubString(functionName, "yup."); if (yupName && previousInstance[yupName] instanceof Function) { return previousInstance[yupName].bind(previousInstance); } if (yupName && yup[yupName] instanceof Function) { return yup[yupName].bind(yup); } throw new ValidationError("Could not find validator " + functionName); } /** * Here we check to see if a passed array could be a prefix notation function. * @param {Array} item - Item to be checked. * @param {any} item.functionName - We'll check this, and perhaps it's a prefix function name. * @returns {boolean} True if we are actually looking at prefix notation. */ function isPrefixNotation(_ref) { let [functionName] = _ref; if (functionName instanceof Array) { if (isPrefixNotation(functionName)) return true; } if (typeof functionName !== "string") return false; if (functionName.indexOf("yup.") < 0) return false; return true; } /** * Ensure that the argument passed is an array. * @param {Any} maybeArray - To be checked. * @returns {Array} forced to array. */ function ensureArray(maybeArray) { if (maybeArray instanceof Array === false) { return [maybeArray]; } return maybeArray; } /** * Converts an array of ['yup.number'] to yup.number(). * @param {[Any]} arrayArgument - The validation array. * @param {Object} previousInstance - The result of a call to yup.number() * i.e. an object schema validation set * @returns {Function} generated yup validator */ function convertArray(arrayArgument) { let previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; const [functionName, ...argsToPass] = ensureArray(arrayArgument); // Handle the case when we have a previous instance // but we don't want to use it for this transformation // [['yup.array'], ['yup.of', [['yup.object'], ['yup.shape'] ...]]] if (functionName instanceof Array) { return transformArray(arrayArgument); } const gotFunc = getYupFunction(functionName, previousInstance, arrayArgument); // Here we'll actually call the function // This might be something like yup.number().min(5) // We could be passing different types of arguments here // so we'll try to transform them before calling the function // yup.object().shape({ test: yup.string()}) should also be transformed const convertedArguments = transformAll(argsToPass, previousInstance); // Handle the case when we've got an array of empty elements if (convertedArguments instanceof Array) { if (convertedArguments.filter(i => i).length < 1) { return gotFunc(); } // Spread the array over the function return gotFunc(...convertedArguments); } // Handle the case when we're passing another validator return gotFunc(convertedArguments); } /** * Transforms an array JSON schema to yup array schema. * * @param {Array} jsonArray - array in JSON to be transformed. * @returns {Array} Array with same keys, but values as yup validators. */ function transformArray(jsonArray) { let previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; let toReturn = convertArray(jsonArray[0]); jsonArray.slice(1).forEach(item => { // Found an array, move to prefix extraction if (item instanceof Array) { toReturn = convertArray(item, toReturn); return; } // Found an object, move to object extraction if (item instanceof Object) { toReturn = transformObject(item, previousInstance); return; } // Handle an edge case where we have something like // [['yup.ref', 'linkName'], 'custom error'], and we don't want // to consume 'custom error as a variable yet' if (toReturn instanceof Array) { toReturn = toReturn.concat(item); return; } toReturn = [toReturn, item]; }); return toReturn; } /** * Transforms an object JSON schema to yup object schema. * * @param {Object} jsonObject - Object in JSON to be transformed. * @returns {Object} Object with same keys, but values as yup validators. */ function transformObject(jsonObject) { let previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; const toReturn = {}; Object.entries(jsonObject).forEach(_ref2 => { let [key, value] = _ref2; // Found an array move to array extraction if (value instanceof Array) { toReturn[key] = transformArray(value, previousInstance); return; } // Found an object recursive extraction if (value instanceof Object) { toReturn[key] = transformObject(value, previousInstance); return; } toReturn[key] = value; }); return toReturn; } /** * Steps into arrays and objects and resolve the items inside to yup validators. * @param {Any} jsonObjectOrArray - Object to be transformed. * @returns {yup.Validator} */ function transformAll(jsonObjectOrArray) { let previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; // We're dealing with an array // This could be a prefix notation function // If so, we'll call the converter if (jsonObjectOrArray instanceof Array) { if (isPrefixNotation(jsonObjectOrArray)) { return transformArray(jsonObjectOrArray, previousInstance); } return jsonObjectOrArray.map(i => transformAll(i, previousInstance)); } // If we're dealing with an object // we should check each of the values for that object. // Some of them may also be prefix notation functiosn if (jsonObjectOrArray instanceof Object) { return transformObject(jsonObjectOrArray, previousInstance); } // No case here, just return anything else return jsonObjectOrArray; } /** * Can transform arrays or an object into a single validator. * This should be your initial entrypoint. * * @param {Any} jsonObjectOrArray - Object to be transformed. * @returns {yup.Validator} */ function transform(jsonObjectOrArray) { try { if (jsonObjectOrArray instanceof Object) { return transformAll([ // build a custom validator which takes an object as parameter // If we don't do this, we'll get back an object of validators ["yup.object"], ["yup.required"], ["yup.shape", jsonObjectOrArray]]); } // No case here, just return anything else return transformAll(jsonObjectOrArray); } catch (error) { if (error instanceof ValidationError) { throw new Error("Could not validate " + JSON.stringify(jsonObjectOrArray, null, 4) + "\n".concat(error.message)); } throw error; } }