Repository: auth0/node-auth0
Pull request #570 (2021-01-08 23:00:31)

[actionsManager] Adding new Actions Managment APIs
This is a replacement for #539 to support updating the beta `actionsManager` branch from the master branch.

### Changes

This PR adds the new classes for support Actions GA release.

Please see https://auth0.com/docs/api/management/v2/#!/Actions/get_actions for details of the management API endpoints for Actions

### References

- https://auth0.com/docs/api/management/v2/#!/Actions/get_actions
- ESD request for approval: https://auth0team.atlassian.net/jira/servicedesk/projects/ESD/queues/issue/ESD-13429
- Original Beta release support ticket: https://auth0team.atlassian.net/browse/DXEX-712
- GA release support ticket: https://auth0team.atlassian.net/browse/DXEX-1315

### Testing

- [X] This change adds unit test coverage
- [x] This change adds integration test coverage

### Checklist

- [X] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
- [X] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
- [X] All existing and new tests complete without errors



Patch:
diff --git a/src/management/ActionsManager.js b/src/management/ActionsManager.js
new file mode 100644
index 000000000..78d6e2d7c
--- /dev/null
+++ b/src/management/ActionsManager.js
@@ -0,0 +1,632 @@
+var ArgumentError = require('rest-facade').ArgumentError;
+var utils = require('../utils');
+var Auth0RestClient = require('../Auth0RestClient');
+var RetryRestClient = require('../RetryRestClient');
+
+/**
+ * Simple facade for consuming a REST API endpoint.
+ * @external RestClient
+ * @see https://github.com/ngonzalvez/rest-facade
+ */
+
+/**
+ * @class ActionsManager
+ * {@link https://auth0.com/docs/api/v2#!/Actions/get_actions Actions} provide a way to extend
+ * Auth0 flows with custom logic.
+ * See the {@link https://auth0.com/docs/actions Actions documentation} for more information.
+ * @constructor
+ * @memberOf module:management
+ *
+ * @param {Object} options            The client options.
+ * @param {String} options.baseUrl    The URL of the API.
+ * @param {Object} [options.headers]  Headers to be included in all requests.
+ * @param {Object} [options.retry]    Retry Policy Config
+ */
+var ActionsManager = function(options) {
+  if (options === null || typeof options !== 'object') {
+    throw new ArgumentError('Must provide client options');
+  }
+
+  if (options.baseUrl === null || options.baseUrl === undefined) {
+    throw new ArgumentError('Must provide a base URL for the API');
+  }
+
+  if ('string' !== typeof options.baseUrl || options.baseUrl.length === 0) {
+    throw new ArgumentError('The provided base URL is invalid');
+  }
+
+  /**
+   * Options object for the Rest Client instance.
+   *
+   * @type {Object}
+   */
+  var clientOptions = {
+    errorFormatter: { message: 'message', name: 'error' },
+    headers: options.headers,
+    query: { repeatParams: false }
+  };
+
+  /**
+   * Provides an abstraction layer for consuming the
+   * [Auth0 Actions endpoint]{@link https://auth0.com/docs/api/v2#!/actions}.
+   *
+   * @type {external:RestClient}
+   */
+  var auth0RestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/actions/:action_id',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.resource = new RetryRestClient(auth0RestClient, options.retry);
+
+  var actionsDeployRestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/actions/:action_id/deploy',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.actionsDeploy = new RetryRestClient(actionsDeployRestClient, options.retry);
+
+  var actionsTestRestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/actions/:action_id/test',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.actionsTest = new RetryRestClient(actionsTestRestClient, options.retry);
+
+  var triggersRestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/triggers/:trigger_id',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.triggers = new RetryRestClient(triggersRestClient, options.retry);
+
+  var triggerBindingsRestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/triggers/:trigger_id/bindings',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.triggerBindings = new RetryRestClient(triggerBindingsRestClient, options.retry);
+
+  var triggersTestRestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/triggers/:trigger_id/test',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.triggersTest = new RetryRestClient(triggersTestRestClient, options.retry);
+
+  var executionsRestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/executions/:execution_id',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.executions = new RetryRestClient(executionsRestClient, options.retry);
+
+  var actionVersionRestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/actions/:action_id/versions/:version_id',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.actionVersions = new RetryRestClient(actionVersionRestClient, options.retry);
+
+  var deployActionVersionRestClient = new Auth0RestClient(
+    options.baseUrl + '/actions/actions/:action_id/versions/:version_id/deploy',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.actionVersionDeploy = new RetryRestClient(deployActionVersionRestClient, options.retry);
+};
+
+/**
+ * Get all Triggers.
+ *
+ * @method    getAllTriggers
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example <caption>
+ *   This method takes an optional object as first argument that may be used to
+ *   specify pagination settings. If pagination options are not present,
+ *   the first page of a limited number of results will be returned.
+ * </caption>
+ * // Pagination settings.
+ * var params = {
+ *   per_page: 10,
+ *   page: 0
+ * };
+ *
+ * management.actions.getAllTriggers(params, function (err, actions) {
+ *   console.log(actions.length);
+ * });
+ *
+ * @param   {Object}    [params]               Actions parameters.
+ * @param   {Number}    [params.per_page]      Number of results per page.
+ * @param   {Number}    [params.page]          Page number, zero indexed.
+ * @param   {Function}  [cb]                   Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.getAllTriggers = function(params, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.triggers.getAll(params, cb);
+  }
+
+  return this.triggers.getAll(params);
+};
+
+/**
+ * Update the actions bound to a trigger .
+ *
+ * @method    updateTriggerBindings
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var data = { bindings: [{ id_type: "action_id", id_value: ACTION_ID1},{id_type: "action_name", id_value: ACTION_NAME2}];
+ * var params = { trigger_id: TRIGGER_ID };
+ *
+ * // Using auth0 instance.
+ * management.actions.updateTriggerBindings(params, data, function (err, bindings) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(bindings.length);  // 2
+ * });
+ *
+ * @param   {Object}    params                Actions Binding parameters.
+ * @param   {String}    params.trigger_id     Actions Trigger ID.
+ * @param   {Object}    data                  bindings array
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.updateTriggerBindings = function(params, data, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.triggerBindings.patch(params, data, cb);
+  }
+
+  return this.triggerBindings.patch(params, data);
+};
+
+/**
+ * test an Trigger.
+ *
+ * @method    testTrigger
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var params = { trigger_id: TRIGGER_ID};
+ * auth0.actions.testTrigger(params, payload, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+
+ * });
+ *
+ * @param   {Object}    params                Action parameters.
+ * @param   {String}    params.trigger_id     Trigger ID.
+ * @param   {Object}    payload               Payload represents the entire structure necessary to test a particular trigger
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.testTrigger = function(params, payload, cb) {
+  params = params || {};
+  payload = payload || {};
+
+  if (cb && cb instanceof Function) {
+    return this.triggersTest.create(params, payload, cb);
+  }
+
+  return this.triggersTest.create(params, payload);
+};
+
+/**
+ * Get the actions bound to a trigger .
+ *
+ * @method    getTriggerBindings
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var params = { trigger_id: TRIGGER_ID };
+ *
+ * // Using auth0 instance.
+ * management.actions.getTriggerBindings(params, function (err, bindings) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(bindings.length);  // 2
+ * });
+ *
+ * @param   {Object}    params                Actions Binding parameters.
+ * @param   {String}    params.trigger_id     Actions Trigger ID.
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.getTriggerBindings = function(params, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.triggerBindings.getAll(params, cb);
+  }
+
+  return this.triggerBindings.getAll(params);
+};
+
+/**
+ * Create a new Action.
+ *
+ * @method    create
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * management.actions.create(data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Action created.
+ * });
+ *
+ * @param   {Object}    data     Action data object.
+ * @param   {Function}  [cb]     Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ActionsManager, 'create', 'resource.create');
+
+/**
+ * Get all actions.
+ *
+ * @method    getAll
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example <caption>
+ *   This method takes an optional object as first argument that may be used to
+ *   specify pagination settings. If pagination options are not present,
+ *   the first page of a limited number of results will be returned.
+ * </caption>
+ * // Pagination settings.
+ * var params = {
+ *   per_page: 10,
+ *   page: 0,
+ *   triggerId:'TRIGGER_ID',
+ *   actionName: 'ACTION_NAME'
+ * };
+ *
+ * management.actions.getAll(params, function (err, actions) {
+ *   console.log(actions.length);
+ * });
+ *
+ * @param   {Object}    [params]               Actions parameters.
+ * @param   {Number}    [params.per_page]      Number of results per page.
+ * @param   {Number}    [params.page]          Page number, zero indexed.
+ * @param   {String}    [params.triggerId]     The trigger ID of the actions to retrieve.
+ * @param   {String}    [params.actionName]    The name of the actions to retrieve.
+ * @param   {Function}  [cb]                   Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ActionsManager, 'getAll', 'resource.getAll');
+
+/**
+ * Get an Auth0 action.
+ *
+ * @method    get
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * management.actions.get({ action_id: ACTION_ID }, function (err, action) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(action);
+ * });
+ *
+ * @param   {Object}    params               Action parameters.
+ * @param   {String}    params.action_id     Action ID.
+ * @param   {Function}  [cb]                 Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ActionsManager, 'get', 'resource.get');
+
+/**
+ * Update an existing action.
+ *
+ * @method    update
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var data = { name: 'new-name' };
+ * var params = { action_id: ACTION_ID };
+ *
+ * // Using auth0 instance.
+ * management.updateAction(params, data, function (err, action) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(action.name);  // 'new-name'
+ * });
+ *
+ * // Using the Actions manager directly.
+ * management.actions.update(params, data, function (err, action) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(action.name);  // 'new-name'
+ * });
+ *
+ * @param   {Object}    params        Action parameters.
+ * @param   {String}    params.action_id     Action ID.
+ * @param   {Object}    data          Updated action data.
+ * @param   {Function}  [cb]          Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ActionsManager, 'update', 'resource.patch');
+
+/**
+ * Delete an existing action. Deleting an Action deletes all the action's versions
+ *
+ * @method    delete
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * management.actions.delete({ action_id: ACTION_ID }, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Action deleted.
+ * });
+ *
+ * @param   {Object}    params        Action parameters.
+ * @param   {String}    params.action_id     Action ID.
+ * @param   {Function}  [cb]          Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ActionsManager, 'delete', 'resource.delete');
+
+/**
+ * test an Action.
+ *
+ * @method    test
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var params = { action_id: ACTION_ID};
+ * management.actions.testAction(params, payload, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+
+ * });
+ *
+ * @param   {Object}    params                Action parameters.
+ * @param   {String}    params.action_id      Action ID.
+ * @param   {Object}    payload               Payload represents the entire structure necessary to test a particular trigger
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.test = function(params, payload, cb) {
+  params = params || {};
+  payload = payload || {};
+
+  if (cb && cb instanceof Function) {
+    return this.actionsTest.create(params, payload, cb);
+  }
+
+  return this.actionsTest.create(params, payload);
+};
+
+/**
+ * deploy an Action.
+ * The action must be in a state of 'built' before it can be deployed.
+ *
+ * @method    deploy
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var params = { action_id: ACTION_ID};
+ * mangement.actions.deploy(params, function (err, actionVersion) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+
+ * });
+ *
+ * @param   {Object}    params                Action parameters.
+ * @param   {String}    params.action_id      Action ID.
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.deploy = function(params, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.actionsDeploy.create(params, {}, cb);
+  }
+
+  return this.actionsDeploy.create(params, {});
+};
+
+/**
+ * Get all action versions
+ *
+ * @method    getVersions
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example <caption>
+ *   This method takes an optional object as first argument that may be used to
+ *   specify pagination settings. If pagination options are not present,
+ *   the first page of a limited number of results will be returned.
+ * </caption>
+ * // Pagination settings.
+ * var params = {
+ *   per_page: 10,
+ *   page: 0
+ * };
+ *
+ * management.actions.getVersions({ action_id: ACTION_ID }, function (err, actionVersions) {
+ *   console.log(actionVersions.length);
+ * });
+ *
+ * @param   {Object}    [params]               ActionVersions parameters.
+ * @param   {Number}    [params.per_page]      Number of results per page.
+ * @param   {Number}    [params.page]          Page number, zero indexed.
+ * @param   {String}    [params.action_id]     Action ID.
+ * @param   {Function}  [cb]                   Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.getVersions = function(params, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.actionVersions.getAll(params, cb);
+  }
+
+  return this.actionVersions.getAll(params);
+};
+
+/**
+ * Get an Action Version.
+ *
+ * @method    getVersion
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var params = { action_id: ACTION_ID, version_id: VERSION_ID };
+ * management.actions.getVersion(params, function (err, actionVersion) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+
+ * });
+ *
+ * @param   {Object}    params                Action parameters.
+ * @param   {String}    params.action_id      Action ID.
+ * @param   {String}    params.version_id     ActionVersion ID.
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.getVersion = function(params, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.actionVersions.get(params, cb);
+  }
+
+  return this.actionVersions.get(params);
+};
+
+/**
+ * Create an Action Version. In general, updating an action and then
+ * deploying it is the preferred way of creating an action version, but
+ * this operation is supported as a way of creating versions without
+ * updating the action's code (which can be useful in some CI/CD scenarios).
+ *
+ * @method    createVersion
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var params = { action_id: ACTION_ID };
+ * management.actions.createActionVersion(params, data, function (err, actionVersion) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ * });
+ *
+ * @param   {Object}    params                Action parameters.
+ * @param   {String}    params.action_id      Action ID.
+ * @param   {Object}    data                  ActionVersion parameters.
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.createVersion = function(params, data, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.actionVersions.create(params, data, cb);
+  }
+
+  return this.actionVersions.create(params, data);
+};
+
+/**
+ * deploy an Action Version to roll back to a previous version.
+ *
+ * @method    deployVersion
+ * @memberOf  module:management.ActionsManager.prototype
+ *
+ * @example
+ * var params = { action_id: ACTION_ID, version_id: VERSION_ID };
+ * management.actions.deployVersion(params, function (err, actionVersion) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+
+ * });
+ *
+ * @param   {Object}    params                Action parameters.
+ * @param   {String}    params.action_id      Action ID.
+ * @param   {String}    params.version_id     Action ID.
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.deployVersion = function(params, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.actionVersionDeploy.create(params, {}, cb);
+  }
+
+  return this.actionVersionDeploy.create(params, {});
+};
+
+/**
+ * Get an execution by ID.
+ *
+ * @method    getExecution
+ * @memberOf  module:management.ActionExecutionsManager.prototype
+ *
+ * @example
+ * management.actions.getExecution({ execution_id: EXECUTION_ID }, function (err, action) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(ActionExecution);
+ * });
+ *
+ * @param   {Object}    params                  Action Execution parameters.
+ * @param   {String}    params.execution_id     Action Execution ID.
+ * @param   {Function}  [cb]                    Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+ActionsManager.prototype.getExecution = function(params, cb) {
+  params = params || {};
+
+  if (cb && cb instanceof Function) {
+    return this.executions.get(params, cb);
+  }
+
+  return this.executions.get(params);
+};
+
+module.exports = ActionsManager;
diff --git a/src/management/index.js b/src/management/index.js
index e71bc054b..670937b47 100644
--- a/src/management/index.js
+++ b/src/management/index.js
@@ -34,6 +34,7 @@ var HooksManager = require('./HooksManager');
 var BrandingManager = require('./BrandingManager');
 var MigrationsManager = require('./MigrationsManager');
 var PromptsManager = require('./PromptsManager');
+var ActionsManager = require('./ActionsManager');
 var OrganizationsManager = require('./OrganizationsManager');
 
 var BASE_URL_FORMAT = 'https://%s/api/v2';
@@ -352,6 +353,14 @@ var ManagementClient = function(options) {
    */
   this.prompts = new PromptsManager(managerOptions);
 
+  /**
+   * Simple abstraction for performing CRUD operations on the
+   * actions endpoint.
+   *
+   * @type {ActionsManager}
+   */
+  this.actions = new ActionsManager(managerOptions);
+
   /**
    * Organizations Manager
    *
@@ -3683,7 +3692,11 @@ utils.wrapPropertyMethod(ManagementClient, 'getBrandingSettings', 'branding.getS
  *
  * @return    {Promise|undefined}
  */
-utils.wrapPropertyMethod(ManagementClient, 'getBrandingUniversalLoginTemplate', 'branding.getUniversalLoginTemplate');
+utils.wrapPropertyMethod(
+  ManagementClient,
+  'getBrandingUniversalLoginTemplate',
+  'branding.getUniversalLoginTemplate'
+);
 
 /**
  * Get the new universal login template.
@@ -3704,7 +3717,11 @@ utils.wrapPropertyMethod(ManagementClient, 'getBrandingUniversalLoginTemplate',
  *
  * @return    {Promise|undefined}
  */
-utils.wrapPropertyMethod(ManagementClient, 'setBrandingUniversalLoginTemplate', 'branding.setUniversalLoginTemplate');
+utils.wrapPropertyMethod(
+  ManagementClient,
+  'setBrandingUniversalLoginTemplate',
+  'branding.setUniversalLoginTemplate'
+);
 
 /**
  * Delete the new universal login template.
@@ -3725,7 +3742,11 @@ utils.wrapPropertyMethod(ManagementClient, 'setBrandingUniversalLoginTemplate',
  *
  * @return    {Promise|undefined}
  */
-utils.wrapPropertyMethod(ManagementClient, 'deleteBrandingUniversalLoginTemplate', 'branding.deleteUniversalLoginTemplate');
+utils.wrapPropertyMethod(
+  ManagementClient,
+  'deleteBrandingUniversalLoginTemplate',
+  'branding.deleteUniversalLoginTemplate'
+);
 
 /**
  * Update the tenant migrations.
@@ -3781,11 +3802,7 @@ utils.wrapPropertyMethod(ManagementClient, 'getMigrations', 'migrations.getMigra
  *
  * @example
  * management.getPromptsSettings(function (err, settings) {
- *   if (err) {
- *     // Handle error.
- *   }
- *
- *   console.log(settings);
+ * *   console.log(settings);
  * });
  *
  * @param   {Function}  [cb]  Callback function.
@@ -3826,11 +3843,7 @@ utils.wrapPropertyMethod(ManagementClient, 'updatePromptsSettings', 'prompts.upd
  * var params = { prompt: PROMPT_NAME, language: LANGUAGE };
  *
  * management.prompts.getCustomTextByLanguage(params, function (err, customText) {
- *   if (err) {
- *     // Handle error.
- *   }
- *
- *   console.log('CustomText', customText);
+ *  *   console.log('CustomText', customText);
  * });
  *
  * @param   {Object}    params            Data object.
@@ -3857,11 +3870,7 @@ utils.wrapPropertyMethod(
  * var params = { prompt: PROMPT_NAME, language: LANGUAGE, body: BODY_OBJECT };
  *
  * management.prompts.updateCustomTextByLanguage(params, function (err, customText) {
- *   if (err) {
- *     // Handle error.
- *   }
- *
- *   console.log('CustomText', customText);
+ * *   console.log('CustomText', customText);
  * });
  *
  * @param   {Object}    params            Data object.


Test patch:
diff --git a/test/management/actions.tests.js b/test/management/actions.tests.js
new file mode 100644
index 000000000..c43d1367f
--- /dev/null
+++ b/test/management/actions.tests.js
@@ -0,0 +1,1191 @@
+var expect = require('chai').expect;
+var nock = require('nock');
+
+var SRC_DIR = '../../src';
+var API_URL = 'https://tenant.auth0.com';
+
+var ActionsManager = require(SRC_DIR + '/management/ActionsManager');
+var ArgumentError = require('rest-facade').ArgumentError;
+
+describe('ActionsManager', function() {
+  before(function() {
+    this.token = 'TOKEN';
+    this.actions = new ActionsManager({
+      headers: { authorization: 'Bearer ' + this.token },
+      baseUrl: API_URL
+    });
+  });
+
+  describe('instance', function() {
+    var methods = [
+      'get',
+      'getAll',
+      'create',
+      'update',
+      'delete',
+      'deploy',
+      'test',
+      'getVersions',
+      'getVersion',
+      'createVersion',
+      'deployVersion',
+      'getAllTriggers',
+      'testTrigger',
+      'getTriggerBindings',
+      'updateTriggerBindings'
+    ];
+
+    methods.forEach(function(method) {
+      it('should have a ' + method + ' method', function() {
+        expect(this.actions[method]).to.exist.to.be.an.instanceOf(Function);
+      });
+    });
+  });
+
+  describe('#constructor', function() {
+    it('should error when no options are provided', function() {
+      expect(ActionsManager).to.throw(ArgumentError, 'Must provide client options');
+    });
+
+    it('should throw an error when no base URL is provided', function() {
+      var client = ActionsManager.bind(null, {});
+
+      expect(client).to.throw(ArgumentError, 'Must provide a base URL for the API');
+    });
+
+    it('should throw an error when the base URL is invalid', function() {
+      var client = ActionsManager.bind(null, { baseUrl: '' });
+
+      expect(client).to.throw(ArgumentError, 'The provided base URL is invalid');
+    });
+  });
+
+  describe('#actions', () => {
+    describe('#getAll', function() {
+      beforeEach(function() {
+        this.request = nock(API_URL)
+          .get('/actions/actions')
+          .reply(200);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.getAll(function() {
+          done();
+        });
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .getAll()
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/actions')
+          .reply(500);
+
+        this.actions.getAll().catch(function(err) {
+          expect(err).to.exist;
+          done();
+        });
+      });
+
+      it('should pass the body of the response to the "then" handler', function(done) {
+        nock.cleanAll();
+
+        var data = [{ test: true }];
+        var request = nock(API_URL)
+          .get('/actions/actions')
+          .reply(200, data);
+
+        this.actions.getAll().then(function(credentials) {
+          expect(credentials).to.be.an.instanceOf(Array);
+
+          expect(credentials.length).to.equal(data.length);
+
+          expect(credentials[0].test).to.equal(data[0].test);
+
+          done();
+        });
+      });
+
+      it('should perform a GET request', function(done) {
+        var request = this.request;
+
+        this.actions.getAll().then(function() {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/actions')
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.getAll().then(function() {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+      });
+
+      it('should pass the parameters in the query-string', function(done) {
+        nock.cleanAll();
+
+        var params = {
+          include_fields: true,
+          fields: 'test'
+        };
+        var request = nock(API_URL)
+          .get('/actions/actions')
+          .query(params)
+          .reply(200);
+
+        this.actions.getAll(params).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#get', function() {
+      beforeEach(function() {
+        this.data = {
+          id: '0d565aa1-d8ce-4802-83e7-82e3d2040222',
+          name: 'Test Action',
+          supported_triggers: [
+            {
+              id: 'post-login',
+              version: 'v1'
+            }
+          ],
+          required_configuration: [],
+          required_secrets: [],
+          created_at: '2020-07-29T19:45:15.725999098Z',
+          updated_at: '2020-07-29T19:45:15.725999098Z'
+        };
+
+        this.request = nock(API_URL)
+          .get('/actions/actions/' + this.data.id)
+          .reply(200, this.data);
+      });
+
+      it('should accept a callback', function(done) {
+        var params = { action_id: this.data.id };
+
+        this.actions.get(params, done.bind(null, null));
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .get({ action_id: this.data.id })
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should perform a GET request', function(done) {
+        var request = this.request;
+
+        this.actions.get({ action_id: this.data.id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/actions' + this.data.id)
+          .reply(500);
+
+        this.actions.get({ action_id: this.data.id }).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/actions/' + this.data.id)
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.get({ action_id: this.data.id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#create', function() {
+      var data = {
+        name: 'my-action-13',
+        supported_triggers: [
+          {
+            id: 'post-login',
+            version: 'v1'
+          }
+        ]
+      };
+
+      beforeEach(function() {
+        this.request = nock(API_URL)
+          .post('/actions/actions')
+          .reply(201);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.create(data, function() {
+          done();
+        });
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .create(data)
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/')
+          .reply(500);
+
+        this.actions.create(data).catch(function(err) {
+          expect(err).to.exist;
+          done();
+        });
+      });
+
+      it('should perform a POST request', function(done) {
+        var request = this.request;
+
+        this.actions.create(data).then(function() {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+      });
+
+      it('should pass the data in the body of the request', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions', data)
+          .reply(200);
+
+        this.actions.create(data).then(function() {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions')
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.create(data).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#update', function() {
+      beforeEach(function() {
+        this.data = { action_id: 'ACTION_ID' };
+
+        this.request = nock(API_URL)
+          .patch('/actions/actions/' + this.data.action_id, { name: 'my-new-action-name' })
+          .reply(200, this.data);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.update(
+          { action_id: 'ACTION_ID' },
+          { name: 'my-new-action-name' },
+          done.bind(null, null)
+        );
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .update({ action_id: 'ACTION_ID' }, { name: 'my-new-action-name' })
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should perform a PATCH request', function(done) {
+        var request = this.request;
+
+        this.actions
+          .update({ action_id: 'ACTION_ID' }, { name: 'my-new-action-name' })
+          .then(function() {
+            expect(request.isDone()).to.be.true;
+
+            done();
+          });
+      });
+
+      it('should include the new data in the body of the request', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .patch('/actions/actions/' + this.data.action_id, { name: 'my-new-action-name' })
+          .reply(200);
+
+        this.actions
+          .update({ action_id: 'ACTION_ID' }, { name: 'my-new-action-name' })
+          .then(function() {
+            expect(request.isDone()).to.be.true;
+
+            done();
+          });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .patch('/actions/actions/' + this.data.id)
+          .reply(500);
+
+        this.actions
+          .update({ action_id: this.data.id }, { name: 'my-new-action-name' })
+          .catch(function(err) {
+            expect(err).to.exist;
+
+            done();
+          });
+      });
+    });
+
+    describe('#deploy', function() {
+      var action_id = 'action-id-1';
+
+      beforeEach(function() {
+        this.request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/deploy')
+          .reply(200);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.deploy({ action_id }, done.bind(null, null));
+      });
+
+      it('should return a promise when no callback is given', function(done) {
+        this.actions.deploy({ action_id }).then(done.bind(null, null));
+      });
+
+      it('should perform a post request' + action_id, function(done) {
+        var request = this.request;
+
+        this.actions.deploy({ action_id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/deploy')
+          .reply(500);
+
+        this.actions.deploy({ action_id }).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+
+      it('should include the token in the authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/deploy')
+          .matchHeader('authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.deploy({ action_id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#test', function() {
+      var action_id = 'action-id-1';
+      const payload = { event: {} };
+
+      beforeEach(function() {
+        this.request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/test')
+          .reply(200);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.test({ action_id }, { payload }, done.bind(null, null));
+      });
+
+      it('should return a promise when no callback is given', function(done) {
+        this.actions.test({ action_id }, { payload }).then(done.bind(null, null));
+      });
+
+      it('should perform a post request' + action_id, function(done) {
+        var request = this.request;
+
+        this.actions.test({ action_id }, { payload }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/test')
+          .reply(500);
+
+        this.actions.test({ action_id }, { payload }).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+
+      it('should include the token in the authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/test')
+          .matchHeader('authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.test({ action_id }, { payload }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#delete', function() {
+      var action_id = 'action-id-1';
+
+      beforeEach(function() {
+        this.request = nock(API_URL)
+          .delete('/actions/actions/' + action_id)
+          .reply(200);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.delete({ action_id }, done.bind(null, null));
+      });
+
+      it('should return a promise when no callback is given', function(done) {
+        this.actions.delete({ action_id }).then(done.bind(null, null));
+      });
+
+      it('should perform a delete request' + action_id, function(done) {
+        var request = this.request;
+
+        this.actions.delete({ action_id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .delete('/actions/actions/' + action_id)
+          .reply(500);
+
+        this.actions.delete({ action_id }).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+
+      it('should include the token in the authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .delete('/actions/actions/' + action_id)
+          .matchHeader('authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.delete({ action_id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+  });
+
+  describe('action versions', function() {
+    describe('#getVersions', function() {
+      beforeEach(function() {
+        nock.cleanAll();
+
+        this.data = {
+          id: '0d565aa1-d8ce-4802-83e7-82e3d2040222'
+        };
+
+        this.request = nock(API_URL)
+          .get('/actions/actions/' + this.data.id + '/versions')
+          .reply(200, this.data);
+      });
+
+      it('should accept a callback', function(done) {
+        var params = { action_id: this.data.id };
+
+        this.actions.getVersions(params, done.bind(null, null));
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .get({ action_id: this.data.id })
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should perform a GET request', function(done) {
+        var request = this.request;
+
+        this.actions.getVersions({ action_id: this.data.id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/actions/' + this.data.id + '/versions')
+          .reply(500);
+
+        this.actions.getVersions({ action_id: this.data.id }).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/actions/' + this.data.id + '/versions')
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.getVersions({ action_id: this.data.id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#getVersion', function() {
+      beforeEach(function() {
+        nock.cleanAll();
+
+        this.data = {
+          id: '0d565aa1-d8ce-4802-83e7-82e3d2040222',
+          versionId: '7asd8sd9-d8ce-4802-83e7-82e3d2040222'
+        };
+
+        this.request = nock(API_URL)
+          .get('/actions/actions/' + this.data.id + '/versions/' + this.data.versionId)
+          .reply(200, this.data);
+      });
+
+      it('should accept a callback', function(done) {
+        var params = { action_id: this.data.id, version_id: this.data.versionId };
+
+        this.actions.getVersion(params, done.bind(null, null));
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .get({ action_id: this.data.id, version_id: this.data.versionId })
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should perform a GET request', function(done) {
+        var request = this.request;
+
+        this.actions
+          .getVersion({ action_id: this.data.id, version_id: this.data.versionId })
+          .then(function() {
+            expect(request.isDone()).to.be.true;
+
+            done();
+          });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/actions/' + this.data.id + '/versions/' + this.data.versionId)
+          .reply(500);
+
+        this.actions
+          .getVersion({ action_id: this.data.id, version_id: this.data.versionId })
+          .catch(function(err) {
+            expect(err).to.exist;
+
+            done();
+          });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/actions/' + this.data.id + '/versions/' + this.data.versionId)
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions
+          .getVersion({ action_id: this.data.id, version_id: this.data.versionId })
+          .then(function() {
+            expect(request.isDone()).to.be.true;
+
+            done();
+          });
+      });
+    });
+
+    describe('#createVersion', function() {
+      beforeEach(function() {
+        nock.cleanAll();
+
+        this.data = {
+          id: '0d565aa1-d8ce-4802-83e7-82e3d2040222'
+        };
+
+        this.request = nock(API_URL)
+          .post('/actions/actions/' + this.data.id + '/versions')
+          .reply(200, this.data);
+      });
+
+      it('should accept a callback', function(done) {
+        var params = { action_id: this.data.id };
+
+        this.actions.createVersion(params, this.data, done.bind(null, null));
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .createVersion({ action_id: this.data.id }, {})
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should perform a POST request', function(done) {
+        var request = this.request;
+
+        this.actions.createVersion({ action_id: this.data.id }, {}).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/' + this.data.id + '/versions')
+          .reply(500);
+
+        this.actions.createVersion({ action_id: this.data.id }, {}).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/' + this.data.id + '/versions')
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.createVersion({ action_id: this.data.id }, {}).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#deployVersion', function() {
+      var action_id = 'action-id-1';
+      var version_id = 'action-version-id-1';
+
+      beforeEach(function() {
+        this.request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/versions/' + version_id + '/deploy')
+          .reply(200);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.deployVersion({ action_id, version_id }, done.bind(null, null));
+      });
+
+      it('should return a promise when no callback is given', function(done) {
+        this.actions.deployVersion({ action_id, version_id }).then(done.bind(null, null));
+      });
+
+      it('should perform a post request', function(done) {
+        var request = this.request;
+
+        this.actions.deployVersion({ action_id, version_id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/versions/' + version_id + '/deploy')
+          .reply(500);
+
+        this.actions.deployVersion({ action_id, version_id }).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+
+      it('should include the token in the authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/actions/' + action_id + '/deploy')
+          .matchHeader('authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.deploy({ action_id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+  });
+
+  describe('executions', function() {
+    describe('#getExecution', function() {
+      beforeEach(function() {
+        this.data = {
+          id: '0d565aa1-d8ce-4802-83e7',
+          name: 'Execution'
+        };
+
+        this.request = nock(API_URL)
+          .get('/actions/executions/' + this.data.id)
+          .reply(200);
+      });
+
+      it('should accept a callback', function(done) {
+        var params = { execution_id: this.data.id };
+
+        this.actions.getExecution(params, done.bind(null, null));
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .getExecution({ execution_id: this.data.id })
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should perform a GET request', function(done) {
+        var request = this.request;
+
+        this.actions
+          .getExecution({ execution_id: this.data.id })
+          .then(function() {
+            expect(request.isDone()).to.be.true;
+
+            done();
+          })
+          .catch(function(err) {
+            console.log(err);
+          });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/executions/' + this.data.id)
+          .reply(500);
+
+        this.actions.getExecution({ execution_id: this.data.id }).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/executions/' + this.data.id)
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.getExecution({ execution_id: this.data.id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+  });
+
+  describe('triggers', () => {
+    const params = { per_page: 2 };
+    describe('#getAllTriggers', function() {
+      beforeEach(function() {
+        this.request = nock(API_URL)
+          .get('/actions/triggers')
+          .reply(200);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.getAllTriggers(params, function() {
+          done();
+        });
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .getAllTriggers()
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/triggers')
+          .reply(500);
+
+        this.actions.getAllTriggers().catch(function(err) {
+          expect(err).to.exist;
+          done();
+        });
+      });
+
+      it('should pass the body of the response to the "then" handler', function(done) {
+        nock.cleanAll();
+
+        var data = [{ trigger1: 'rigger1' }];
+        var request = nock(API_URL)
+          .get('/actions/triggers')
+          .reply(200, data);
+
+        this.actions.getAllTriggers().then(function(triggers) {
+          expect(triggers).to.be.an.instanceOf(Array);
+
+          expect(triggers.length).to.equal(data.length);
+
+          expect(triggers[0].trigger1).to.equal(data[0].trigger1);
+
+          done();
+        });
+      });
+
+      it('should perform a GET request', function(done) {
+        var request = this.request;
+
+        this.actions.getAllTriggers().then(function() {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/triggers')
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.getAllTriggers().then(function() {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+      });
+
+      it('should pass the parameters in the query-string', function(done) {
+        nock.cleanAll();
+
+        var params = {
+          include_fields: true,
+          fields: 'test'
+        };
+        var request = nock(API_URL)
+          .get('/actions/triggers')
+          .query(params)
+          .reply(200);
+
+        this.actions.getAllTriggers(params).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#testTrigger', function() {
+      const params = { trigger_id: 'post-login' };
+      const data = {
+        payload: {
+          user_info: 'userInfo'
+        }
+      };
+
+      beforeEach(function() {
+        this.request = nock(API_URL)
+          .post('/actions/triggers/' + params.trigger_id + '/test', data)
+          .reply(200);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.testTrigger(params, data, function() {
+          done();
+        });
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .testTrigger(params, data)
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/triggers/' + params.trigger_id + '/test', data)
+          .reply(500);
+
+        this.actions.testTrigger(params, data).catch(function(err) {
+          expect(err).to.exist;
+          done();
+        });
+      });
+
+      it('should perform a POST request', function(done) {
+        var request = this.request;
+
+        this.actions.testTrigger(params, data).then(function() {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+      });
+
+      it('should pass the data in the body of the request', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/triggers/' + params.trigger_id + '/test', data)
+          .reply(200);
+
+        this.actions.testTrigger(params, data).then(function() {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+      });
+
+      it('should include the token in the Authorization header', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .post('/actions/triggers/' + params.trigger_id + '/test', data)
+          .matchHeader('Authorization', 'Bearer ' + this.token)
+          .reply(200);
+
+        this.actions.testTrigger(params, data).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+    });
+
+    describe('#updateTriggerBindings', function() {
+      const trigger_id = 'post-login';
+
+      beforeEach(function() {
+        this.data = { bindings: [] };
+
+        this.request = nock(API_URL)
+          .patch('/actions/triggers/' + trigger_id + '/bindings')
+          .reply(200, this.data);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.updateTriggerBindings({ trigger_id }, this.data, done.bind(null, null));
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .updateTriggerBindings({ trigger_id }, this.data)
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should perform a PATCH request', function(done) {
+        var request = this.request;
+
+        this.actions.updateTriggerBindings({ trigger_id }, this.data).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should include the new data in the body of the request', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .patch('/actions/triggers/' + trigger_id + '/bindings')
+          .reply(200);
+
+        this.actions.updateTriggerBindings({ trigger_id }, this.data).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .patch('/actions/triggers/' + trigger_id + '/bindings')
+          .reply(500);
+
+        this.actions.updateTriggerBindings({ trigger_id }, this.data).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+    });
+
+    describe('#getTriggerBindings', function() {
+      const trigger_id = 'post-login';
+
+      beforeEach(function() {
+        this.data = { bindings: {} };
+
+        this.request = nock(API_URL)
+          .get('/actions/triggers/' + trigger_id + '/bindings')
+          .reply(200, this.data);
+      });
+
+      it('should accept a callback', function(done) {
+        this.actions.getTriggerBindings({ trigger_id }, done.bind(null, null));
+      });
+
+      it('should return a promise if no callback is given', function(done) {
+        this.actions
+          .getTriggerBindings({ trigger_id })
+          .then(done.bind(null, null))
+          .catch(done.bind(null, null));
+      });
+
+      it('should perform a GET request', function(done) {
+        var request = this.request;
+
+        this.actions.getTriggerBindings({ trigger_id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should include the new data in the body of the request', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .get('/actions/triggers/' + trigger_id + '/bindings')
+          .reply(200);
+
+        this.actions.getTriggerBindings({ trigger_id }).then(function() {
+          expect(request.isDone()).to.be.true;
+
+          done();
+        });
+      });
+
+      it('should pass any errors to the promise catch handler', function(done) {
+        nock.cleanAll();
+
+        var request = nock(API_URL)
+          .patch('/actions/triggers/' + trigger_id + '/bindings')
+          .reply(500);
+
+        this.actions.getTriggerBindings({ trigger_id }).catch(function(err) {
+          expect(err).to.exist;
+
+          done();
+        });
+      });
+    });
+  });
+});