From 6c65c4e0be4bacb894e84cc7e4755937f35dddf6 Mon Sep 17 00:00:00 2001 From: Luka Skukan Date: Tue, 13 Dec 2016 15:20:25 +0100 Subject: [PATCH 1/7] Add WebConfig resource --- lib/resource/Application.js | 5 +++++ lib/resource/WebConfig.js | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 lib/resource/WebConfig.js diff --git a/lib/resource/Application.js b/lib/resource/Application.js index 43f02038..7c8ca727 100644 --- a/lib/resource/Application.js +++ b/lib/resource/Application.js @@ -1503,4 +1503,9 @@ Application.prototype.getAccountLinkingPolicy = function getApplicationAccountLi return this.dataStore.getResource(this.accountLinkingPolicy.href, args.options, require('./AccountLinkingPolicy'), args.callback); }; +Application.prototype.getWebConfig = function getApplicationWebConfig(/* [options,] callback */) { + var args = utils.resolveArgs(arguments, ['options', 'callback'], true); + return this.dataStore.getResource(this.webConfig.href, args.options, require('./WebConfig'), args.callback); +}; + module.exports = Application; diff --git a/lib/resource/WebConfig.js b/lib/resource/WebConfig.js new file mode 100644 index 00000000..21f2d694 --- /dev/null +++ b/lib/resource/WebConfig.js @@ -0,0 +1,27 @@ +'use strict'; + +var InstanceResource = require('./InstanceResource'); +var utils = require('../utils'); + +function WebConfig() { + WebConfig.super_.apply(this, arguments); +} + +utils.inherits(WebConfig, InstanceResource); + +WebConfig.prototype.getAccount = function getWebConfigAccount(/* [options], callback */) { + var args = utils.resolveArgs(arguments, ['options', 'callback'], true); + return this.dataStore.getResource(this.application.href, args.options, require('./Application'), args.callback); +}; + +WebConfig.prototype.getSigningApiKey = function getSigningApiKey(/* [options], callback */) { + var args = utils.resolveArgs(arguments, ['options', 'callback'], true); + return this.dataStore.getResource(this.signingApiKey.href, args.options, require('./ApiKey'), args.callback); +}; + +WebConfig.prototype.getTenant = function getWebConfigTenant(/* [options], callback */) { + var args = utils.resolveArgs(arguments, ['options', 'callback'], true); + return this.dataStore.getResource(this.tenant.href, args.options, require('./Tenant'), args.callback); +}; + +module.exports = WebConfig; \ No newline at end of file From 4927fe277b469ee069464936300675e41986503d Mon Sep 17 00:00:00 2001 From: Luka Skukan Date: Tue, 13 Dec 2016 16:05:03 +0100 Subject: [PATCH 2/7] Fix naming --- lib/resource/WebConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/resource/WebConfig.js b/lib/resource/WebConfig.js index 21f2d694..150f5538 100644 --- a/lib/resource/WebConfig.js +++ b/lib/resource/WebConfig.js @@ -9,7 +9,7 @@ function WebConfig() { utils.inherits(WebConfig, InstanceResource); -WebConfig.prototype.getAccount = function getWebConfigAccount(/* [options], callback */) { +WebConfig.prototype.getApplication = function getWebConfigApplication(/* [options], callback */) { var args = utils.resolveArgs(arguments, ['options', 'callback'], true); return this.dataStore.getResource(this.application.href, args.options, require('./Application'), args.callback); }; From 13bc77e7f353b478f1a4a518d6834a7d036b5ec5 Mon Sep 17 00:00:00 2001 From: Luka Skukan Date: Tue, 13 Dec 2016 16:05:11 +0100 Subject: [PATCH 3/7] Add unit tests for web config --- test/sp.resource.webConfig_test.js | 258 +++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 test/sp.resource.webConfig_test.js diff --git a/test/sp.resource.webConfig_test.js b/test/sp.resource.webConfig_test.js new file mode 100644 index 00000000..c883d2ee --- /dev/null +++ b/test/sp.resource.webConfig_test.js @@ -0,0 +1,258 @@ +'use strict'; + +var common = require('./common'); +var assert = common.assert; +var sinon = common.sinon; + +var ApiKey = require('../lib/resource/ApiKey'); +var Application = require('../lib/resource/Application'); +var DataStore = require('../lib/ds/DataStore'); +var InstanceResource = require('../lib/resource/InstanceResource'); +var Tenant = require('../lib/resource/Tenant'); +var WebConfig = require('../lib/resource/WebConfig'); + +var webConfigData = { + href: 'https://api.stormpath.com/v1/applicationWebConfigs/14wIJqPVADzqMfjcY7DJ2p', + createdAt: '2016-11-11T04:27:21.688Z', + modifiedAt: '2016-12-13T14:20:02.958Z', + dnsLabel: 'example-application', + domainName: 'example-application.apps.stormpath.io', + status: 'ENABLED', + oauth2: { + enabled: true + }, + register: { + enabled: true + }, + login: { + enabled: true + }, + verifyEmail: { + enabled: true + }, + forgotPassword: { + enabled: null + }, + changePassword: { + enabled: null + }, + me: { + enabled: true, + expand: { + providerData: false, + applications: false, + apiKeys: false, + customData: false, + groups: false, + groupMemberships: false, + directory: false, + tenant: true + } + }, + signingApiKey: { + href: 'https://api.stormpath.com/v1/apiKeys/3NQKZ6CLAE1QJAQFDS9B3WT81' + }, + application: { + href: 'https://api.stormpath.com/v1/applications/2jalCiSk81m0jjFCzQPVb8' + }, + tenant: { + href: 'https://api.stormpath.com/v1/tenants/3BDwQnZM0rtp1VJ0rWE8IC' + } +}; + +describe('WebConfig resource', function() { + var opts; + + before(function() { + opts = {expand: 'something'}; + }); + + describe('constructor', function() { + var superSpy; + var sandbox; + var dataStore; + var webConfig; + + before(function() { + dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); + webConfig = new WebConfig(webConfigData, dataStore); + sandbox = sinon.sandbox.create(); + superSpy = sandbox.spy(WebConfig, 'super_'); + + new WebConfig(webConfigData, dataStore); + }); + + after(function() { + sandbox.restore(); + }); + + it('should call super_ with the same arguments', function() { + /*jshint -W030 */ + superSpy.should.have.been.calledOnce; + superSpy.should.have.been.calledWithExactly(webConfigData, dataStore); + /*jshint +W030 */ + }); + }); + + describe('instantiation and inheritance', function() { + var dataStore; + var webConfig; + + before(function() { + dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); + webConfig = new WebConfig(webConfigData, dataStore); + }); + + it('should inherit from InstanceResource', function() { + assert.instanceOf(webConfig, InstanceResource); + }); + + it('should be an instance of WebConfig', function() { + assert.instanceOf(webConfig, WebConfig); + }); + }); + + describe('#getApplication(options, callback)', function() { + var callback; + var getResourceStub; + var sandbox; + var dataStore; + var webConfig + + before(function() { + dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); + webConfig = new WebConfig(webConfigData, dataStore); + sandbox = sinon.sandbox.create(); + getResourceStub = sinon.stub(dataStore, 'getResource'); + callback = sinon.spy(); + webConfig.getApplication(callback); + webConfig.getApplication(opts, callback); + }); + + after(function() { + sandbox.restore(); + }); + + it('should call dataStore#getResource', function() { + /*jshint -W030 */ + getResourceStub.should.have.been.calledTwice; + /*jshint +W030 */ + }); + + it('should pass the correct href to dataStore#getResource', function() { + getResourceStub.args[0][0].should.equal(webConfigData.application.href); + getResourceStub.args[1][0].should.equal(webConfigData.application.href); + }); + + it('should pass expansion options, if passed, to dataStore#getResource', function() { + assert.isNotOk(getResourceStub.args[0][1]); + getResourceStub.args[1][1].should.equal(opts); + }); + + it('should pass the constructor for Application to dataStore#getResource', function() { + getResourceStub.args[0][2].should.equal(Application); + getResourceStub.args[1][2].should.equal(Application); + }); + + it('should pass the callback to dataStore#getResource', function() { + getResourceStub.args[0][3].should.equal(callback); + getResourceStub.args[1][3].should.equal(callback); + }); + }); + + describe('#getSigningApiKey(options, callback)', function() { + var callback; + var getResourceStub; + var sandbox; + var dataStore; + var webConfig; + + before(function() { + dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); + webConfig = new WebConfig(webConfigData, dataStore); + sandbox = sinon.sandbox.create(); + getResourceStub = sinon.stub(dataStore, 'getResource'); + callback = sinon.spy(); + webConfig.getSigningApiKey(callback); + webConfig.getSigningApiKey(opts, callback); + }); + + after(function() { + sandbox.restore(); + }); + + it('should call dataStore#getResource', function() { + /*jshint -W030 */ + getResourceStub.should.have.been.calledTwice; + /*jshint +W030 */ + }); + + it('should pass the correct href to dataStore#getResource', function() { + getResourceStub.args[0][0].should.equal(webConfigData.signingApiKey.href); + getResourceStub.args[1][0].should.equal(webConfigData.signingApiKey.href); + }); + + it('should pass expansion options, if passed, to dataStore#getResource', function() { + assert.isNotOk(getResourceStub.args[0][1]); + getResourceStub.args[1][1].should.equal(opts); + }); + + it('should pass the constructor for ApiKey to dataStore#getResource', function() { + getResourceStub.args[0][2].should.equal(ApiKey); + getResourceStub.args[1][2].should.equal(ApiKey); + }); + + it('should pass the callback to dataStore#getResource', function() { + getResourceStub.args[0][3].should.equal(callback); + getResourceStub.args[1][3].should.equal(callback); + }); + }); + + describe('#getTenant(options, callback)', function() { + var callback; + var getResourceStub; + var sandbox; + var dataStore; + var webConfig; + + before(function() { + dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); + webConfig = new WebConfig(webConfigData, dataStore); + sandbox = sinon.sandbox.create(); + getResourceStub = sinon.stub(dataStore, 'getResource'); + callback = sinon.spy(); + webConfig.getTenant(callback); + webConfig.getTenant(opts, callback); + }); + + after(function() { + sandbox.restore(); + }); + + it('should call dataStore#getResource', function() { + /*jshint -W030 */ + getResourceStub.should.have.been.calledTwice; + /*jshint +W030 */ + }); + + it('should pass the correct href to dataStore#getResource', function() { + getResourceStub.args[0][0].should.equal(webConfigData.tenant.href); + getResourceStub.args[1][0].should.equal(webConfigData.tenant.href); + }); + + it('should pass expansion options, if passed, to dataStore#getResource', function() { + assert.isNotOk(getResourceStub.args[0][1]); + getResourceStub.args[1][1].should.equal(opts); + }); + + it('should pass the constructor for Tenant to dataStore#getResource', function() { + getResourceStub.args[0][2].should.equal(Tenant); + getResourceStub.args[1][2].should.equal(Tenant); + }); + + it('should pass the callback to dataStore#getResource', function() { + getResourceStub.args[0][3].should.equal(callback); + getResourceStub.args[1][3].should.equal(callback); + }); + }); +}); From c6fecfe7b41fd1bbdcfc4aeabae22d9695b4ead0 Mon Sep 17 00:00:00 2001 From: Luka Skukan Date: Tue, 13 Dec 2016 17:04:51 +0100 Subject: [PATCH 4/7] Fix unit test lint issue --- test/sp.resource.webConfig_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sp.resource.webConfig_test.js b/test/sp.resource.webConfig_test.js index c883d2ee..cccfa261 100644 --- a/test/sp.resource.webConfig_test.js +++ b/test/sp.resource.webConfig_test.js @@ -117,7 +117,7 @@ describe('WebConfig resource', function() { var getResourceStub; var sandbox; var dataStore; - var webConfig + var webConfig; before(function() { dataStore = new DataStore({client: {apiKey: {id: 1, secret: 2}}}); From 151984fef8faadd9fae9e7cea59d732a896ff8f8 Mon Sep 17 00:00:00 2001 From: Luka Skukan Date: Tue, 13 Dec 2016 17:05:07 +0100 Subject: [PATCH 5/7] Add integration tests describing Client API scenarios --- test/it/web_config_it.js | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 test/it/web_config_it.js diff --git a/test/it/web_config_it.js b/test/it/web_config_it.js new file mode 100644 index 00000000..544fe7f6 --- /dev/null +++ b/test/it/web_config_it.js @@ -0,0 +1,97 @@ +'use strict'; + +var common = require('../common'); +var helpers = require('./helpers'); +var assert = common.assert; + +var WebConfig = require('../../lib/resource/WebConfig'); + +describe('WebConfig', function() { + var client; + var app; + var webConfig; + var signingKey; + + before(function(done) { + helpers.getClient(function(_client) { + client = _client; + signingKey = client._dataStore.requestExecutor.options.client.apiKey.secret; + + client.createApplication({ name: helpers.uniqId()}, function(err, _app) { + if (err) { + return done(err); + } + + app = _app; + app.getWebConfig(function(err, _webConfig) { + if (err) { + return done(err); + } + + webConfig = _webConfig; + done(); + }); + }); + }); + }); + + after(function(done) { + app.delete(function(err) { + if (err) { + return done(err); + } + + done(); + }); + }); + + it('should be get-able', function() { + assert.instanceOf(webConfig, WebConfig); + }); + + it('should have the application uri', function() { + assert.isOk(webConfig.dnsLabel); + }); + + it('should be possible to enable it', function(done) { + webConfig.status = 'ENABLED'; + + webConfig.save(function(err, _webConfig) { + if (err) { + return done(err); + } + + assert.equal(_webConfig.status, 'ENABLED'); + done(); + }); + }); + + it('should be possible to disable it', function(done) { + webConfig.status = 'DISABLED'; + + webConfig.save(function(err, _webConfig) { + if (err) { + return done(err); + } + + assert.equal(_webConfig.status, 'DISABLED'); + done(); + }); + }); + + it('should be possible to edit it', function(done) { + var oldRegisterStatus = webConfig.register.enabled; + var newRegisterStatus = !oldRegisterStatus; + + webConfig.register.enabled = newRegisterStatus; + + webConfig.save(function(err, _webConfig) { + if (err) { + return done(err); + } + + assert.equal(_webConfig.register.enabled, newRegisterStatus); + done(); + }); + }); +}); \ No newline at end of file From 0508f2dd1d6339a135f9f7de7bef056147791f79 Mon Sep 17 00:00:00 2001 From: Luka Skukan Date: Wed, 14 Dec 2016 10:31:09 +0100 Subject: [PATCH 6/7] Add docs and update tests to reflect everything correctly --- lib/resource/Application.js | 14 ++++++-- lib/resource/WebConfig.js | 51 ++++++++++++++++++++++++++++ test/sp.resource.application_test.js | 35 +++++++++++++++++++ 3 files changed, 97 insertions(+), 3 deletions(-) diff --git a/lib/resource/Application.js b/lib/resource/Application.js index 7c8ca727..b6ad032e 100644 --- a/lib/resource/Application.js +++ b/lib/resource/Application.js @@ -1503,9 +1503,17 @@ Application.prototype.getAccountLinkingPolicy = function getApplicationAccountLi return this.dataStore.getResource(this.accountLinkingPolicy.href, args.options, require('./AccountLinkingPolicy'), args.callback); }; -Application.prototype.getWebConfig = function getApplicationWebConfig(/* [options,] callback */) { - var args = utils.resolveArgs(arguments, ['options', 'callback'], true); - return this.dataStore.getResource(this.webConfig.href, args.options, require('./WebConfig'), args.callback); +/** + * Retrieves the application's {@link WebConfig}, which determines its behaviour + * regarding the ClientAPI. This resource cannot be expanded. + * + * @param {Function} callback + * The function that will be called when the query is finished, with the parameters + * (err, {@link WebConfig}). + */ +Application.prototype.getWebConfig = function getApplicationWebConfig(/* callback */) { + var args = utils.resolveArgs(arguments, ['callback'], true); + return this.dataStore.getResource(this.webConfig.href, null, require('./WebConfig'), args.callback); }; module.exports = Application; diff --git a/lib/resource/WebConfig.js b/lib/resource/WebConfig.js index 150f5538..b202d3fb 100644 --- a/lib/resource/WebConfig.js +++ b/lib/resource/WebConfig.js @@ -3,22 +3,73 @@ var InstanceResource = require('./InstanceResource'); var utils = require('../utils'); +/** + * @class WebConfig + * + * @augments {InstanceResource} + * + * @description + * + * Encapsulates an WebConfig resource, which determines the behaviour of the + * web application used for the Client API. For full documentation of this + * resource, please see + * [REST API Reference: WebConfig](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#web-config). + * + * This class should not be manually constructed. It should be obtained from one + * of these methods: + * + * - {@link Application#getWebConfig Application.getWebConfig()}. + * + * @param {Object} webConfigResource + * + * The JSON representation of this resource. + */ function WebConfig() { WebConfig.super_.apply(this, arguments); } utils.inherits(WebConfig, InstanceResource); +/** + * Retrieves the {@link Application} that this web configuration is attached to. + * + * @param {ExpansionOptions} options + * Options for retrieving the linked resources of the application. + * + * @param {Function} callback + * The function to call after the query has completed. It will be called with + * (err, {@link Application}). + */ WebConfig.prototype.getApplication = function getWebConfigApplication(/* [options], callback */) { var args = utils.resolveArgs(arguments, ['options', 'callback'], true); return this.dataStore.getResource(this.application.href, args.options, require('./Application'), args.callback); }; +/** + * Retrieves the {@link ApiKey} that this web config is using for signing tokens. + * + * @param {Options} options + * TODO when the official docs describe this + * + * @param {Function} callback + * The function to call after the query has completed. It will be called with + * (err, {@link ApiKey}). + */ WebConfig.prototype.getSigningApiKey = function getSigningApiKey(/* [options], callback */) { var args = utils.resolveArgs(arguments, ['options', 'callback'], true); return this.dataStore.getResource(this.signingApiKey.href, args.options, require('./ApiKey'), args.callback); }; +/** + * Retrieves the {@link Tenant} for this web configuration. + * + * @param {ExpansionOptions} options + * Options for retrieving the linked resources of the tenant. + * + * @param {Function} callback + * The function to call after the query has completed. It will be called with + * (err, {@link Tenant}). + */ WebConfig.prototype.getTenant = function getWebConfigTenant(/* [options], callback */) { var args = utils.resolveArgs(arguments, ['options', 'callback'], true); return this.dataStore.getResource(this.tenant.href, args.options, require('./Tenant'), args.callback); diff --git a/test/sp.resource.application_test.js b/test/sp.resource.application_test.js index 3f7510d5..8c129658 100644 --- a/test/sp.resource.application_test.js +++ b/test/sp.resource.application_test.js @@ -19,6 +19,7 @@ var ApiKey = require('../lib/resource/ApiKey'); var DataStore = require('../lib/ds/DataStore'); var PasswordResetToken = require('../lib/resource/PasswordResetToken'); var AccountLinkingPolicy = require('../lib/resource/AccountLinkingPolicy'); +var WebConfig = require('../lib/resource/WebConfig'); var nJwt = require('njwt'); var nJwtProperties = require('njwt/properties'); var uuid = require('uuid'); @@ -1501,5 +1502,39 @@ describe('Resources: ', function () { }); }); + describe('get web config', function () { + var sandbox, application, getResourceStub, cbSpy, app; + before(function () { + sandbox = sinon.sandbox.create(); + app = { + webConfig: { + href: 'boom!' + } + }; + + application = new Application(app, dataStore); + getResourceStub = sandbox.stub(dataStore, 'getResource', function (href, options, ctor, cb) { + cb(); + }); + cbSpy = sandbox.spy(); + + application.getWebConfig(cbSpy); + }); + + after(function () { + sandbox.restore(); + }); + + it('should get the web config', function () { + /* jshint -W030 */ + getResourceStub.should.have.been.calledOnce; + cbSpy.should.have.been.calledOnce; + /* jshint +W030 */ + + getResourceStub.should.have.been + .calledWith(app.webConfig.href, null, WebConfig, cbSpy); + }); + }); + }); }); From 421cf1a6b2f796c9542520d01d1b34fc9e09dc05 Mon Sep 17 00:00:00 2001 From: Robert Date: Fri, 3 Feb 2017 18:11:24 -0800 Subject: [PATCH 7/7] Docs updates --- lib/resource/Application.js | 2 +- lib/resource/WebConfig.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/resource/Application.js b/lib/resource/Application.js index b6ad032e..3a0128e5 100644 --- a/lib/resource/Application.js +++ b/lib/resource/Application.js @@ -1505,7 +1505,7 @@ Application.prototype.getAccountLinkingPolicy = function getApplicationAccountLi /** * Retrieves the application's {@link WebConfig}, which determines its behaviour - * regarding the ClientAPI. This resource cannot be expanded. + * regarding the Client API. * * @param {Function} callback * The function that will be called when the query is finished, with the parameters diff --git a/lib/resource/WebConfig.js b/lib/resource/WebConfig.js index b202d3fb..99843ef9 100644 --- a/lib/resource/WebConfig.js +++ b/lib/resource/WebConfig.js @@ -10,10 +10,10 @@ var utils = require('../utils'); * * @description * - * Encapsulates an WebConfig resource, which determines the behaviour of the + * Encapsulates a WebConfig resource, which determines the behaviour of the * web application used for the Client API. For full documentation of this * resource, please see - * [REST API Reference: WebConfig](https://docs.stormpath.com/rest/product-guide/latest/reference.html?#web-config). + * [Configuring the Client API](https://docs.stormpath.com/client-api/product-guide/latest/configuration.html). * * This class should not be manually constructed. It should be obtained from one * of these methods: @@ -49,7 +49,7 @@ WebConfig.prototype.getApplication = function getWebConfigApplication(/* [option * Retrieves the {@link ApiKey} that this web config is using for signing tokens. * * @param {Options} options - * TODO when the official docs describe this + * Options for retrieving the linked resources of the API Key. * * @param {Function} callback * The function to call after the query has completed. It will be called with