Repository: auth0/node-auth0
Pull request #142 (2016-11-08 23:11:44)

Add manager for the Client Grants endpoint (updated)
Added client grants manager methods to management client there were missing in https://github.com/auth0/node-auth0/pull/129

coveralls: 
[![Coverage Status](https://coveralls.io/builds/8733433/badge)](https://coveralls.io/builds/8733433)

Coverage increased (+0.2%) to 95.099% when pulling **a9935d2be2a271b36254ed0d929a0621d9ed988c on ognus:master** into **96b2a12b4cbed44c7611ed2f9b774cd45b9a0b93 on auth0:master**.


Patch:
diff --git a/src/management/ClientGrantsManager.js b/src/management/ClientGrantsManager.js
new file mode 100644
index 000000000..3f1e24f9f
--- /dev/null
+++ b/src/management/ClientGrantsManager.js
@@ -0,0 +1,149 @@
+var RestClient = require('rest-facade').Client;
+var ArgumentError = require('../exceptions').ArgumentError;
+var utils = require('../utils');
+
+
+/**
+ * @class ClientGrantsManager
+ * Auth0 Client Grants Manager.
+ *
+ * See {@link https://auth0.com/docs/api/v2#!/Client_Grants Client Grants}
+ *
+ * @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.
+ */
+var ClientGrantsManager = 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 = {
+    headers: options.headers,
+    query: { repeatParams: false }
+  };
+
+  /**
+   * Provides an abstraction layer for consuming the
+   * {@link https://auth0.com/docs/api/v2#!/Client_Grants Auth0 Client Grantss endpoint}.
+   *
+   * @type {external:RestClient}
+   */
+  this.resource = new RestClient(options.baseUrl + '/client-grants/:id', clientOptions);
+};
+
+
+/**
+ * Create an Auth0 client grant.
+ *
+ * @method    create
+ * @memberOf  module:management.ClientGrantsManager.prototype
+ *
+ * @example
+ * management.clientGrants.create(data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Client grant created.
+ * });
+ *
+ * @param   {Object}    data     The client data object.
+ * @param   {Function}  [cb]     Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ClientGrantsManager, 'create', 'resource.create');
+
+
+/**
+ * Get all Auth0 Client Grants.
+ *
+ * @method    getAll
+ * @memberOf  module:management.ClientGrantsManager.prototype
+ *
+ * @example
+ * management.clientGrants.getAll(function (err, grants) {
+ *   console.log(grants.length);
+ * });
+ *
+ * @param   {Function}  [cb]    Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ClientGrantsManager, 'getAll', 'resource.getAll');
+
+
+/**
+ * Update an Auth0 client grant.
+ *
+ * @method    update
+ * @memberOf  module:management.ClientGrantsManager.prototype
+ *
+ * @example
+ * var data = {
+ *   client_id: CLIENT_ID,
+ *   audience: AUDIENCE,
+ *   scope: []
+ * };
+ * var params = { id: CLIENT_GRANT_ID };
+ *
+ * management.clientGrants.update(params, data, function (err, grant) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(grant.id);
+ * });
+ *
+ * @param   {Object}    params     Client parameters.
+ * @param   {String}    params.id  Client grant ID.
+ * @param   {Object}    data       Updated client data.
+ * @param   {Function}  [cb]       Callback function.
+ *
+ * @return    {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ClientGrantsManager, 'update', 'resource.patch');
+
+
+/**
+ * Delete an Auth0 client grant.
+ *
+ * @method    delete
+ * @memberOf  module:management.ClientGrantsManager.prototype
+ *
+ * @example
+ * management.clientGrants.delete({ id: GRANT_ID }, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Grant deleted.
+ * });
+ *
+ * @param   {Object}    params     Client parameters.
+ * @param   {String}    params.id  Client grant ID.
+ * @param   {Function}  [cb]       Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ClientGrantsManager, 'delete', 'resource.delete');
+
+
+module.exports = ClientGrantsManager;
diff --git a/src/management/index.js b/src/management/index.js
index bd7ee030a..9cc6b6f10 100644
--- a/src/management/index.js
+++ b/src/management/index.js
@@ -9,6 +9,7 @@ var ArgumentError = require('../exceptions').ArgumentError;
 
 // Managers.
 var ClientsManager = require('./ClientsManager');
+var ClientGrantsManager = require('./ClientGrantsManager');
 var UsersManager = require('./UsersManager');
 var ConnectionsManager = require('./ConnectionsManager');
 var BlacklistedTokensManager = require('./BlacklistedTokensManager');
@@ -86,6 +87,14 @@ var ManagementClient = function (options) {
    */
   this.clients = new ClientsManager(managerOptions);
 
+  /**
+   * Simple abstraction for performing CRUD operations on the client grants
+   * endpoint.
+   *
+   * @type {ClientGrantsManager}
+   */
+  this.clientGrants = new ClientGrantsManager(managerOptions);
+
   /**
    * Simple abstraction for performing CRUD operations on the
    * users endpoint.
@@ -438,6 +447,103 @@ utils.wrapPropertyMethod(ManagementClient, 'updateClient', 'clients.update');
 utils.wrapPropertyMethod(ManagementClient, 'deleteClient', 'clients.delete');
 
 
+/**
+ * Get all Auth0 Client Grants.
+ *
+ * @method    getAll
+ * @memberOf  module:management.ClientGrantsManager.prototype
+ *
+ * @example
+ * management.clientGrants.getAll(function (err, grants) {
+ *   console.log(grants.length);
+ * });
+ *
+ * @param   {Function}  [cb]    Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'getClientGrants', 'clientGrants.getAll');
+
+
+/**
+ * Create an Auth0 client grant.
+ *
+ * @method    create
+ * @memberOf  module:management.ClientGrantsManager.prototype
+ *
+ * @example
+ * management.clientGrants.create(data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Client grant created.
+ * });
+ *
+ * @param   {Object}    data     The client data object.
+ * @param   {Function}  [cb]     Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'createClientGrant', 'clientGrants.create');
+
+
+/**
+ * Update an Auth0 client grant.
+ *
+ * @method    update
+ * @memberOf  module:management.ClientGrantsManager.prototype
+ *
+ * @example
+ * var data = {
+ *   client_id: CLIENT_ID,
+ *   audience: AUDIENCE,
+ *   scope: []
+ * };
+ * var params = { id: CLIENT_GRANT_ID };
+ *
+ * management.clientGrants.update(params, data, function (err, grant) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(grant.id);
+ * });
+ *
+ * @param   {Object}    params     Client parameters.
+ * @param   {String}    params.id  Client grant ID.
+ * @param   {Object}    data       Updated client data.
+ * @param   {Function}  [cb]       Callback function.
+ *
+ * @return    {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'updateClientGrant', 'clientGrants.patch');
+
+
+/**
+ * Delete an Auth0 client grant.
+ *
+ * @method    delete
+ * @memberOf  module:management.ClientGrantsManager.prototype
+ *
+ * @example
+ * management.clientGrants.delete({ id: GRANT_ID }, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Grant deleted.
+ * });
+ *
+ * @param   {Object}    params     Client parameters.
+ * @param   {String}    params.id  Client grant ID.
+ * @param   {Function}  [cb]       Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'deleteClientGrant', 'clientGrants.delete');
+
+
 /**
  * Create an Auth0 credential.
  *


Test patch:
diff --git a/test/management/client-grants.tests.js b/test/management/client-grants.tests.js
new file mode 100644
index 000000000..a66b46797
--- /dev/null
+++ b/test/management/client-grants.tests.js
@@ -0,0 +1,369 @@
+var expect = require('chai').expect;
+var nock = require('nock');
+var Promise = require('bluebird');
+
+var SRC_DIR = '../../src';
+var API_URL = 'https://tenant.auth0.com';
+
+var ClientGrantsManager = require(SRC_DIR + '/management/ClientGrantsManager');
+var ArgumentError = require(SRC_DIR + '/exceptions').ArgumentError;
+
+
+describe('ClientGrantsManager', function () {
+  before(function () {
+    this.token = 'TOKEN';
+    this.grants = new ClientGrantsManager({
+      headers: {
+        authorization: 'Bearer ' + this.token
+      },
+      baseUrl: API_URL
+    });
+  });
+
+  afterEach(function () {
+    nock.cleanAll();
+  });
+
+
+  describe('instance', function () {
+    var methods = ['getAll', 'create', 'update', 'delete'];
+
+    methods.forEach(function (method) {
+      it('should have a ' + method + ' method', function () {
+        expect(this.grants[method])
+          .to.exist
+          .to.be.an.instanceOf(Function);
+      })
+    });
+  });
+
+
+  describe('#constructor', function () {
+    it('should error when no options are provided', function () {
+      expect(ClientGrantsManager)
+        .to.throw(ArgumentError, 'Must provide client options');
+    });
+
+
+    it('should throw an error when no base URL is provided', function () {
+      var grants = ClientGrantsManager.bind(null, {});
+
+      expect(grants)
+        .to.throw(ArgumentError, 'Must provide a base URL for the API');
+    });
+
+
+    it('should throw an error when the base URL is invalid', function () {
+      var grants = ClientGrantsManager.bind(null, { baseUrl: '' });
+
+      expect(grants)
+        .to.throw(ArgumentError, 'The provided base URL is invalid');
+    });
+  });
+
+
+  describe('#getAll', function () {
+
+    beforeEach(function () {
+      this.request = nock(API_URL)
+        .get('/client-grants')
+        .reply(200);
+    });
+
+    it('should accept a callback', function (done) {
+      this.grants.getAll(function () {
+        done();
+      });
+    });
+
+
+    it('should return a promise if no callback is given', function (done) {
+      this
+        .grants
+        .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('/client-grants')
+        .reply(500);
+
+      this
+        .grants
+        .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('/client-grants')
+        .reply(200, data);
+
+      this
+        .grants
+        .getAll()
+        .then(function (grants) {
+          expect(grants)
+            .to.be.an.instanceOf(Array);
+
+          expect(grants.length)
+            .to.equal(data.length);
+
+          expect(grants[0].test)
+            .to.equal(data[0].test);
+
+          done();
+        });
+    });
+
+
+    it('should perform a GET request to /api/v2/client-grants', function (done) {
+      var request = this.request;
+
+      this
+        .grants
+        .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('/client-grants')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200)
+
+      this
+        .grants
+        .getAll()
+        .then(function () {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+    });
+
+    it('should pass the parameters in the query-string', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/client-grants')
+        .query({
+          include_fields: true,
+          fields: 'test'
+        })
+        .reply(200)
+
+      this
+        .grants
+        .getAll({ include_fields: true, fields: 'test' })
+        .then(function () {
+          expect(request.isDone()).to.be.true;
+          done();
+        });
+    });
+  });
+
+
+
+  describe('#create', function () {
+    var data = {
+      client_id: 'CLIENT_ID',
+      audience: 'AUDIENCE',
+      scope: ['user']
+    };
+
+    beforeEach(function () {
+      this.request = nock(API_URL)
+        .post('/client-grants')
+        .reply(201, data)
+    })
+
+    it('should accept a callback', function (done) {
+      this
+        .grants
+        .create(data, done.bind(null, null));
+    });
+
+
+    it('should return a promise if no callback is given', function (done) {
+      this
+        .grants
+        .create(data)
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+
+    it('should perform a POST request to /api/v2/client-grants', function (done) {
+      var request = this.request;
+
+      this
+        .grants
+        .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('/client-grants')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(201, data)
+
+      this
+        .grants
+        .create(data)
+        .then(function () {
+          expect(request.isDone())
+            .to.be.true;
+
+          done();
+        });
+    });
+
+
+    it('should include the new client grant data in the request body', function (done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/client-grants', data)
+        .reply(201, data)
+
+      this
+        .grants
+        .create(data)
+        .then(function () {
+          expect(request.isDone())
+            .to.be.true;
+
+          done();
+        });
+    });
+  });
+
+
+  describe('#update', function () {
+    beforeEach(function () {
+      this.data = { id: 5 };
+
+      this.request = nock(API_URL)
+        .patch('/client-grants/' + this.data.id)
+        .reply(200, this.data);
+    });
+
+
+    it('should accept a callback', function (done) {
+      this
+        .grants
+        .update({ id: 5 }, {}, done.bind(null, null));
+    });
+
+
+    it('should return a promise if no callback is given', function (done) {
+      this
+        .grants
+        .update({ id: 5 }, {})
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+
+    it('should perform a PATCH request to /api/v2/client-grants/5', function (done) {
+      var request = this.request;
+
+      this
+        .grants
+        .update({ id: 5 }, {})
+        .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('/client-grants/' + this.data.id, this.data)
+        .reply(200);
+
+      this
+        .grants
+        .update({ id: 5 }, this.data)
+        .then(function () {
+          expect(request.isDone())
+            .to.be.true;
+
+          done();
+        });
+    });
+  });
+
+
+  describe('#delete', function () {
+    var id = 5;
+
+    beforeEach(function () {
+      this.request = nock(API_URL)
+        .delete('/client-grants/' + id)
+        .reply(200);
+    });
+
+
+    it('should accept a callback', function (done) {
+      this
+        .grants
+        .delete({ id: id }, done.bind(null, null));
+    });
+
+
+    it('should return a promise when no callback is given', function (done) {
+      this
+        .grants
+        .delete({ id: id })
+        .then(done.bind(null, null));
+    });
+
+
+    it('should perform a DELETE request to /client-grants/' + id, function (done) {
+      var request = this.request;
+
+      this
+        .grants
+        .delete({ id: id })
+        .then(function () {
+          expect(request.isDone())
+            .to.be.true;
+
+          done();
+        });
+    });
+  });
+});
diff --git a/test/management/management-client.tests.js b/test/management/management-client.tests.js
index 819c0231e..b8eb6ce4e 100644
--- a/test/management/management-client.tests.js
+++ b/test/management/management-client.tests.js
@@ -6,6 +6,7 @@ var ArgumentError = require('../../src/exceptions').ArgumentError;
 var UsersManager = require('../../src/management/UsersManager');
 var BlacklistedTokensManager = require('../../src/management/BlacklistedTokensManager');
 var ClientsManager = require('../../src/management/ClientsManager');
+var ClientGrantsManager = require('../../src/management/ClientGrantsManager');
 var ConnectionsManager = require('../../src/management/ConnectionsManager');
 var DeviceCredentialsManager = require('../../src/management/DeviceCredentialsManager');
 var EmailProviderManager = require('../../src/management/EmailProviderManager');
@@ -55,6 +56,10 @@ describe('ManagementClient', function () {
           property: 'clients',
           cls: ClientsManager
         },
+        'ClientGrantsManager': {
+          property: 'clientGrants',
+          cls: ClientGrantsManager
+        },
         'ConnectionsManager': {
           property: 'connections',
           cls: ConnectionsManager
@@ -115,6 +120,10 @@ describe('ManagementClient', function () {
         'createClient',
         'updateClient',
         'deleteClient',
+        'getClientGrants',
+        'createClientGrant',
+        'updateClientGrant',
+        'deleteClientGrant',
         'createDevicePublicKey',
         'getDeviceCredentials',
         'deleteDeviceCredential',