/**
* @file Process class to manage/run workflow rule and approval process
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
*/
'use strict';
var _ = require('lodash/core'),
Promise = require('./promise'),
Conneciton = require('./connection');
/**
* A class which manages process rules and approval processes
*
* @class
* @param {Connection} conn - Connection object
*/
var Process = module.exports = function(conn) {
/**
* Object which mangages process rules
* @member {Process~ProcessRule} Process#rule
*/
this.rule = new ProcessRule(conn);
/**
* Object which mangages approval process
* @member {Process~ApprovalProcess} Process#approval
*/
this.approval = new ApprovalProcess(conn);
};
/**
* A class which manages process (workflow) rules
*
* @class Process~ProcessRule
* @param {Connection} conn - Connection object
*/
var ProcessRule = function(conn) {
this._conn = conn;
};
/**
* @typedef {Object} Process~ProcessRuleDefinition
* @prop {String} id - Id of approval process definition
* @prop {String} name - Name of process rule definition
* @prop {String} object - SObject name which process rule is defined
*/
/**
* Get all process rule definitions registered to sobjects
*
* @method Process~ProcessRule#list
* @param {Callback.<Map.<String, Array.<Process~ProcessRuleDefinition>>>} [callback] - Callback function
* @returns {Promise.<Map.<String, Array.<Process~ProcessRuleDefinition>>>}
*/
ProcessRule.prototype.list = function(callback) {
return this._conn.request("/process/rules").then(function(res) {
return res.rules;
}).thenCall(callback);
};
/**
* @typedef {Object} Process~ProcessRuleTriggerResult
* @prop {Boolean} success - Is process rule trigger succeeded or not
* @prop {Array.<Object>} errors - Array of errors returned if the request failed
*/
/**
* Trigger process rule for given entities
*
* @method Process~ProcessRule#trigger
* @param {String|Array.<String>} contextIds - Entity ID(s) to trigger workflow process
* @param {Callback.<Process~ProcessRuleTriggerResult>} [callback] - Callback function
* @returns {Promise.<Process~ProcessRuleTriggerResult>}
*/
ProcessRule.prototype.trigger = function(contextIds, callback) {
contextIds = _.isArray(contextIds) ? contextIds : [ contextIds ];
return this._conn.request({
method: "POST",
url: "/process/rules/",
body: JSON.stringify({
contextIds: contextIds
}),
headers: {
"content-type": "application/json"
}
}).thenCall(callback);
};
/**
* A class which manages approval processes
*
* @class Process~ApprovalProcess
* @param {Connection} conn - Connection object
*/
var ApprovalProcess = function(conn) {
this._conn = conn;
};
/**
* @typedef {Object} Process~ApprovalProcessDefinition
* @prop {String} id - Id of approval process definition
* @prop {String} name - Name of approval process definition
* @prop {String} object - SObject name which approval process is defined
* @prop {Number} sortOrder - Processing order of approval in SObject
*/
/**
* Get all approval process definitions registered to sobjects
*
* @method Process~ApprovalProcess#list
* @param {Callback.<Map.<String, Array.<ApprovalProcessDefinition>>>} [callback] - Callback function
* @returns {Promise.<Map.<String, Array.<ApprovalProcessDefinition>>>}
*/
ApprovalProcess.prototype.list = function(callback) {
return this._conn.request("/process/approvals").then(function(res) {
return res.approvals;
}).thenCall(callback);
};
/**
* @typedef {Object} Process~ApprovalProcessRequestResult
* @prop {Boolean} success - True if processing or approval completed successfully
* @prop {Array.<Object>} errors - The set of errors returned if the request failed
* @prop {Array.<String>} actorIds - IDs of the users who are currently assigned to this approval step
* @prop {String} entityId - Object being processed
* @prop {String} instanceId - ID of the ProcessInstance associated with the object submitted for processing
* @prop {String} instanceStatus - Status of the current process instance (not an individual object but the entire process instance)
* @prop {Array.<String>} newWorkItemIds - Case-insensitive IDs that point to ProcessInstanceWorkitem items (the set of pending approval requests)
*/
/**
* Send bulk requests for approval process
*
* @method Process~ApprovalProcess#request
* @param {Array.<ApprovalProcessRequest>} requests - Array of approval process request to send
* @param {Callback.<Array.<ApprovalProcessRequestResult>>} - Callback function
* @param {Promise.<Array.<ApprovalProcessRequestResult>>}
*/
ApprovalProcess.prototype.request = function(requests, callback) {
requests = requests.map(function(req) {
return req._request ? req._request : req;
});
return this._conn.request({
method: 'POST',
url: '/process/approvals',
headers: { "content-type": "application/json" },
body: JSON.stringify({ requests: requests })
}).thenCall(callback);
};
/**
* Create approval process request
*
* @private
*/
ApprovalProcess.prototype._createRequest = function(actionType, contextId, comments, options, callback) {
if (typeof comments === "function") {
callback = comments;
options = null;
comments = null;
}
if (typeof options === "function") {
callback = options;
options = null;
}
options = options || {};
var request = {
actionType: actionType,
contextId: contextId,
comments: comments
};
_.extend(request, options);
return new ApprovalProcessRequest(this, request).thenCall(callback);
};
/**
* Submit approval request for an item
*
* @method Process~ApprovalProcess#submit
* @param {String} contextId - ID of the item that is being acted upon
* @param {String} [comments] - Comment to add to the history step associated with this request
* @param {Object} [options] - Request parameters
* @param {Array.<String>} [options.nextApproverIds] - If the process requires specification of the next approval, the ID of the user to be assigned the next request
* @param {String} [options.processDefinitionNameOrId] - Developer name or ID of the process definition
* @param {Boolean} [options.skipEntryCriteria] - Determines whether to evaluate the entry criteria for the process (true) or not (false) if the process definition name or ID isn’t null
* @param {Callback.<ApprovalProcessRequestResult>} [callback] - Callback function
* @returns {ApprovalProcessRequest}
*/
ApprovalProcess.prototype.submit = function(contextId, comments, options, callback) {
return this._createRequest("Submit", contextId, comments, options, callback);
};
/**
* Approve approval request for an item
*
* @method Process~ApprovalProcess#approve
* @param {String} workitemId - ID of the item that is being acted upon
* @param {String} [comments] - Comment to add to the history step associated with this request
* @param {Object} [options] - Request parameters
* @param {Array.<String>} [options.nextApproverIds] - If the process requires specification of the next approval, the ID of the user to be assigned the next request
* @param {String} [options.processDefinitionNameOrId] - Developer name or ID of the process definition
* @param {Boolean} [options.skipEntryCriteria] - Determines whether to evaluate the entry criteria for the process (true) or not (false) if the process definition name or ID isn’t null
* @param {Callback.<ApprovalProcessRequestResult>} [callback] - Callback function
* @returns {ApprovalProcessRequest}
*/
ApprovalProcess.prototype.approve = function(workitemId, comments, options, callback) {
return this._createRequest("Approve", workitemId, comments, options, callback);
};
/**
* Reject approval request for an item
*
* @method Process~ApprovalProcess#reject
* @param {String} workitemId - ID of the item that is being acted upon
* @param {String} [comments] - Comment to add to the history step associated with this request
* @param {Object} [options] - Request parameters
* @param {Array.<String>} [options.nextApproverIds] - If the process requires specification of the next approval, the ID of the user to be assigned the next request
* @param {String} [options.processDefinitionNameOrId] - Developer name or ID of the process definition
* @param {Boolean} [options.skipEntryCriteria] - Determines whether to evaluate the entry criteria for the process (true) or not (false) if the process definition name or ID isn’t null
* @param {Callback.<ApprovalProcessRequestResult>} [callback] - Callback function
* @returns {ApprovalProcessRequest}
*/
ApprovalProcess.prototype.reject = function(workitemId, comments, options, callback) {
return this._createRequest("Reject", workitemId, comments, options, callback);
};
/**
* A class representing approval process request
*
* @protected
* @class Process~ApprovalProcessRequest
* @implements {Promise.<Process~ApprovalProcessRequestResult>}
* @param {Process~ApprovalProcess} process - ApprovalProcess
* @param {Object} request - Request parameters
* @param {String} request.actionType - Represents the kind of action to take: Submit, Approve, or Reject
* @param {String} request.contextId - ID of the item that is being acted upon
* @param {String} request.comments - Comment to add to the history step associated with this request
* @param {Array.<String>} [request.nextApproverIds] - If the process requires specification of the next approval, the ID of the user to be assigned the next request
* @param {String} [request.processDefinitionNameOrId] - Developer name or ID of the process definition
* @param {Boolean} [request.skipEntryCriteria] - Determines whether to evaluate the entry criteria for the process (true) or not (false) if the process definition name or ID isn’t null
*/
var ApprovalProcessRequest = function(process, request) {
this._process = process;
this._request = request;
};
/**
* Promise/A+ interface
* http://promises-aplus.github.io/promises-spec/
*
* @method Process~ApprovalProcessRequest#then
*/
ApprovalProcessRequest.prototype.then = function(onResolve, onReject) {
if (!this._promise) {
this._promise = this._process.request([ this ]).then(function(rets) {
return rets[0];
});
}
this._promise.then(onResolve, onReject);
};
/**
* Promise/A+ extension
* Call "then" using given node-style callback function
*
* @method Process~ApprovalProcessRequest#thenCall
*/
ApprovalProcessRequest.prototype.thenCall = function(callback) {
return callback ? this.then(function(res) {
callback(null, res);
}, function(err) {
callback(err);
}) :
this;
};