From 05392f6b28fc38c8bab7fb46ef92d70b17875bad Mon Sep 17 00:00:00 2001 From: Chris Scott Date: Wed, 26 Jun 2019 08:51:23 -0400 Subject: [PATCH] Add Management API support for Branding and Prompts endpoints. --- src/management/BrandingManager.js | 104 +++++++++++++ src/management/PromptsManager.js | 104 +++++++++++++ test/management/branding.tests.js | 236 ++++++++++++++++++++++++++++++ test/management/prompts.tests.js | 222 ++++++++++++++++++++++++++++ 4 files changed, 666 insertions(+) create mode 100644 src/management/BrandingManager.js create mode 100644 src/management/PromptsManager.js create mode 100644 test/management/branding.tests.js create mode 100644 test/management/prompts.tests.js diff --git a/src/management/BrandingManager.js b/src/management/BrandingManager.js new file mode 100644 index 000000000..e743189d9 --- /dev/null +++ b/src/management/BrandingManager.js @@ -0,0 +1,104 @@ +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 BrandingManager + * Manages settings related to branding. + * @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 BrandingManager = function(options) { + if (options === null || typeof options !== 'object') { + throw new ArgumentError('Must provide manager 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'); + } + + var clientOptions = { + errorFormatter: { message: 'message', name: 'error' }, + headers: options.headers, + query: { repeatParams: false } + }; + + /** + * Provides an abstraction layer for consuming the + * {@link https://auth0.com/docs/api/management/v2#!/Branding Branding endpoint}. + * + * @type {external:RestClient} + */ + var auth0RestClient = new Auth0RestClient( + options.baseUrl + '/branding', + clientOptions, + options.tokenProvider + ); + this.resource = new RetryRestClient(auth0RestClient, options.retry); +}; + +/** + * Update the branding settings. + * + * @method updateSettings + * @memberOf module:management.BrandingManager.prototype + * + * @example + * management.branding.updateSettings(data, function (err, branding) { + * if (err) { + * // Handle error. + * } + * + * // Updated branding + * console.log(branding); + * }); + * + * @param {Object} params Branding parameters. + * @param {Object} data Updated branding data. + * @param {Function} [cb] Callback function. + * + * @return {Promise|undefined} + */ +utils.wrapPropertyMethod(BrandingManager, 'updateSettings', 'resource.patch'); + +/** + * Get the branding settings.. + * + * @method getSettings + * @memberOf module:management.BrandingManager.prototype + * + * @example + * management.branding.getSettings(data, function (err, branding) { + * if (err) { + * // Handle error. + * } + * + * // Branding + * console.log(branding); + * }); + * + * @param {Object} params Branding parameters. + * @param {Object} data Branding data. + * @param {Function} [cb] Callback function. + * + * @return {Promise|undefined} + */ +utils.wrapPropertyMethod(BrandingManager, 'getSettings', 'resource.get'); + +module.exports = BrandingManager; diff --git a/src/management/PromptsManager.js b/src/management/PromptsManager.js new file mode 100644 index 000000000..9d2f7aaba --- /dev/null +++ b/src/management/PromptsManager.js @@ -0,0 +1,104 @@ +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 PromptsManager + * Manages settings related to prompts. + * @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 PromptsManager = function(options) { + if (options === null || typeof options !== 'object') { + throw new ArgumentError('Must provide manager 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'); + } + + var clientOptions = { + errorFormatter: { message: 'message', name: 'error' }, + headers: options.headers, + query: { repeatParams: false } + }; + + /** + * Provides an abstraction layer for consuming the + * {@link https://auth0.com/docs/api/management/v2#!/Prompts Prompts endpoint}. + * + * @type {external:RestClient} + */ + var auth0RestClient = new Auth0RestClient( + options.baseUrl + '/prompts', + clientOptions, + options.tokenProvider + ); + this.resource = new RetryRestClient(auth0RestClient, options.retry); +}; + +/** + * Update the prompts settings. + * + * @method updateSettings + * @memberOf module:management.PromptsManager.prototype + * + * @example + * management.prompts.updateSettings(data, function (err, prompts) { + * if (err) { + * // Handle error. + * } + * + * // Updated prompts + * console.log(prompts); + * }); + * + * @param {Object} params Prompts parameters. + * @param {Object} data Updated prompts data. + * @param {Function} [cb] Callback function. + * + * @return {Promise|undefined} + */ +utils.wrapPropertyMethod(PromptsManager, 'updateSettings', 'resource.patch'); + +/** + * Get the prompts settings.. + * + * @method getSettings + * @memberOf module:management.PromptsManager.prototype + * + * @example + * management.prompts.getSettings(data, function (err, prompts) { + * if (err) { + * // Handle error. + * } + * + * // Prompts + * console.log(prompts); + * }); + * + * @param {Object} params Prompts parameters. + * @param {Object} data Prompts data. + * @param {Function} [cb] Callback function. + * + * @return {Promise|undefined} + */ +utils.wrapPropertyMethod(PromptsManager, 'getSettings', 'resource.get'); + +module.exports = PromptsManager; diff --git a/test/management/branding.tests.js b/test/management/branding.tests.js new file mode 100644 index 000000000..10f4f9a16 --- /dev/null +++ b/test/management/branding.tests.js @@ -0,0 +1,236 @@ +var expect = require('chai').expect; +var nock = require('nock'); + +var SRC_DIR = '../../src'; +var API_URL = 'https://tenant.auth0.com'; + +var BrandingManager = require(SRC_DIR + '/management/BrandingManager'); +var ArgumentError = require('rest-facade').ArgumentError; + +describe('BrandingManager', function() { + before(function() { + this.token = 'TOKEN'; + this.branding = new BrandingManager({ + headers: { authorization: 'Bearer ' + this.token }, + baseUrl: API_URL + }); + }); + + describe('instance', function() { + var methods = ['getSettings', 'updateSettings']; + + methods.forEach(function(method) { + it('should have a ' + method + ' method', function() { + expect(this.branding[method]).to.exist.to.be.an.instanceOf(Function); + }); + }); + }); + + describe('#constructor', function() { + it('should error when no options are provided', function() { + expect(BrandingManager).to.throw(ArgumentError, 'Must provide manager options'); + }); + + it('should throw an error when no base URL is provided', function() { + var client = BrandingManager.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 = BrandingManager.bind(null, { baseUrl: '' }); + + expect(client).to.throw(ArgumentError, 'The provided base URL is invalid'); + }); + }); + + describe('#getSettings', function() { + var data = { + colors: { + primary: '#FFF' + }, + favicon_url: 'https://example.com/favicon.ico', + logo_url: 'https://example.com/logo.png', + font: { + url: 'https://example.com/font.ttf' + } + }; + + beforeEach(function() { + this.request = nock(API_URL) + .get('/branding') + .reply(200); + }); + + it('should accept a callback', function(done) { + this.branding.getSettings(function() { + done(); + }); + }); + + it('should return a promise if no callback is given', function(done) { + this.branding + .getSettings() + .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('/branding') + .reply(500); + + this.branding.getSettings().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 request = nock(API_URL) + .get('/branding') + .reply(200, data); + + this.branding.getSettings().then(function(provider) { + expect(provider.id).to.equal(data.id); + + done(); + }); + }); + + it('should perform a GET request to /api/v2/branding', function(done) { + var request = this.request; + + this.branding.getSettings().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('/branding') + .matchHeader('Authorization', 'Bearer ' + this.token) + .reply(200); + + this.branding.getSettings().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('/branding') + .query(params) + .reply(200); + + this.branding.getSettings(params).then(function() { + expect(request.isDone()).to.be.true; + + done(); + }); + }); + }); + + describe('#updateSettings', function() { + var data = { + colors: { + primary: '#FFF' + }, + favicon_url: 'https://example.com/favicon.ico', + logo_url: 'https://example.com/logo.png', + font: { + url: 'https://example.com/font.ttf' + } + }; + + beforeEach(function() { + this.request = nock(API_URL) + .patch('/branding') + .reply(200, data); + }); + + it('should accept a callback', function(done) { + this.branding.updateSettings({}, data, function() { + done(); + }); + }); + + it('should return a promise if no callback is given', function(done) { + this.branding + .updateSettings({}, 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) + .patch('/branding/' + data.id) + .reply(500); + + this.branding.updateSettings({}, data).catch(function(err) { + expect(err).to.exist.to.be.an.instanceOf(Error); + + done(); + }); + }); + + it('should perform a PATCH request to /api/v2/branding', function(done) { + var request = this.request; + + this.branding.updateSettings({}, 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) + .patch('/branding', data) + .reply(200); + + this.branding.updateSettings({}, 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) + .patch('/branding') + .matchHeader('Authorization', 'Bearer ' + this.token) + .reply(200); + + this.branding.updateSettings({}, data).then(function() { + expect(request.isDone()).to.be.true; + + done(); + }); + }); + }); +}); diff --git a/test/management/prompts.tests.js b/test/management/prompts.tests.js new file mode 100644 index 000000000..4df16a881 --- /dev/null +++ b/test/management/prompts.tests.js @@ -0,0 +1,222 @@ +var expect = require('chai').expect; +var nock = require('nock'); + +var SRC_DIR = '../../src'; +var API_URL = 'https://tenant.auth0.com'; + +var PromptsManager = require(SRC_DIR + '/management/PromptsManager'); +var ArgumentError = require('rest-facade').ArgumentError; + +describe('PromptsManager', function() { + before(function() { + this.token = 'TOKEN'; + this.prompts = new PromptsManager({ + headers: { authorization: 'Bearer ' + this.token }, + baseUrl: API_URL + }); + }); + + describe('instance', function() { + var methods = ['getSettings', 'updateSettings']; + + methods.forEach(function(method) { + it('should have a ' + method + ' method', function() { + expect(this.prompts[method]).to.exist.to.be.an.instanceOf(Function); + }); + }); + }); + + describe('#constructor', function() { + it('should error when no options are provided', function() { + expect(PromptsManager).to.throw(ArgumentError, 'Must provide manager options'); + }); + + it('should throw an error when no base URL is provided', function() { + var client = PromptsManager.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 = PromptsManager.bind(null, { baseUrl: '' }); + + expect(client).to.throw(ArgumentError, 'The provided base URL is invalid'); + }); + }); + + describe('#getSettings', function() { + var data = { + universal_login_experience: '' + }; + + beforeEach(function() { + this.request = nock(API_URL) + .get('/prompts') + .reply(200); + }); + + it('should accept a callback', function(done) { + this.prompts.getSettings(function() { + done(); + }); + }); + + it('should return a promise if no callback is given', function(done) { + this.prompts + .getSettings() + .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('/prompts') + .reply(500); + + this.prompts.getSettings().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 request = nock(API_URL) + .get('/prompts') + .reply(200, data); + + this.prompts.getSettings().then(function(provider) { + expect(provider.id).to.equal(data.id); + + done(); + }); + }); + + it('should perform a GET request to /api/v2/prompts', function(done) { + var request = this.request; + + this.prompts.getSettings().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('/prompts') + .matchHeader('Authorization', 'Bearer ' + this.token) + .reply(200); + + this.prompts.getSettings().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('/prompts') + .query(params) + .reply(200); + + this.prompts.getSettings(params).then(function() { + expect(request.isDone()).to.be.true; + + done(); + }); + }); + }); + + describe('#updateSettings', function() { + var data = { + universal_login_experience: 'new' + }; + + beforeEach(function() { + this.request = nock(API_URL) + .patch('/prompts') + .reply(200, data); + }); + + it('should accept a callback', function(done) { + this.prompts.updateSettings({}, data, function() { + done(); + }); + }); + + it('should return a promise if no callback is given', function(done) { + this.prompts + .updateSettings({}, 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) + .patch('/prompts/' + data.id) + .reply(500); + + this.prompts.updateSettings({}, data).catch(function(err) { + expect(err).to.exist.to.be.an.instanceOf(Error); + + done(); + }); + }); + + it('should perform a PATCH request to /api/v2/prompts', function(done) { + var request = this.request; + + this.prompts.updateSettings({}, 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) + .patch('/prompts', data) + .reply(200); + + this.prompts.updateSettings({}, 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) + .patch('/prompts') + .matchHeader('Authorization', 'Bearer ' + this.token) + .reply(200); + + this.prompts.updateSettings({}, data).then(function() { + expect(request.isDone()).to.be.true; + + done(); + }); + }); + }); +});