UNPKG

usher

Version:

Simple DSL for composing decision workflows for AWS Simple Workflow

443 lines (356 loc) 14.7 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: decider/fragment.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: decider/fragment.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/*! * Usher * Copyright(c) 2014 meltmedia &lt;mike@meltmedia.com> */ 'use strict'; var util = require('util'), events = require('events'), _ = require('lodash'), sequencify = require('@meltmedia/sequencify'), Activity = require('./tasks/activity'), ChildWorkflow = require('./tasks/child'), Loop = require('./tasks/loop'), Accumulator = require('./tasks/accumulator'), WhileLoop = require('./tasks/while-loop'), Decision = require('./tasks/decision'), Transform = require('./tasks/transform'), Result = require('./tasks/result'), Variable = require('./tasks/variable'), Terminate = require('./tasks/terminate'); module.exports = Fragment; /** * Represents a single, named workflow, where all activities and decisions are defined. * @constructor * @param {string} name - The name of the workflow. * @param {string} domain - The AWS SWF domain name to execute this workflow in. * @param {string} [tasklist=&lt;name>-tasklist] - The name of the tasklist to listen for tasks on. */ function Fragment(name) { if (!(this instanceof Fragment)) { return new Fragment(name); } events.EventEmitter.call(this); if (!_.isString(name)) { throw new Error('A `name` is required'); } this.name = name; this.activityOptions = {}; /** @private */ this._tasks = []; this._sequencedTasks = []; } // Make Fragment an EventEmitter util.inherits(Fragment, events.EventEmitter); /** * Get the defined tasks for the fragment * @returns {Array} An array of defined tasks */ Fragment.prototype.tasks = function tasks() { return this._tasks; }; /** * Get the defined tasks for the fragment in execution order * @returns {Array} An array of defined tasks */ Fragment.prototype.sequencedTasks = function sequencedTasks() { if (this._sequencedTasks &amp;&amp; this._sequencedTasks.length > 0) { return this._sequencedTasks; } // Map tasks into sequencify's input format var names = [], items = {}; _.each(this.tasks(), function (item) { names.push(item.name); items[item.name] = { name: item.name, dep: item.deps, task: item }; }); var results = sequencify(items, names); // Required dependencies not defined if (!_.isEmpty(results.missingTasks)) { throw new Error('The workflow is missing tasks: ' + JSON.stringify(results.missingTasks)); } // Recursive dependencies found if (!_.isEmpty(results.recursiveDependencies)) { throw new Error('The workflow has recursive dependencies: ' + JSON.stringify(results.recursiveDependencies)); } // Cache sequenced tasks this._sequencedTasks = _.map(results.sequence, function (item) { return items[item].task; }); return this._sequencedTasks; }; /** * Set global activity options for the entire workflow * @param {Object} options - Custom options to use for all defined activities. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.activityDefaults = function activityDefaults(options) { if (_.isPlainObject(options)) { this.activityOptions = options; } return this; // chainable }; /** * Add an activity to the workflow * @param {string} name - The unique name of the activity. * @param {Array} [deps] - The names of the dependencies that must be met before this activity can execute. * @param {Object} [options] - Custom options for this activity. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.activity = function activity(name, deps, options) { if (!options &amp;&amp; !_.isArray(deps)) { options = deps; deps = undefined; } deps = deps || []; options = options || {}; options = _.defaults(options, this.activityOptions); var task = new Activity(name, deps, options); this._tasks.push(task); // Add the activity this._sequencedTasks = null; return this; // chainable }; /** * Add an child workflow execution to the workflow * @param {string} name - The unique name of the child workflow. * @param {Array} [deps] - The names of the dependencies that must be met before this activity can execute. * @param {string} workflowName - The name of the workflow to execute. * @param {string} workflowVersion - The version of the workflow to execute. * @param {Object} [options] - Custom options for this activity. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.child = function child(name, deps, workflowName, workflowVersion, options) { if (!options &amp;&amp; !_.isArray(deps)) { options = workflowVersion; workflowVersion = workflowName; workflowName = deps; deps = undefined; } deps = deps || []; options = options || {}; var task = new ChildWorkflow(name, deps, workflowName, workflowVersion, options); this._tasks.push(task); // Add the child workflow this._sequencedTasks = null; return this; // chainable }; /** * Add a looping workflow execution to the workflow * * The loop executes in batches to help alleviate rate limit exceptions. * The number of items to proccess per batch and the delay between batches are both configurable. * * @param {string} name - The unique name of the loop fragment. * @param {Array} [deps] - The names of the dependencies that must be met before this activity can execute. * @param {Fragment} fragment - The workflow fragment to loop over. Generated by `usher.fragment()` * @param {Function} loopFn - A function given the taks's input that returns an array. For every item in the Array an * execution of the `fragment` workflow will execute. * @param {Object} [options] - Custom options for this activity. [itemsPerBatch, batchDelay] * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.loop = function loop(name, deps, fragment, loopFn, options) { if (!options &amp;&amp; !_.isArray(deps)) { options = loopFn; loopFn = fragment; fragment = deps; deps = undefined; } deps = deps || []; var task = new Loop(name, deps, fragment, loopFn, options); this._tasks.push(task); // Add the loop this._sequencedTasks = null; return this; // chainable }; /** * Add a while loop workflow execution to the workflow * * The loop executes until the `doneFn` returns a truthy value * * @param {string} name - The unique name of the while loop fragment. * @param {Array} [deps] - The names of the dependencies that must be met before this activity can execute. * @param {Fragment} fragment - The workflow fragment to loop over. Generated by `usher.fragment()` * @param {Function} doneFn - Indicates when the loop is complete by returning a truthy value. * @param {Object} [options] - Custom options for this activity. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.whileLoop = function whileLoop(name, deps, fragment, doneFn, options) { if (!options &amp;&amp; !_.isArray(deps)) { options = doneFn; doneFn = fragment; fragment = deps; deps = undefined; } deps = deps || []; var task = new WhileLoop(name, deps, fragment, doneFn, options); this._tasks.push(task); // Add the while loop this._sequencedTasks = null; return this; // chainable }; /** * Add a accumulator execution to the workflow * * The accumulator executes a fragment until the `doneFn` returns a truthy value, taking the results * of each iteration and making them available to any dependents * * @param {string} name - The unique name of the accumulator fragment. * @param {Array} [deps] - The names of the dependencies that must be met before this activity can execute. * @param {Fragment} fragment - The workflow fragment to iterate over. Generated by `usher.fragment()` * @param {Function} doneFn - Indicates when the accumulator is complete by returning a truthy value. * @param {Object} [options] - Custom options for this activity. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.accumulator = function accumulator(name, deps, fragment, doneFn, options) { if (!options &amp;&amp; !_.isArray(deps)) { options = doneFn; doneFn = fragment; fragment = deps; deps = undefined; } deps = deps || []; var task = new Accumulator(name, deps, fragment, doneFn, options); this._tasks.push(task); // Add the while loop this._sequencedTasks = null; return this; // chainable }; /** * Add a decision to the workflow * @param {string} name - The unique name of the decision. * @param {Array} [deps] - The names of the dependencies that must be met before this decision can execute. * @param {Fragment~decisionLogic} decisionFn - The logic for this decision. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.decision = function decision(name, deps, decisionFn) { if (!decisionFn &amp;&amp; !_.isArray(deps)) { decisionFn = deps; deps = undefined; } deps = deps || []; decisionFn = decisionFn || function () { return true; }; var task = new Decision(name, deps, decisionFn); this._tasks.push(task); // Add the decision this._sequencedTasks = null; return this; // chainable }; /** * The decision logic to execute when evaluating the given named decision * @callback Fragment~decisionLogic * @param {Object} input - The results of all dependencies for this decision * @return {Boolean} Should dependents of this decision execute */ /** * Add a termination point to the workflow * @param {string} name - The unique name that represents this termination point. * @param {Array} [deps] - The names of the dependencies that must be met before the workflow can terminate. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.terminate = function terminate(name, deps) { deps = deps || []; var task = new Terminate(name, deps); this._tasks.push(task); // Add the terminate task this._sequencedTasks = null; return this; // chainable }; /** * Add a transformation to the workflow. Transformations are good for manipulating the results of prior activities into new representations for future dependents. * @param {string} name - The unique name of the transformation. * @param {Array} [deps] - The names of the dependencies that must be met before this decision can execute. * @param {Fragment~transformationLogic} [transformFn] - The funtion that will perform the transformation. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.transform = function transform(name, deps, transformFn) { if (!transformFn &amp;&amp; !_.isArray(deps)) { transformFn = deps; deps = undefined; } deps = deps || []; transformFn = transformFn || function (input) { return input; }; var task = new Transform(name, deps, transformFn); this._tasks.push(task); // Add the transformation this._sequencedTasks = null; return this; // chainable }; /** * The tranformation to execute * @callback Fragment~transformationLogic * @param {Object} input - The results of all dependencies for this transformation * @return {*} The transformed input */ /** * Add a result transformation to the workflow. This sets the fragments results based on the `transformFn` * @param {string} name - The unique name of the result task. * @param {Array} [deps] - The names of the dependencies that must be met before this decision can execute. * @param {Fragment~transformationLogic} [transformFn] - The funtion that will perform the transformation. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.result = function result(name, deps, transformFn) { if (!transformFn &amp;&amp; !_.isArray(deps)) { transformFn = deps; deps = undefined; } deps = deps || []; transformFn = transformFn || function (input) { return input; }; var task = new Result(name, deps, transformFn); this._tasks.push(task); // Add the transformation this._sequencedTasks = null; return this; // chainable }; /** * Set an variable accessable to all future scheduled activities * @param {string} name - The unique name of the variable. * @param {Array} [deps] - The names of the dependencies that must be met before this variable can be set. * @param {Fragment~variableValueFunction} [valueFn] - The funtion that will calculate the variable name. * @returns {Fragment} This workflow so you can chain commands. */ Fragment.prototype.variable = function variable(name, deps, valueFn) { if (!valueFn &amp;&amp; !_.isArray(deps)) { valueFn = deps; deps = undefined; } deps = deps || []; valueFn = valueFn || function (input) { return input; }; var task = new Variable(name, deps, valueFn); this._tasks.push(task); // Add the variable task this._sequencedTasks = null; return this; // chainable }; /** * A function to determine the value of the variable * @callback Fragment~variableValueFunction * @param {Object} input - The results of all dependencies for this variable * @return {*} The variable value */ </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Index</a></h2><h3>Classes</h3><ul><li><a href="Accumulator.html">Accumulator</a></li><li><a href="ActivityPoller.html">ActivityPoller</a></li><li><a href="ActivityTask.html">ActivityTask</a></li><li><a href="DecisionPoller.html">DecisionPoller</a></li><li><a href="Fragment.html">Fragment</a></li><li><a href="Loop.html">Loop</a></li><li><a href="Usher.html">Usher</a></li><li><a href="WhileLoop.html">WhileLoop</a></li><li><a href="WorkflowVersion.html">WorkflowVersion</a></li></ul> </nav> <br clear="both"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha5</a> on Mon Sep 12 2016 14:59:00 GMT-0700 (MST) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>