UNPKG

beelzebub

Version:
1,568 lines (1,319 loc) 44.1 kB
'use strict'; var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); var _getOwnPropertyNames = require('babel-runtime/core-js/object/get-own-property-names'); var _getOwnPropertyNames2 = _interopRequireDefault(_getOwnPropertyNames); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _ = require('lodash'); var when = require('when'); var co = require('co'); var chalk = require('chalk'); var whenSeq = require('when/sequence'); var streamToPromise = require('stream-to-promise'); var manifest = require('../package.json'); var bzStats = require('./bzStats.js'); var util = require('./util.js'); /** * Beelzebub Task Class, should be extended * @class */ var BzTasks = function () { function BzTasks(config) { var hidden = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; (0, _classCallCheck3.default)(this, BzTasks); this._hidden = hidden; this.beelzebub = config.beelzebub || util.getInstance(); util.processConfig(config, this.beelzebub.getConfig(), this); this.name = config.name || this.constructor.name || 'BzTasks'; this.version = manifest.version; this.namePath = this._buildNamePath(config); // this.vLogger.log('constructor namePath:', this.namePath, ', name:', this.name); // TODO: use config function/util to process this this._rootLevel = false; this._defaultTaskFuncName = this.$defaultTask || null; this._tasks = {}; this._subTasks = {}; this._running = null; this._beforeAllRun = false; this._stats = new bzStats.Task(); // TODO: add cli options/commands } /** * Util to build Name Path * TODO: move this out of the class * @private */ (0, _createClass3.default)(BzTasks, [{ key: '_buildNamePath', value: function _buildNamePath(config) { var namePath = this.name; if (config.parentPath) { namePath = config.parentPath + '.' + namePath; } return namePath; } /** * Get Task Tree starting with this task * @returns {object} */ }, { key: '$getTaskTree', value: function $getTaskTree() { var _this = this; var tree = { name: this.name, tasks: this._tasks, stats: this._stats, subTasks: [] }; var i = 0; _.forEach(this.$getSubTasks(), function (task) { // if task is suppose to be hidden ($root$ for example) // use first sub task as tree if (_this._hidden && i === 0) { tree = task.$getTaskTree(); } else { if (_this._hidden) { // this should not really happen _this.logger.warn('multi sub tasks in hidden task node not allowed'); } tree.subTasks.push(task.$getTaskTree()); } i++; }); return tree; } /** * Get flatten task tree so it's one level * @returns {object} */ }, { key: '$getTaskFlatList', value: function $getTaskFlatList() { var list = []; if (!this._hidden) { list.push({ name: this.name, tasks: this._tasks, stats: this._stats }); } _.forEach(this.$getSubTasks(), function (task) { list = list.concat(task.$getTaskFlatList()); }); return list; } /** * Get task status and all it's sub tasks stats * @returns {object} */ }, { key: '$getStatsSummary', value: function $getStatsSummary(parentSummary) { var summary = this._stats.getSummary(parentSummary); _.forEach(this.$getSubTasks(), function (task) { summary = task.$getStatsSummary(summary); }); return summary; } /** * Prints Task help and all sub tasks help * @example {@embed ../examples/api/helpDocs.js} */ }, { key: '$printHelp', value: function $printHelp() { var _this2 = this; _.forEach(this.$getSubTasks(), function (task) { task.$printHelp(); }); if (this.$helpDocs) { this.beelzebub.drawBox(this.name); _.forEach(this.$helpDocs, function (doc, taskName) { // use helpLogger so time stamp's are not printed _this2.helpLogger.log(chalk.bold.underline(taskName)); _this2.helpLogger.log('\t', doc, '\n'); }); } } /** * Gets Current Config * @returns {object} config */ }, { key: '$config', value: function $config() { return this._config; } /** * Use this Task as root task * @example {@embed ../examples/api/defaultRootlevelAdvanced.js} */ }, { key: '$useAsRoot', value: function $useAsRoot() { this._rootLevel = true; this.name = '$root$'; } /** * Set a Task as default * @param {string} taskFuncName - This Class (Task) function name * @example {@embed ../examples/api/defaultRootlevelAdvanced.js} */ }, { key: '$setDefault', value: function $setDefault(taskFuncName) { this._defaultTaskFuncName = taskFuncName; } /** * Has any of the tasks ran before? * @returns {boolean} */ }, { key: '$hasRunBefore', value: function $hasRunBefore() { return this._beforeAllRun; } /** * Is this task root level? * @returns {boolean} */ }, { key: '$isRoot', value: function $isRoot() { return this._rootLevel; } /** * Set name of Task Group/Class, when refering to Task Group in CLI or other Tasks * @param {string} name - New name of Task Group/Class */ }, { key: '$setName', value: function $setName(name) { this.name = name; } /** * Get name of Task Group/Class * @returns {string} */ }, { key: '$getName', value: function $getName() { return this.name; } /** * Get Task by Name * @param {string} name - Name of Task to get * @returns {function} */ }, { key: '$getTask', value: function $getTask(name) { return this._tasks[name]; } /** * Does this Task Group have a Task with the name? * @param {string} name - Name of Task * @returns {boolean} */ }, { key: '$hasTask', value: function $hasTask(name) { return this._tasks.hasOwnProperty(name); } /** * Get SubTask by Name * @param {string} name - Name of Sub Task * @returns {object} */ }, { key: '$getSubTask', value: function $getSubTask(name) { return this._subTasks[name]; } /** * Set SubTask * @param {string} name - Name of Sub Task * @param {string} task - Task Class */ }, { key: '$setSubTask', value: function $setSubTask(name, task) { this._subTasks[name] = task; } /** * Does this have a Sub Task with the name? * @param {string} name - Name of Sub Task * @returns {boolean} */ }, { key: '$hasSubTask', value: function $hasSubTask(name) { return this._subTasks.hasOwnProperty(name); } /** * Get All Sub Task(s) * @returns {object} */ }, { key: '$getSubTasks', value: function $getSubTasks() { return this._subTasks; } /** * Set All Sub Task(s) * @param {object} tasks */ }, { key: '$setSubTasks', value: function $setSubTasks(tasks) { this._subTasks = tasks; } /** * Set Global Vars * @param {object} vars */ }, { key: '$setGlobalVars', value: function $setGlobalVars(vars) { this.beelzebub.setGlobalVars(vars); } /** * Get Global Vars * @returns {object} */ }, { key: '$getGlobalVars', value: function $getGlobalVars() { return this.beelzebub.getGlobalVars(); } /** * Define Task Vars * @param {string} taskName - Name of Task * @param {object} taskDef - Var Defintion for Task * @example {@embed ../examples/api/defineVars.js} */ }, { key: '$defineTaskVars', value: function $defineTaskVars(taskName, taskDef) { if (!_.isObject(this.$varDefs)) { this.$varDefs = {}; } this.$varDefs[taskName] = taskDef; } /** * Set Help Docs for Task * @param {string} taskName - Name of Task * @param {string} helpDocs - Help Docs for Task * @example {@embed ../examples/api/helpDocs.js} */ }, { key: '$setTaskHelpDocs', value: function $setTaskHelpDocs(taskName, helpDocs) { if (!_.isObject(this.$helpDocs)) { this.$helpDocs = {}; } this.$helpDocs[taskName] = helpDocs; } /** * Get Define Task Vars by Name * @param {string} taskStr - Name of Task * @returns {object} Varaible Definition for Task */ }, { key: '$getVarDefsForTaskName', value: function $getVarDefsForTaskName(taskStr) { var taskParts = taskStr.split('.'); var taskName = taskParts.shift(); if (!taskName || !taskName.length) { taskName = 'default'; } // this.vLogger.log('taskName:', taskName); // this.vLogger.log('taskParts:', taskParts); if (this.$hasSubTask(taskName)) { var newTaskName = taskParts.join('.'); // this.vLogger.log('newTaskName:', newTaskName); return this.$getSubTask(taskName).$getVarDefsForTaskName(newTaskName); } else if (this.$hasTask(taskName)) { if (!this.$varDefs || _.keys(this.$varDefs).length === 0) { return null; } return this.$varDefs[taskName]; } } /** * This should be Extented * @interface * @example {@embed ../examples/api/subTasksSimple.js} */ }, { key: '$init', value: function $init() { return null; } /** * This should to be Extented * @interface * @param {object} taskInfo - {name, vars} * @example {@embed ../examples/api/beforeAfter.js} */ }, { key: '$beforeEach', value: function $beforeEach(taskInfo) { return null; } /** * This should to be Extented * @interface * @param {object} taskInfo - {name, vars} * @example {@embed ../examples/api/beforeAfter.js} */ }, { key: '$afterEach', value: function $afterEach(taskInfo) { return null; } /** * This should to be Extented * @interface * @param {object} taskInfo - {name, vars} * @example {@embed ../examples/api/beforeAfterAdvanced.js} */ }, { key: '$beforeAll', value: function $beforeAll(taskInfo) { return null; } /** * This should to be Extented * @interface * @example {@embed ../examples/api/beforeAfterAdvanced.js} */ }, { key: '$afterAll', value: function $afterAll() { return null; } /** * Is Task Running? * @returns {boolean} */ }, { key: '$getRunning', value: function $getRunning() { return this._running; } /** * Register the Task with BZ * @returns {object} Promise */ }, { key: '$register', value: function $register() { // this.vLogger.log('$register start'); var tList = []; this._bfsTaskBuilder(tList, this); // run init, running as optimal to shortcut $init's that don't return promises var initPromise = this._normalizeExecFuncToPromise(this.$init, this); // this.vLogger.log('$register bfsTaskBuilder outList:', tList); this._addTasks(tList, this); return initPromise // .then((results) => { // this.vLogger.log('$register initFunctionList done:', results); // }) .then(function (results) { // this.vLogger.log('$register initFunctionList done:', results); return results; }); } /** * Util - Start Task Stats * @private */ }, { key: '_taskStatsStart', value: function _taskStatsStart(parent, taskName) { var name = taskName; if (parent.name !== '$root$') { name = this.namePath + '.' + taskName; } this.logger.group(name); return this._stats.startTask(); } /** * Util - End Task Stats * @private */ }, { key: '_taskStatsEnd', value: function _taskStatsEnd(parent, taskName, statsId) { var name = taskName; if (parent.name !== '$root$') { name = this.namePath + '.' + taskName; } this._stats.endTask(statsId); // TODO: add option to not display this var stats = this._stats.getTask(statsId); var time = Number(stats.diff.time.toFixed(2)); this.logger.groupEnd(name + ' (' + time + ' ms)'); } /** * Util - Run Before All * @private */ }, { key: '_runBeforeAll', value: function _runBeforeAll(taskInfo) { var _this3 = this; return this._normalizeExecFuncToPromise(this.$beforeAll, this, taskInfo).then(function () { _this3._beforeAllRun = true; }); } /** * Util - Run After All * @private */ }, { key: '_runAfterAll', value: function _runAfterAll() { var _this4 = this; // sequance running all sub task AfterAll function var afterPromises = []; _.forEach(this.$getSubTasks(), function (task) { afterPromises.push(function () { return task._runAfterAll(); }); }); return whenSeq(afterPromises).then(function () { return _this4._normalizeExecFuncToPromise(_this4.$afterAll, _this4); }); } /** * Util - Add Tasks * @private */ }, { key: '_addTasks', value: function _addTasks(tList, task) { var _this5 = this; // this.vLogger.log('addTasksToGulp tList:', tList, ', name:', this.name, ', rootLevel:', this._rootLevel, ', this != task:', this != task); _.forEach(tList, function (funcName) { var taskId = ''; if (_this5 !== task && !_this5._rootLevel) { taskId += task.name + '.'; } taskId += funcName; if (funcName === _this5._defaultTaskFuncName && funcName !== 'default') { // default tasks have two entries _this5._tasks[taskId] = { taskId: taskId, tasksObj: task, func: task[funcName] }; taskId = 'default'; } // this.vLogger.log('taskId:', taskId); _this5._tasks[taskId] = { taskId: taskId, tasksObj: task, func: task[funcName] }; }); } // TODO: ??? combine the logic of 'add' and 'addSubTasks' // move to recursive run model using task $register instead of mixing sub tasks with current task class // TODO: should this always return a promise? when adding in $init should the use be forced to wait on this? /** * Add Sub Tasks * @public * @param {object} Task - Task Class * @param {object} [config={}] - Config for Task * @returns {object} Promise * @example {@embed ../examples/api/subTasksSimple.js} */ }, { key: '$addSubTasks', value: function $addSubTasks(Task) { var _this6 = this; var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var task = null; if (_.isFunction(Task) && _.isObject(Task)) { config.parentPath = this.namePath; config.beelzebub = this.beelzebub; task = new Task(config); } else { task = Task; } if (!this.beelzebub.isLoading()) { return task.$register().then(function () { _this6.$setSubTask(task.$getName(), task); }); // this.vLogger.error('$addSubTasks can only be called during init'); // return when.reject(); } else { // this.vLogger.log('$addSubTasks addInitFunction', task.name); this.beelzebub.addInitFunction(function () { return task.$register(); }); // this.vLogger.log('task:', task); this.$setSubTask(task.$getName(), task); return when.resolve(); } } /** * Util - Normalize Execute of Function, Promise, or Generator (using CO) * @private */ }, { key: '_normalizeExecFuncToPromise', value: function _normalizeExecFuncToPromise(func, parent) { var p = null; // this.logger.log('normalizeExecFuncToPromise', // 'isPromise:', isPromise(func), // ', isGenerator:', isGenerator(func), // ', isFunction:', _.isFunction(func)); // func already a promise for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { args[_key - 2] = arguments[_key]; } if (util.isPromise(func)) { p = func; } // func is a generator function else if (util.isGenerator(func)) { // run generator using co if (parent) { p = co(func.bind.apply(func, [parent].concat(args))); } else { p = co(func.bind.apply(func, [func].concat(args))); } } // if task is function, run it else if (_.isFunction(func)) { if (parent) { p = func.apply(parent, args); } else { p = func.apply(undefined, args); } } else { // TODO: check other this.logger.warn('other type?? func:', func, ', parent:', parent); } // this.logger.log('normalizeExecFuncToPromise', // 'isStream:', isStream(p), // ', isPromise:', isPromise(p), // ', optimize:', optimize); // convert streams to promise if (util.isStream(p)) { p = streamToPromise(p); } if (!util.isPromise(p)) { p = when.resolve(p); } return p; } /** * Util - Breath First Search Task Builder * @private */ }, { key: '_bfsTaskBuilder', value: function _bfsTaskBuilder(outList, task, name) { var proto = (0, _getPrototypeOf2.default)(task); // this.vLogger.log('task:', task, ', proto:', proto, ', name:', name); if (proto && _.isObject(proto)) { // this.vLogger.log('name:', name, ', task.name:', task.name); name = name || task.name; var oproto = this._bfsTaskBuilder(outList, proto, name); if ((0, _getPrototypeOf2.default)(oproto) && !(oproto === BzTasks.prototype)) { // this.vLogger.log('name:', name, 'oproto:', oproto, ', oproto instanceof BzTasks:', (oproto === BzTasks.prototype)); var tList = (0, _getOwnPropertyNames2.default)(oproto); tList = tList.filter(function (p) { return _.isFunction(task[p]) && p !== 'constructor' /* NOT constructor */ && p[0] !== '_' /* doesn't start with underscore */ && p[0] !== '$' /* doesn't start with $ */ ; }); // this.vLogger.log('name:', name, ', oproto:', oproto, ', tList:', tList); for (var i = 0; i < tList.length; i++) { outList.push(tList[i]); } } } return task; } /** * Runs task(s) in sequence * @param {(object|string)} args - task(s) * @returns {Object} Promise * @example {@embed ../examples/api/sequence.js} */ }, { key: '$sequence', value: function $sequence() { var _beelzebub; for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } // TODO: prevent infinite loop return (_beelzebub = this.beelzebub).sequence.apply(_beelzebub, [this].concat(args)); } /** * Runs task(s) in parallel * @param {(function|string)} args - task(s) * @returns {object} Promise * @example {@embed ../examples/api/parallel.js} */ }, { key: '$parallel', value: function $parallel() { var _beelzebub2; for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } // TODO: prevent infinite loop return (_beelzebub2 = this.beelzebub).parallel.apply(_beelzebub2, [this].concat(args)); } /** * Runs task(s) - multi args run in sequence, arrays are run in parallel * @param {(function|string)} args - task(s) * @returns {object} Promise */ }, { key: '$run', value: function $run() { var _beelzebub3; for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } // TODO: prevent infinite loop return (_beelzebub3 = this.beelzebub).run.apply(_beelzebub3, [this].concat(args)); } /** * Util - Breath First Search Task Builder * @private */ }, { key: '_waitForInit', value: function _waitForInit() { var p = null; // wait for self to complete if (this.beelzebub.isLoading()) { p = this.beelzebub.getInitPromise(); } else { p = when.resolve(); } return p; } /** * Util - Run task(s) in sequence * @private * @param {(function|string)} [parent] - Parent Task * @param {(object|string)} [args] - Arguments for Task * @returns {object} Promise */ }, { key: '_sequence', value: function _sequence(parent) { var _this7 = this; for (var _len5 = arguments.length, args = Array(_len5 > 1 ? _len5 - 1 : 0), _key5 = 1; _key5 < _len5; _key5++) { args[_key5 - 1] = arguments[_key5]; } // this.vLogger.log('sequence args:', args); // this.vLogger.log('sequence parent:', parent); if (parent && (_.isString(parent) || _.isArray(parent) || util.isTaskObject(parent))) { args.unshift(parent); parent = undefined; // this.vLogger.log('sequence args:', args); } return this._waitForInit().then(function () { // normalize tasks (aka args) args = _this7._normalizeTask(parent, args); var aTasks = []; _.forEach(args, function (task) { aTasks.push(function () { return _this7._runPromiseTask(parent, task); }); }); // this.vLogger.log('sequence args:', aTasks); return whenSeq(aTasks); }); } /** * Util - Runs task(s) in parallel * @private * @param {(function|string)} [parent] - Parent Task * @param {(object|string)} [args] - Arguments for Task * @returns {object} Promise */ }, { key: '_parallel', value: function _parallel(parent) { var _this8 = this; for (var _len6 = arguments.length, args = Array(_len6 > 1 ? _len6 - 1 : 0), _key6 = 1; _key6 < _len6; _key6++) { args[_key6 - 1] = arguments[_key6]; } // this.vLogger.log('parallel args:', args); if (parent && (_.isString(parent) || _.isArray(parent) || util.isTaskObject(parent))) { args.unshift(parent); parent = undefined; // this.vLogger.log('parallel args:', args); } return this._waitForInit().then(function () { // normalize tasks (aka args) args = _this8._normalizeTask(parent, args); var pList = _.map(args, function (task) { return _this8._runPromiseTask(parent, task); }); // this.vLogger.log('parallel pList:', pList); return when.all(pList); }); } /** * Util - Runs task(s) - multi args run in sequence, arrays are run in parallel * @private * @param {(function|string)} [parent] - Parent Task * @param {(object|string)} [args] - Arguments for Task * @returns {object} Promise */ }, { key: '_run', value: function _run(parent) { var _this9 = this; for (var _len7 = arguments.length, args = Array(_len7 > 1 ? _len7 - 1 : 0), _key7 = 1; _key7 < _len7; _key7++) { args[_key7 - 1] = arguments[_key7]; } // this.vLogger.log('run args:', args); // if no args if (_.isArray(args) && args.length === 0) { // check if root, level and has 'default' task if (this._rootLevel && _.isObject(this._tasks[this._defaultTaskFuncName])) { args.push(this._defaultTaskFuncName); } } if (parent && (_.isString(parent) || _.isArray(parent) || util.isTaskObject(parent))) { args.unshift(parent); parent = undefined; // this.vLogger.log('run args:', args); } return this._waitForInit().then(function () { var promise = null; // normalize tasks (aka args) args = _this9._normalizeTask(parent, args); if (args.length === 1) { promise = _this9._runPromiseTask(parent, args[0]); } else { // multi args mean, run in sequence promise = _this9._sequence.apply(_this9, [parent].concat((0, _toConsumableArray3.default)(args))); } _this9._running = promise.then(function (result) { _this9._running = null; return result; }); return _this9._running.catch(function (e) { _this9.logger.error(e); }); }); } /** * Util - Run a task * @private * @param {string} task * @returns {object} Promise */ }, { key: '_runTask', value: function _runTask(task) { // this.vLogger.log('task class:', this.name // , ', running task:', task // , ', all tasks:', _.keys(this._tasks) // , ', all subTasks:', _.keys(this.$getSubTasks())); // if no task specified, then use default if (!task) { task = { task: 'default' }; // console.error('setting to default'); } var taskParts = task.task.split('.'); var taskName = taskParts.shift(); if (!taskName || !taskName.length) { taskName = 'default'; } if (this.$hasSubTask(taskName)) { task.task = taskParts.join('.'); // this.vLogger.info('runTask taskName:', taskName, ', runTask:', task.task); var tastObject = this.$getSubTask(taskName); // has beforeAll run if (!tastObject.$hasRunBefore()) { var taskInfo = { task: taskParts[taskParts.length - 1], vars: task.vars }; // call run beforeAll function which sets internal var if ran before // not crazy about using private, but don't want people to thing it's ok to run this return tastObject._runBeforeAll(taskInfo).then(function () { return tastObject._runTask(task); }); } // beforeAll has already run else { return tastObject._runTask(task); } } else if (this.$hasTask(taskName)) { var taskObj = this.$getTask(taskName); return this._execTaskFun(taskName, taskObj.func, taskObj.tasksObj, task.vars); } else { this.logger.error('Task "' + taskName + '" - not found'); } } /** * Util - Run Before Each * @private * @param {object} parent * @param {object} taskInfo * @returns {object} Promise */ }, { key: '_runBeforeEach', value: function _runBeforeEach(parent, taskInfo) { // run beforeEach return this._normalizeExecFuncToPromise(parent.$beforeEach, parent, taskInfo); } /** * Util - Exec Task Fucntion * @private * @param {string} taskName * @param {function} func * @param {object} parent * @param {object} vars * @returns {object} Promise */ }, { key: '_execTaskFun', value: function _execTaskFun(taskName, func, parent, vars) { var _this10 = this; var taskInfo = { task: taskName, vars: vars }; var fullTaskName = taskName; if (parent && _.isString(parent.name) && parent.name !== '$root$') { fullTaskName = this.namePath + '.' + taskName; } // this.vLogger.info('execTaskFun taskName:', JSON.stringify(taskInfo)); var beforePromise = null; if (!parent.$hasRunBefore()) { // call run beforeAll function which sets internal var if ran before // not crazy about using private, but don't want people to thing it's ok to run this beforePromise = parent._runBeforeAll(taskInfo).then(function () { return _this10._runBeforeEach(parent, taskInfo); }); } else { beforePromise = this._runBeforeEach(parent, taskInfo); } // run beforeAll var statsId = null; return beforePromise.then(function () { // after, before _this10.beelzebub.emit('$before', { task: fullTaskName, vars: taskInfo.vars }); statsId = parent._taskStatsStart(parent, taskName); // if parent is Object if (_.isObject(parent)) { // add context aware $emit parent.$emit = function (name, data) { _this10.beelzebub.emit(name, { task: fullTaskName, vars: taskInfo.vars }, data); }; } // run task function return _this10._normalizeExecFuncToPromise(func, parent, vars); }).then(function () { parent._taskStatsEnd(parent, taskName, statsId); // run afterEach return _this10._normalizeExecFuncToPromise(parent.$afterEach, parent, taskInfo); }).then(function () { // after, after _this10.beelzebub.emit('$after', { task: fullTaskName, vars: taskInfo.vars }); }); } /** * Util - Runs Promise task * TODO: issues, need to circle back around to top level BZ and trickle down * TODO: can we merge this with runTask? * so it can recursivly chain down to resolve promises all the way down * @private * @param {object} parent - Parent Task * @param {(function|object)} task * @returns {object} Promise */ }, { key: '_runPromiseTask', value: function _runPromiseTask(parent, task) { var p = null; // if task is array, then run in parallel if (_.isArray(task)) { return this._parallel.apply(this, [parent].concat((0, _toConsumableArray3.default)(task))); } // if task is object, then find function and parent in list else if (_.isObject(task)) { var taskParts = []; var taskName = 'default'; // task is function if (_.isFunction(task.task)) { p = this._execTaskFun(taskName, task.task, parent, task.vars); } // task is string else if (_.isString(task.task)) { taskParts = task.task.split('.'); taskName = taskParts.shift(); if (!this.$hasTask(taskName)) { // now check if in sub level if (this.$hasSubTask(taskName)) { task.task = taskParts.join('.'); // this.vLogger.info('runPromiseTask taskName:', taskName, ', runTask:', task.task); var tastObject = this.$getSubTask(taskName); // run parent beforeAll running subTask // has beforeAll run if (!tastObject.$hasRunBefore()) { var taskInfo = { task: taskParts[taskParts.length - 1], vars: task.vars }; // call run beforeAll function which sets internal var if ran before // not crazy about using private, but don't want people to thing it's ok to run this p = tastObject._runBeforeAll(taskInfo).then(function () { return tastObject._runTask(task); }); } // beforeAll has already run else { p = tastObject._runTask(task); } } else { var error = 'task name not found: "' + task.task + '"'; this.logger.error(error); p = when.reject(error); } } } else { var _error = 'invalid task name: "' + task.task + '"'; this.logger.error(_error); p = when.reject(_error); } // no promise if (!p) { if (taskParts.length > 0) { task.task = taskParts.join('.'); // this.vLogger.info('runPromiseTask taskName:', taskName, ', runTask:', task.task); p = this._tasks[taskName]._runTask(task); } else { if (this._tasks[taskName]) { task = this._tasks[taskName].func; parent = this._tasks[taskName].tasksObj; p = this._execTaskFun(taskName, task, parent, task.vars); } else { var _error2 = 'task name not found: "' + task + '"'; this.logger.error(_error2); p = when.reject(_error2); } } } } else { var _error3 = 'task type not supported: "' + task + '"'; this.logger.trace(_error3); p = when.reject(_error3); } // TODO: what happens to the data at the end? TBD return p; } /** * Util - Normalize Task * @private * @param {object} parent - Parent Task * @param {(function|object)} task * @returns {object} Promise */ }, { key: '_normalizeTask', value: function _normalizeTask(parent, tasks) { var _this11 = this; var objTasks = _.map(tasks, function (task) { var taskObj = void 0; if (_.isString(task)) { // if first char "." then relative to parent path if (task.charAt(0) === '.') { if (!parent) { _this11.logger.trace('parent missing but expected'); } else { task = parent.namePath + task; } } var taskParts = task.split('.'); var taskName = taskParts.shift(); var taskFullName = task; var taskVarParts = task.split(':'); var taskVars = {}; if (taskVarParts.length > 0) { taskFullName = taskVarParts.shift(); taskVars = taskVarParts.join(':'); // vars a string and empty, this can happen is no vars pass to task string if (_.isString(taskVars) && taskVars.length === 0) { taskVars = {}; } else { try { taskVars = JSON.parse(taskVars); } catch (err) { // this is ok _this11.vLogger.warn('Parsing Task Error:', err); } } } if (!_this11.$getSubTask(taskName) && !_this11.$getTask(taskName)) { _this11.logger.warn(taskFullName, 'task not added'); return false; } taskObj = { task: taskFullName, vars: taskVars }; } else if (_.isArray(task)) { taskObj = _this11._normalizeTask(parent, task); } else if (_.isFunction(task)) { taskObj = { task: task }; } else if (_.isObject(task)) { if (!task.hasOwnProperty('task')) { _this11.logger.warn('invalid object: task property required'); return null; } // if first char "." then relative to parent path if (task.task.charAt(0) === '.') { if (!parent) { _this11.logger.trace('parent missing but expected'); } else { task.task = parent.namePath + task.task; } } return task; } else { _this11.logger.warn('unknown task input type'); return null; } // make sure vars is object if (taskObj.vars === null || taskObj.vars === undefined) { taskObj.vars = {}; } if (!_.isObject(taskObj.vars)) { _this11.logger.warn('Vars should be an object'); taskObj.vars = {}; } return taskObj; }); // apply var definitions if they exist // definitions -> defaults, data types and requirements objTasks = this._applyVarDefsToAllTasks(objTasks); // this.vLogger.log('objTasks:', objTasks); return objTasks; } /** * Util - Apply Variable Definitions To All Tasks * @private * @param {object} objTasks - Tasks * @returns {object} objTasks */ }, { key: '_applyVarDefsToAllTasks', value: function _applyVarDefsToAllTasks(objTasks) { var _this12 = this; objTasks = _.map(objTasks, function (task) { return _this12._applyVarDefToTask(task); }); return objTasks; } /** * Util - Apply Variable Definitions To Task * @private * @param {object} task - Task * @returns {object} task */ }, { key: '_applyVarDefToTask', value: function _applyVarDefToTask(task) { var _this13 = this; if (_.isArray(task)) { return _.map(task, function (t) { return _this13._applyVarDefToTask(t); }); } // task object and task is string else if (_.isObject(task) && _.isString(task.task)) { var taskParts = task.task.split('.'); var taskName = taskParts.shift(); if (!taskName || !taskName.length) { taskName = 'default'; } if (this.$hasSubTask(taskName)) { // use copy of tasks to pass to child, as we don't want to mutate the task name var newTask = _.cloneDeep(task); newTask.task = taskParts.join('.'); newTask = this.$getSubTask(taskName)._applyVarDefToTask(newTask); // update task vars task.vars = newTask.vars; } else if (this.$hasTask(taskName)) { // no vardefs or no keys in object if (!this.$varDefs || _.keys(this.$varDefs).length === 0) { return task; } // has vars definition if (this.$varDefs[taskName]) { // apply def to vars task.vars = this._applyVarDefs(this.$varDefs[taskName], task.vars); } } else { // this.logger.error(`Task "${taskName}" - not found`); } return task; } // could be task is function or something else, but can't look up vardef to apply else { return task; } } /** * Util - Apply Variable Definitions to input Variables * TODO: need loads of tests to cover all the conditionals * @private * @param {object} varDefs - variable defintions * @param {object} vars - input variable * @returns {object} vars */ }, { key: '_applyVarDefs', value: function _applyVarDefs(varDefs, vars) { var _this14 = this; // this.vLogger.info('varDefs:', varDefs); // this.vLogger.info('vars:', vars); _.forEach(varDefs, function (varDef, key) { var type = varDef.type.toLowerCase(); // if as alias if (varDef.alias) { var tkey = varDef.alias; // if alias var has value, then use this as the key if (vars[tkey] !== null && vars[tkey] !== undefined) { vars[key] = vars[tkey]; } } // var is set to something if (vars[key] !== null && vars[key] !== undefined) { if (type === 'string') { // not string if (!_.isString(vars[key])) { _this14.logger.error(key + ' is not a string but defined as one, converting to string'); vars[key] = String(vars[key]); } } else if (type === 'number') { // not number if (!_.isNumber(vars[key])) { _this14.logger.error(key + ' is not a number but defined as one, converting to number'); vars[key] = Number(vars[key]); } } else if (type === 'boolean') { // not boolean if (!_.isBoolean(vars[key])) { _this14.logger.error(key + ' is not a boolean but defined as one, converting to boolean'); // is string, only compare if 'true', otherwise false if (_.isString(vars[key])) { vars[key] = vars[key].toLowerCase() === 'true'; } // convert all else use Boolean else { vars[key] = Boolean(vars[key]); } } } else if (type === 'array') { // not array if (!_.isArray(vars[key])) { _this14.logger.error(key + ' is not a array but defined as one, converting to array'); if (_.isString(vars[key])) { try { vars[key] = JSON.parse(vars[key]); } catch (err) { // if parsing fails then split the string by commas vars[key] = vars[key].split(','); } } else { vars[key] = Array(vars[key]); } } } else if (type === 'object') { if (!_.isObject(vars[key])) { _this14.logger.error(key + ' is not a object but defined as one, converting to object'); // convert vars[key] to object if (_.isString(vars[key])) { try { vars[key] = JSON.parse(vars[key]); } catch (err) { // if parsing fails then just stick in data prop _this14.logger.error('object "' + key + '" json parsing error: ' + err); vars[key] = { data: vars[key] }; } } else { vars[key] = { data: vars[key] }; } } var varProps = varDef.properties; if (!varProps || !_.isObject(varProps)) { _this14.logger.error('object "' + key + '" properties is not defined as object, skipping all sub properties.'); } else { // recursivly check children (properties) vars[key] = _this14._applyVarDefs(varProps, vars[key]); } } else { _this14.logger.warn('Unknown Variable Definition Type: ' + type); } } // not set to anything else { if (varDef.required) { _this14.logger.error('Var "' + key + '" is required but not set in vars.'); } var defValue = null; if (type === 'string') { defValue = ''; } else if (type === 'number') { defValue = 0; } else if (type === 'boolean') { defValue = false; } else if (type === 'array') { defValue = []; } else if (type === 'object') { defValue = {}; } // has default if (varDef.default) { vars[key] = varDef.default; } // else default to empty string else { vars[key] = defValue; } } }); return vars; } }]); return BzTasks; }(); module.exports = BzTasks;