diff --git a/src/apps.js b/src/apps.js index 89f2bd00d..562cff2d1 100644 --- a/src/apps.js +++ b/src/apps.js @@ -698,7 +698,7 @@ function install(data, user, auditSource, callback) { if (error && error.reason === DatabaseError.NOT_FOUND) return callback(new AppsError(AppsError.NOT_FOUND, error.message)); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); - appstore.purchase(appId, { appstoreId: appStoreId, manifestId: manifest.id }, function (appstoreError) { + appstore.purchase({ appId: appId, appstoreId: appStoreId, manifestId: manifest.id }, function (appstoreError) { // if purchase failed, rollback the appdb record if (appstoreError) { appdb.del(appId, function (error) { @@ -1135,7 +1135,7 @@ function clone(appId, data, user, auditSource, callback) { if (error && error.reason === DatabaseError.ALREADY_EXISTS) return callback(getDuplicateErrorDetails(error, location, domainObject, portBindings)); if (error) return callback(new AppsError(AppsError.INTERNAL_ERROR, error)); - appstore.purchase(newAppId, { appstoreId: app.appStoreId, manifestId: manifest.id }, function (appstoreError) { + appstore.purchase({ appId: newAppId, appstoreId: app.appStoreId, manifestId: manifest.id }, function (appstoreError) { // if purchase failed, rollback the appdb record if (appstoreError) { appdb.del(newAppId, function (error) { diff --git a/src/appstore.js b/src/appstore.js index 11a904a37..774aca225 100644 --- a/src/appstore.js +++ b/src/appstore.js @@ -106,18 +106,18 @@ function isFreePlan(subscription) { } // See app.js install it will create a db record first but remove it again if appstore purchase fails -function purchase(appId, data, callback) { - assert.strictEqual(typeof appId, 'string'); - assert.strictEqual(typeof data, 'object'); // { appstoreId, manifestId } +function purchase(data, callback) { + assert.strictEqual(typeof data, 'object'); // { appstoreId, manifestId, appId } assert(data.appstoreId || data.manifestId); + assert.strictEqual(typeof data.appId, 'string'); assert.strictEqual(typeof callback, 'function'); - getAppstoreConfig(function (error, appstoreConfig) { + getAppstoreToken(function (error, token) { if (error) return callback(error); - var url = config.apiServerOrigin() + '/api/v1/users/' + appstoreConfig.userId + '/cloudrons/' + appstoreConfig.cloudronId + '/apps/' + appId; + const url = `${config.apiServerOrigin()}/api/v1/cloudronapps`; - superagent.post(url).send(data).query({ accessToken: appstoreConfig.token }).timeout(30 * 1000).end(function (error, result) { + superagent.post(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) { if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); if (result.statusCode === 404) return callback(new AppstoreError(AppstoreError.NOT_FOUND)); if (result.statusCode === 403 || result.statusCode === 401) return callback(new AppstoreError(AppstoreError.BILLING_REQUIRED)); @@ -135,18 +135,18 @@ function unpurchase(appId, data, callback) { assert(data.appstoreId || data.manifestId); assert.strictEqual(typeof callback, 'function'); - getAppstoreConfig(function (error, appstoreConfig) { + getAppstoreToken(function (error, token) { if (error) return callback(error); - var url = config.apiServerOrigin() + '/api/v1/users/' + appstoreConfig.userId + '/cloudrons/' + appstoreConfig.cloudronId + '/apps/' + appId; + const url = `${config.apiServerOrigin()}/api/v1/cloudronapps/${appId}`; - superagent.get(url).query({ accessToken: appstoreConfig.token }).timeout(30 * 1000).end(function (error, result) { - if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error)); + superagent.get(url).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) { + if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error.message)); if (result.statusCode === 403 || result.statusCode === 401) return callback(new AppstoreError(AppstoreError.BILLING_REQUIRED)); if (result.statusCode === 404) return callback(null); // was never purchased if (result.statusCode !== 201 && result.statusCode !== 200) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body))); - superagent.del(url).send(data).query({ accessToken: appstoreConfig.token }).timeout(30 * 1000).end(function (error, result) { + superagent.del(url).send(data).query({ accessToken: token }).timeout(30 * 1000).end(function (error, result) { if (error && !error.response) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, error)); if (result.statusCode === 403 || result.statusCode === 401) return callback(new AppstoreError(AppstoreError.BILLING_REQUIRED)); if (result.statusCode !== 204) return callback(new AppstoreError(AppstoreError.EXTERNAL_ERROR, util.format('App unpurchase failed. %s %j', result.status, result.body))); diff --git a/src/routes/test/apps-test.js b/src/routes/test/apps-test.js index e0c438276..d29b10ba2 100644 --- a/src/routes/test/apps-test.js +++ b/src/routes/test/apps-test.js @@ -4,6 +4,7 @@ /* global describe:false */ /* global before:false */ /* global after:false */ +/* global xit:false */ var appdb = require('../../appdb.js'), apps = require('../../apps.js'), @@ -30,6 +31,7 @@ var appdb = require('../../appdb.js'), safe = require('safetydance'), server = require('../../server.js'), settings = require('../../settings.js'), + settingsdb = require('../../settingsdb.js'), superagent = require('superagent'), taskmanager = require('../../taskmanager.js'), tokendb = require('../../tokendb.js'), @@ -464,24 +466,28 @@ describe('App API', function () { it('app install succeeds with purchase', function (done) { var fake1 = nock(config.apiServerOrigin()).post(function (uri) { return uri.indexOf('/api/v1/users/' + user_1_id + '/cloudrons') >= 0; }, { 'domain': DOMAIN_0.domain }).reply(201, { cloudron: { id: CLOUDRON_ID } }); var fake2 = nock(config.apiServerOrigin()).get('/api/v1/apps/' + APP_STORE_ID).reply(200, { manifest: APP_MANIFEST }); - var fake3 = nock(config.apiServerOrigin()).post(function (uri) { return uri.indexOf('/api/v1/users/' + user_1_id + '/cloudrons/' + CLOUDRON_ID + '/apps/') >= 0; }, { 'appstoreId': APP_STORE_ID, 'manifestId': APP_MANIFEST.id }).reply(201, { }); + var fake3 = nock(config.apiServerOrigin()).post(function (uri) { return uri.indexOf('/api/v1/cloudronapps') >= 0; }, (body) => body.appstoreId === APP_STORE_ID && body.manifestId === APP_MANIFEST.id && body.appId).reply(201, { }); settings.setAppstoreConfig({ userId: user_1_id, token: USER_1_APPSTORE_TOKEN }, function (error) { if (error) return done(error); - expect(fake1.isDone()).to.be.ok(); + settingsdb.set(settings.APPSTORE_TOKEN_KEY, USER_1_APPSTORE_TOKEN, function (error) { + if (error) return done(error); - superagent.post(SERVER_URL + '/api/v1/apps/install') - .query({ access_token: token }) - .send({ appStoreId: APP_STORE_ID, location: APP_LOCATION, domain: DOMAIN_0.domain, portBindings: null, accessRestriction: { users: [ 'someuser' ], groups: [] } }) - .end(function (err, res) { - expect(res.statusCode).to.equal(202); - expect(res.body.id).to.be.a('string'); - APP_ID = res.body.id; - expect(fake2.isDone()).to.be.ok(); - expect(fake3.isDone()).to.be.ok(); - done(); - }); + expect(fake1.isDone()).to.be.ok(); + + superagent.post(SERVER_URL + '/api/v1/apps/install') + .query({ access_token: token }) + .send({ appStoreId: APP_STORE_ID, location: APP_LOCATION, domain: DOMAIN_0.domain, portBindings: null, accessRestriction: { users: [ 'someuser' ], groups: [] } }) + .end(function (err, res) { + expect(res.statusCode).to.equal(202); + expect(res.body.id).to.be.a('string'); + APP_ID = res.body.id; + expect(fake2.isDone()).to.be.ok(); + expect(fake3.isDone()).to.be.ok(); + done(); + }); + }); }); }); @@ -578,8 +584,8 @@ describe('App API', function () { }); it('can uninstall app', function (done) { - var fake1 = nock(config.apiServerOrigin()).get(function (uri) { return uri.indexOf('/api/v1/users/' + user_1_id + '/cloudrons/' + CLOUDRON_ID + '/apps/') >= 0; }).reply(200, { }); - var fake2 = nock(config.apiServerOrigin()).delete(function (uri) { return uri.indexOf('/api/v1/users/' + user_1_id + '/cloudrons/' + CLOUDRON_ID + '/apps/') >= 0; }).reply(204, { }); + var fake1 = nock(config.apiServerOrigin()).get(function (uri) { return uri.indexOf('/api/v1/cloudronapps/') >= 0; }).reply(200, { }); + var fake2 = nock(config.apiServerOrigin()).delete(function (uri) { return uri.indexOf('/api/v1/cloudronapps/') >= 0; }).reply(204, { }); superagent.post(SERVER_URL + '/api/v1/apps/' + APP_ID + '/uninstall') .send({ password: PASSWORD }) @@ -594,7 +600,7 @@ describe('App API', function () { it('app install succeeds again', function (done) { var fake1 = nock(config.apiServerOrigin()).get('/api/v1/apps/' + APP_STORE_ID).reply(200, { manifest: APP_MANIFEST }); - var fake2 = nock(config.apiServerOrigin()).post(function (uri) { return uri.indexOf('/api/v1/users/' + user_1_id + '/cloudrons/' + CLOUDRON_ID + '/apps/') >= 0; }, { 'appstoreId': APP_STORE_ID, 'manifestId': APP_MANIFEST.id }).reply(201, { }); + var fake2 = nock(config.apiServerOrigin()).post(function (uri) { return uri.indexOf('/api/v1/cloudronapps') >= 0; }, (body) => body.appstoreId === APP_STORE_ID && body.manifestId === APP_MANIFEST.id && body.appId).reply(201, { }); superagent.post(SERVER_URL + '/api/v1/apps/install') .query({ access_token: token }) @@ -660,9 +666,13 @@ describe('App installation', function () { settings.setAppstoreConfig({ userId: user_1_id, token: USER_1_APPSTORE_TOKEN }, function (error) { if (error) return callback(error); - expect(fake1.isDone()).to.be.ok(); + settingsdb.set(settings.APPSTORE_TOKEN_KEY, USER_1_APPSTORE_TOKEN, function (error) { + if (error) return callback(error); - callback(); + expect(fake1.isDone()).to.be.ok(); + + callback(); + }); }); } ], done); @@ -674,7 +684,7 @@ describe('App installation', function () { it('can install test app', function (done) { var fake1 = nock(config.apiServerOrigin()).get('/api/v1/apps/' + APP_STORE_ID).reply(200, { manifest: APP_MANIFEST }); - var fake2 = nock(config.apiServerOrigin()).post(function (uri) { return uri.indexOf('/api/v1/users/' + user_1_id + '/cloudrons/' + CLOUDRON_ID + '/apps/') >= 0; }, { 'appstoreId': APP_STORE_ID, 'manifestId': APP_MANIFEST.id }).reply(201, { }); + var fake2 = nock(config.apiServerOrigin()).post(function (uri) { return uri.indexOf('/api/v1/cloudronapps') >= 0; }, (body) => body.appstoreId === APP_STORE_ID && body.manifestId === APP_MANIFEST.id && body.appId).reply(201, { }); var count = 0; function checkInstallStatus() { @@ -913,7 +923,7 @@ describe('App installation', function () { it('did stop the app', function (done) { function waitForAppToDie() { - superagent.get('http://localhost:' + appEntry.httpPort + appResult.manifest.healthCheckPath).end(function (err, res) { + superagent.get('http://localhost:' + appEntry.httpPort + appResult.manifest.healthCheckPath).end(function (err) { if (!err || err.code !== 'ECONNREFUSED') return setTimeout(waitForAppToDie, 500); // wait for app status to be updated @@ -1135,8 +1145,8 @@ describe('App installation', function () { }); it('can uninstall app', function (done) { - var fake1 = nock(config.apiServerOrigin()).get(function (uri) { return uri.indexOf('/api/v1/users/' + user_1_id + '/cloudrons/' + CLOUDRON_ID + '/apps/') >= 0; }).reply(200, { }); - var fake2 = nock(config.apiServerOrigin()).delete(function (uri) { return uri.indexOf('/api/v1/users/' + user_1_id + '/cloudrons/' + CLOUDRON_ID + '/apps/') >= 0; }).reply(204, { }); + var fake1 = nock(config.apiServerOrigin()).get(function (uri) { return uri.indexOf('/api/v1/cloudronapps/') >= 0; }).reply(200, { }); + var fake2 = nock(config.apiServerOrigin()).delete(function (uri) { return uri.indexOf('/api/v1/cloudronapps/') >= 0; }).reply(204, { }); var count = 0; function checkUninstallStatus() { @@ -1195,7 +1205,7 @@ describe('App installation', function () { }); it('uninstalled - removed redis addon', function (done) { - docker.getContainer('redis-' + APP_ID).inspect(function (error, data) { + docker.getContainer('redis-' + APP_ID).inspect(function (error) { expect(error).to.be.ok(); done(); }); diff --git a/src/test/appstore-test.js b/src/test/appstore-test.js index bcbac79bc..a66dff7ce 100644 --- a/src/test/appstore-test.js +++ b/src/test/appstore-test.js @@ -112,10 +112,10 @@ describe('Appstore', function () { it('can purchase an app', function (done) { var scope1 = nock('http://localhost:6060') - .post(`/api/v1/users/${APPSTORE_USER_ID}/cloudrons/${CLOUDRON_ID}/apps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) + .post(`/api/v1/cloudronapps?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) .reply(201, {}); - appstore.purchase(APP_ID, { appstoreId: APPSTORE_APP_ID, manifestId: APPSTORE_APP_ID }, function (error) { + appstore.purchase({ appId: APP_ID, appstoreId: APPSTORE_APP_ID, manifestId: APPSTORE_APP_ID }, function (error) { expect(error).to.not.be.ok(); expect(scope1.isDone()).to.be.ok(); @@ -125,11 +125,11 @@ describe('Appstore', function () { it('unpurchase succeeds if app was never purchased', function (done) { var scope1 = nock('http://localhost:6060') - .get(`/api/v1/users/${APPSTORE_USER_ID}/cloudrons/${CLOUDRON_ID}/apps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`) + .get(`/api/v1/cloudronapps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`) .reply(404, {}); var scope2 = nock('http://localhost:6060') - .delete(`/api/v1/users/${APPSTORE_USER_ID}/cloudrons/${CLOUDRON_ID}/apps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) + .delete(`/api/v1/cloudronapps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) .reply(204, {}); appstore.unpurchase(APP_ID, { appstoreId: APPSTORE_APP_ID, manifestId: APPSTORE_APP_ID }, function (error) { @@ -143,11 +143,11 @@ describe('Appstore', function () { it('can unpurchase an app', function (done) { var scope1 = nock('http://localhost:6060') - .get(`/api/v1/users/${APPSTORE_USER_ID}/cloudrons/${CLOUDRON_ID}/apps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`) + .get(`/api/v1/cloudronapps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`) .reply(200, {}); var scope2 = nock('http://localhost:6060') - .delete(`/api/v1/users/${APPSTORE_USER_ID}/cloudrons/${CLOUDRON_ID}/apps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) + .delete(`/api/v1/cloudronapps/${APP_ID}?accessToken=${APPSTORE_TOKEN}`, function () { return true; }) .reply(204, {}); appstore.unpurchase(APP_ID, { appstoreId: APPSTORE_APP_ID, manifestId: APPSTORE_APP_ID }, function (error) {