Remove app purchase/unpurchase
This commit is contained in:
-19
@@ -1474,8 +1474,6 @@ async function install(data, auditSource) {
|
|||||||
if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings);
|
if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings);
|
||||||
if (addError) throw addError;
|
if (addError) throw addError;
|
||||||
|
|
||||||
await purchaseApp({ appId, appstoreId: appStoreId, manifestId: manifest.id || 'customapp' });
|
|
||||||
|
|
||||||
const task = {
|
const task = {
|
||||||
args: { restoreConfig: null, skipDnsSetup, overwriteDns },
|
args: { restoreConfig: null, skipDnsSetup, overwriteDns },
|
||||||
values: { },
|
values: { },
|
||||||
@@ -2376,17 +2374,6 @@ async function exportApp(app, data, auditSource) {
|
|||||||
return { taskId };
|
return { taskId };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function purchaseApp(data) {
|
|
||||||
assert.strictEqual(typeof data, 'object');
|
|
||||||
|
|
||||||
const [purchaseError] = await safe(appstore.purchaseApp(data));
|
|
||||||
if (!purchaseError) return;
|
|
||||||
|
|
||||||
await del(data.appId);
|
|
||||||
|
|
||||||
throw purchaseError;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function clone(app, data, user, auditSource) {
|
async function clone(app, data, user, auditSource) {
|
||||||
assert.strictEqual(typeof app, 'object');
|
assert.strictEqual(typeof app, 'object');
|
||||||
assert.strictEqual(typeof data, 'object');
|
assert.strictEqual(typeof data, 'object');
|
||||||
@@ -2458,8 +2445,6 @@ async function clone(app, data, user, auditSource) {
|
|||||||
if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings);
|
if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings);
|
||||||
if (addError) throw addError;
|
if (addError) throw addError;
|
||||||
|
|
||||||
await purchaseApp({ appId: newAppId, appstoreId: app.appStoreId, manifestId: manifest.id || 'customapp' });
|
|
||||||
|
|
||||||
const restoreConfig = { remotePath: backupInfo.remotePath, backupFormat: backupInfo.format };
|
const restoreConfig = { remotePath: backupInfo.remotePath, backupFormat: backupInfo.format };
|
||||||
const task = {
|
const task = {
|
||||||
args: { restoreConfig, overwriteDns, skipDnsSetup, oldManifest: null },
|
args: { restoreConfig, overwriteDns, skipDnsSetup, oldManifest: null },
|
||||||
@@ -2535,8 +2520,6 @@ async function unarchive(archive, data, auditSource) {
|
|||||||
if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings);
|
if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings);
|
||||||
if (addError) throw addError;
|
if (addError) throw addError;
|
||||||
|
|
||||||
await purchaseApp({ appId, appstoreId: appStoreId, manifestId: manifest.id || 'customapp' });
|
|
||||||
|
|
||||||
const task = {
|
const task = {
|
||||||
args: { restoreConfig, overwriteDns },
|
args: { restoreConfig, overwriteDns },
|
||||||
values: {},
|
values: {},
|
||||||
@@ -2564,8 +2547,6 @@ async function uninstall(app, auditSource) {
|
|||||||
const error = checkAppState(app, exports.ISTATE_PENDING_UNINSTALL);
|
const error = checkAppState(app, exports.ISTATE_PENDING_UNINSTALL);
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
|
|
||||||
await appstore.unpurchaseApp(appId, { appstoreId: app.appStoreId, manifestId: app.manifest.id || 'customapp' });
|
|
||||||
|
|
||||||
const task = {
|
const task = {
|
||||||
args: {},
|
args: {},
|
||||||
values: {},
|
values: {},
|
||||||
|
|||||||
@@ -18,9 +18,6 @@ exports = module.exports = {
|
|||||||
registerCloudronWithLogin,
|
registerCloudronWithLogin,
|
||||||
updateCloudron,
|
updateCloudron,
|
||||||
|
|
||||||
purchaseApp,
|
|
||||||
unpurchaseApp,
|
|
||||||
|
|
||||||
getSubscription,
|
getSubscription,
|
||||||
isFreePlan,
|
isFreePlan,
|
||||||
|
|
||||||
@@ -171,60 +168,6 @@ function isFreePlan(subscription) {
|
|||||||
return !subscription || subscription.plan.id === 'free';
|
return !subscription || subscription.plan.id === 'free';
|
||||||
}
|
}
|
||||||
|
|
||||||
// See app.js install it will create a db record first but remove it again if appstore purchase fails
|
|
||||||
async function purchaseApp(data) {
|
|
||||||
assert.strictEqual(typeof data, 'object'); // { appstoreId, manifestId, appId }
|
|
||||||
assert(data.appstoreId || data.manifestId);
|
|
||||||
assert.strictEqual(typeof data.appId, 'string');
|
|
||||||
|
|
||||||
const token = await settings.get(settings.APPSTORE_API_TOKEN_KEY);
|
|
||||||
if (!token) throw new BoxError(BoxError.LICENSE_ERROR, 'Missing token');
|
|
||||||
|
|
||||||
const [error, response] = await safe(superagent.post(`${await getApiServerOrigin()}/api/v1/cloudronapps`)
|
|
||||||
.send(data)
|
|
||||||
.query({ accessToken: token })
|
|
||||||
.timeout(60 * 1000)
|
|
||||||
.ok(() => true));
|
|
||||||
|
|
||||||
if (error) throw new BoxError(BoxError.NETWORK_ERROR, error);
|
|
||||||
if (response.status === 404) throw new BoxError(BoxError.NOT_FOUND, 'appstoreId does not exist');
|
|
||||||
if (response.status === 401) throw new BoxError(BoxError.INVALID_CREDENTIALS, 'Invalid appstore token');
|
|
||||||
if (response.status === 402) throw new BoxError(BoxError.LICENSE_ERROR, response.body.message);
|
|
||||||
// 200 if already purchased, 201 is newly purchased
|
|
||||||
if (response.status !== 201 && response.status !== 200) throw new BoxError(BoxError.EXTERNAL_ERROR, `App purchase failed. ${response.status} ${JSON.stringify(response.body)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function unpurchaseApp(appId, data) {
|
|
||||||
assert.strictEqual(typeof appId, 'string');
|
|
||||||
assert.strictEqual(typeof data, 'object'); // { appstoreId, manifestId }
|
|
||||||
assert(data.appstoreId || data.manifestId);
|
|
||||||
|
|
||||||
const token = await settings.get(settings.APPSTORE_API_TOKEN_KEY);
|
|
||||||
if (!token) throw new BoxError(BoxError.LICENSE_ERROR, 'Missing token');
|
|
||||||
|
|
||||||
const url = `${await getApiServerOrigin()}/api/v1/cloudronapps/${appId}`;
|
|
||||||
|
|
||||||
let [error, response] = await safe(superagent.get(url)
|
|
||||||
.query({ accessToken: token })
|
|
||||||
.timeout(60 * 1000)
|
|
||||||
.ok(() => true));
|
|
||||||
|
|
||||||
if (error) throw new BoxError(BoxError.NETWORK_ERROR, error);
|
|
||||||
if (response.status === 404) return; // was never purchased
|
|
||||||
if (response.status === 401) throw new BoxError(BoxError.INVALID_CREDENTIALS, 'Invalid appstore token');
|
|
||||||
if (response.status !== 200) throw new BoxError(BoxError.EXTERNAL_ERROR, `App unpurchase failed to get app. status:${response.status}`);
|
|
||||||
|
|
||||||
[error, response] = await safe(superagent.del(url)
|
|
||||||
.send(data)
|
|
||||||
.query({ accessToken: token })
|
|
||||||
.timeout(60 * 1000)
|
|
||||||
.ok(() => true));
|
|
||||||
|
|
||||||
if (error) throw new BoxError(BoxError.NETWORK_ERROR, error);
|
|
||||||
if (response.status === 401) throw new BoxError(BoxError.INVALID_CREDENTIALS, 'Invalid appstore token');
|
|
||||||
if (response.status !== 204) throw new BoxError(BoxError.EXTERNAL_ERROR, `App unpurchase failed. status:${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getBoxUpdate(options) {
|
async function getBoxUpdate(options) {
|
||||||
assert.strictEqual(typeof options, 'object');
|
assert.strictEqual(typeof options, 'object');
|
||||||
|
|
||||||
@@ -327,11 +270,6 @@ async function registerCloudron3(domain, version) {
|
|||||||
await settings.set(settings.APPSTORE_API_TOKEN_KEY, response.body.token);
|
await settings.set(settings.APPSTORE_API_TOKEN_KEY, response.body.token);
|
||||||
|
|
||||||
debug(`registerCloudron: Cloudron registered with id ${response.body.id}`);
|
debug(`registerCloudron: Cloudron registered with id ${response.body.id}`);
|
||||||
|
|
||||||
// // app could already have been installed if we deleted the cloudron.io record and user re-registers
|
|
||||||
// for (const app of await apps.list()) {
|
|
||||||
// await safe(purchaseApp({ appId: app.id, appstoreId: app.appStoreId, manifestId: app.manifest.id || 'customapp' }), { debug });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerCloudron(data) {
|
async function registerCloudron(data) {
|
||||||
@@ -355,11 +293,6 @@ async function registerCloudron(data) {
|
|||||||
await settings.set(settings.APPSTORE_API_TOKEN_KEY, response.body.cloudronToken);
|
await settings.set(settings.APPSTORE_API_TOKEN_KEY, response.body.cloudronToken);
|
||||||
|
|
||||||
debug(`registerCloudron: Cloudron registered with id ${response.body.cloudronId}`);
|
debug(`registerCloudron: Cloudron registered with id ${response.body.cloudronId}`);
|
||||||
|
|
||||||
// app could already have been installed if we deleted the cloudron.io record and user re-registers
|
|
||||||
for (const app of await apps.list()) {
|
|
||||||
await safe(purchaseApp({ appId: app.id, appstoreId: app.appStoreId, manifestId: app.manifest.id || 'customapp' }), { debug });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateCloudron(data) {
|
async function updateCloudron(data) {
|
||||||
|
|||||||
@@ -396,22 +396,8 @@ xdescribe('App API', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('app install fails due to purchase failure', function (done) {
|
it('app install succeeds', async function () {
|
||||||
const fake1 = nock(settings.apiServerOrigin()).get('/api/v1/apps/test').reply(200, { manifest: APP_MANIFEST });
|
|
||||||
|
|
||||||
superagent.post(SERVER_URL + '/api/v1/apps')
|
|
||||||
.query({ access_token: token })
|
|
||||||
.send({ appStoreId: APP_STORE_ID, subdomain: APP_SUBDOMAIN, domain: DOMAIN_0.domain, portBindings: null, accessRestriction: null })
|
|
||||||
.end(function (err, res) {
|
|
||||||
expect(res.status).to.equal(402);
|
|
||||||
expect(fake1.isDone()).to.be.ok();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('app install succeeds with purchase', async function () {
|
|
||||||
const fake1 = nock(settings.apiServerOrigin()).get('/api/v1/apps/' + APP_STORE_ID).reply(200, { manifest: APP_MANIFEST });
|
const fake1 = nock(settings.apiServerOrigin()).get('/api/v1/apps/' + APP_STORE_ID).reply(200, { manifest: APP_MANIFEST });
|
||||||
const fake2 = nock(settings.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, { });
|
|
||||||
|
|
||||||
await settings.setAppstoreApiToken(USER_1_APPSTORE_TOKEN);
|
await settings.setAppstoreApiToken(USER_1_APPSTORE_TOKEN);
|
||||||
|
|
||||||
@@ -423,7 +409,6 @@ xdescribe('App API', function () {
|
|||||||
expect(res.body.id).to.be.a('string');
|
expect(res.body.id).to.be.a('string');
|
||||||
APP_ID = res.body.id;
|
APP_ID = res.body.id;
|
||||||
expect(fake1.isDone()).to.be.ok();
|
expect(fake1.isDone()).to.be.ok();
|
||||||
expect(fake2.isDone()).to.be.ok();
|
|
||||||
taskId = res.body.taskId;
|
taskId = res.body.taskId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
/* jslint node:true */
|
|
||||||
/* global it:false */
|
|
||||||
/* global describe:false */
|
|
||||||
/* global before:false */
|
|
||||||
/* global after:false */
|
|
||||||
/* global beforeEach:false */
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const appstore = require('../appstore.js'),
|
|
||||||
common = require('./common.js'),
|
|
||||||
expect = require('expect.js'),
|
|
||||||
nock = require('nock');
|
|
||||||
|
|
||||||
const APP_ID = 'appid';
|
|
||||||
const APPSTORE_APP_ID = 'appstoreappid';
|
|
||||||
|
|
||||||
describe('Appstore', function () {
|
|
||||||
const { setup, cleanup, appstoreToken, mockApiServerOrigin } = common;
|
|
||||||
|
|
||||||
before(setup);
|
|
||||||
before(() => { if (!nock.isActive()) nock.activate(); });
|
|
||||||
after(cleanup);
|
|
||||||
|
|
||||||
beforeEach(nock.cleanAll);
|
|
||||||
|
|
||||||
it('can purchase an app', async function () {
|
|
||||||
const scope1 = nock(mockApiServerOrigin)
|
|
||||||
.post(`/api/v1/cloudronapps?accessToken=${appstoreToken}`, function () { return true; })
|
|
||||||
.reply(201, {});
|
|
||||||
|
|
||||||
await appstore.purchaseApp({ appId: APP_ID, appstoreId: APPSTORE_APP_ID, manifestId: APPSTORE_APP_ID });
|
|
||||||
expect(scope1.isDone()).to.be.ok();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('unpurchase succeeds if app was never purchased', async function () {
|
|
||||||
const scope1 = nock(mockApiServerOrigin)
|
|
||||||
.get(`/api/v1/cloudronapps/${APP_ID}?accessToken=${appstoreToken}`)
|
|
||||||
.reply(404, {});
|
|
||||||
|
|
||||||
const scope2 = nock(mockApiServerOrigin)
|
|
||||||
.delete(`/api/v1/cloudronapps/${APP_ID}?accessToken=${appstoreToken}`, function () { return true; })
|
|
||||||
.reply(204, {});
|
|
||||||
|
|
||||||
await appstore.unpurchaseApp(APP_ID, { appstoreId: APPSTORE_APP_ID, manifestId: APPSTORE_APP_ID });
|
|
||||||
expect(scope1.isDone()).to.be.ok();
|
|
||||||
expect(scope2.isDone()).to.not.be.ok();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can unpurchase an app', async function () {
|
|
||||||
const scope1 = nock(mockApiServerOrigin)
|
|
||||||
.get(`/api/v1/cloudronapps/${APP_ID}?accessToken=${appstoreToken}`)
|
|
||||||
.reply(200, {});
|
|
||||||
|
|
||||||
const scope2 = nock(mockApiServerOrigin)
|
|
||||||
.delete(`/api/v1/cloudronapps/${APP_ID}?accessToken=${appstoreToken}`, function () { return true; })
|
|
||||||
.reply(204, {});
|
|
||||||
|
|
||||||
await appstore.unpurchaseApp(APP_ID, { appstoreId: APPSTORE_APP_ID, manifestId: APPSTORE_APP_ID });
|
|
||||||
expect(scope1.isDone()).to.be.ok();
|
|
||||||
expect(scope2.isDone()).to.be.ok();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user