/** * @author d98762625 [d98762625@gmail.com] * @copyright Crown Copyright 2018 * @license Apache-2.0 */ import {operations} from "./index.mjs"; import { sanitise } from "./apiUtils.mjs"; /** * Similar to core/Recipe, Recipe controls a list of operations and * the NodeDish the operate on. However, this Recipe is for the node * environment. */ class NodeRecipe { /** * Recipe constructor * @param recipeConfig */ constructor(recipeConfig) { this._parseConfig(recipeConfig); } /** * Validate an ingredient & coerce to operation if necessary. * @param {String | Function | Object} ing * @returns {Function || Object} The operation, or an object with the * operation and its arguments * @throws {TypeError} If it cannot find the operation in chef's list of operations. */ _validateIngredient(ing) { // CASE operation name given. Find operation and validate if (typeof ing === "string") { const op = operations.find((op) => { return sanitise(op.opName) === sanitise(ing); }); if (op) { // Need to validate against case 2 return this._validateIngredient(op); } else { throw new TypeError(`Couldn't find an operation with name '${ing}'.`); } // CASE operation given. Check its a chef operation and check its not flowcontrol } else if (typeof ing === "function") { if (ing.flowControl) { throw new TypeError(`flowControl operations like ${ing.opName} are not currently allowed in recipes for chef.bake in the Node API`); } if (operations.includes(ing)) { return ing; } else { throw new TypeError("Inputted function not a Chef operation."); } // CASE: op, maybe with configuration } else if (ing.op) { const sanitisedOp = this._validateIngredient(ing.op); if (ing.args) { return {op: sanitisedOp, args: ing.args}; } return sanitisedOp; } else { throw new TypeError("Recipe can only contain function names or functions"); } } /** * Parse an opList from a recipeConfig and assign it to the recipe's opList. * @param {String | Function | String[] | Function[] | [String | Function]} recipeConfig */ _parseConfig(recipeConfig) { if (!recipeConfig) { this.opList = []; return; } if (!Array.isArray(recipeConfig)) { recipeConfig = [recipeConfig]; } this.opList = recipeConfig.map((ing) => this._validateIngredient(ing)); } /** * Run the dish through each operation, one at a time. * @param {NodeDish} dish * @returns {NodeDish} */ execute(dish) { return this.opList.reduce((prev, curr) => { // CASE where opList item is op and args if (Object.prototype.hasOwnProperty.call(curr, "op") && Object.prototype.hasOwnProperty.call(curr, "args")) { return curr.op(prev, curr.args); } // CASE opList item is just op. return curr(prev); }, dish); } } export default NodeRecipe;