diff --git a/packages/zcli-apps/src/commands/apps/create.ts b/packages/zcli-apps/src/commands/apps/create.ts index 1e9df36d..a76a225e 100644 --- a/packages/zcli-apps/src/commands/apps/create.ts +++ b/packages/zcli-apps/src/commands/apps/create.ts @@ -51,7 +51,7 @@ export default class Create extends Command { const configParams = allConfigs?.parameters || {} // if there are no parameters in the config, just attach an empty object const settings = manifest.parameters ? await getAppSettings(manifest, configParams) : {} - if (!manifest.requirementsOnly) { + if (!manifest.requirementsOnly && manifest.location) { Object.keys(manifest.location).forEach(async product => { if (!createProductInstallation(settings, manifest, app_id, product)) { this.error(chalk.red(`Failed to install ${manifest.name} with app_id: ${app_id}`)) diff --git a/packages/zcli-apps/src/commands/apps/update.ts b/packages/zcli-apps/src/commands/apps/update.ts index 611a3349..f93fab7c 100644 --- a/packages/zcli-apps/src/commands/apps/update.ts +++ b/packages/zcli-apps/src/commands/apps/update.ts @@ -32,12 +32,13 @@ export default class Update extends Command { try { const { app_id }: any = await getUploadJobStatus(job_id, appPath) CliUx.ux.action.stop('Deployed') - - Object.keys(manifest.location).forEach(async product => { - if (!updateProductInstallation(appConfig, manifest, app_id, product)) { - this.error(chalk.red(`Failed to update ${manifest.name} with app_id: ${app_id}`)) - } - }) + if (!manifest.requirementsOnly && manifest.location) { + Object.keys(manifest.location).forEach(async product => { + if (!updateProductInstallation(appConfig, manifest, app_id, product)) { + this.error(chalk.red(`Failed to update ${manifest.name} with app_id: ${app_id}`)) + } + }) + } this.log(chalk.green(`Successfully updated app: ${manifest.name} with app_id: ${app_id}`)) } catch (error) { CliUx.ux.action.stop('Failed') diff --git a/packages/zcli-apps/src/lib/buildAppJSON.test.ts b/packages/zcli-apps/src/lib/buildAppJSON.test.ts index 0bf09603..99b9f78f 100644 --- a/packages/zcli-apps/src/lib/buildAppJSON.test.ts +++ b/packages/zcli-apps/src/lib/buildAppJSON.test.ts @@ -190,6 +190,24 @@ describe('getAppPayloadFromManifest', () => { framework_version: '2.0' }) }) + + it('generate requirements-only app payload from manifest without locaitons', () => { + const manifest = { ...manifestOutput } + manifest.requirementsOnly = true + delete manifest.location + + const appPayload = buildAppJSON.getAppPayloadFromManifest(manifest, 4567, '123', {}) + expect(appPayload).to.deep.equal({ + name: 'app 1', + id: '123', + default_locale: 'en', + asset_url_prefix: 'http://localhost:4567/123/assets/', + version: undefined, + single_install: true, + signed_urls: false, + framework_version: '2.0' + }) + }) }) describe('getLocationIcons', () => { diff --git a/packages/zcli-apps/src/lib/buildAppJSON.ts b/packages/zcli-apps/src/lib/buildAppJSON.ts index 26109bd6..70f26026 100644 --- a/packages/zcli-apps/src/lib/buildAppJSON.ts +++ b/packages/zcli-apps/src/lib/buildAppJSON.ts @@ -40,7 +40,7 @@ export const getIconsByProduct = (product: string, locations: AppLocation, manif return productLocationIcons }, {}) -export const getLocationIcons = (appPath: string, manifestLocations: Location): LocationIcons => { +export const getLocationIcons = (appPath: string, manifestLocations: Location = {}): LocationIcons => { return Object .entries(manifestLocations) .reduce((locationIcons: LocationIcons, [product, locations]) => { @@ -104,17 +104,22 @@ const mergeLocationAndIcons = (locations: Location, locationIcons: LocationIcons } export const getAppPayloadFromManifest = (appManifest: Manifest, port: number, appID: string, locationIcons: LocationIcons): App => { - return { + const appPayload: App = { name: appManifest.name, id: appID, default_locale: appManifest.defaultLocale, - locations: mergeLocationAndIcons(appManifest.location, locationIcons), asset_url_prefix: getAppAssetsURLPrefix(port, appID), single_install: appManifest.singleInstall, signed_urls: appManifest.signedUrls, version: appManifest.version, framework_version: appManifest.frameworkVersion } + + if (!appManifest.requirementsOnly && appManifest.location) { + appPayload.locations = mergeLocationAndIcons(appManifest.location, locationIcons) + } + + return appPayload } const getSettingsArr = (appSettings: any) => { diff --git a/packages/zcli-apps/src/types.ts b/packages/zcli-apps/src/types.ts index 81f75bbe..bb35757f 100644 --- a/packages/zcli-apps/src/types.ts +++ b/packages/zcli-apps/src/types.ts @@ -53,7 +53,7 @@ export interface Manifest { author: Author; defaultLocale: string; private?: boolean; - location: Location; + location?: Location; version?: string; frameworkVersion: string; singleInstall?: boolean; @@ -78,7 +78,7 @@ export interface App { name?: string; default_locale: string; private?: boolean; - locations: Location; + locations?: Location; version?: string; framework_version: string; single_install?: boolean; diff --git a/packages/zcli-apps/tests/functional/create.test.ts b/packages/zcli-apps/tests/functional/create.test.ts index 7212ac9a..820b3e55 100644 --- a/packages/zcli-apps/tests/functional/create.test.ts +++ b/packages/zcli-apps/tests/functional/create.test.ts @@ -6,11 +6,12 @@ import * as appConfig from '../../src/utils/appConfig' import * as requestUtils from '../../../zcli-core/src/lib/requestUtils' import * as packageUtil from '../../src/lib/package' -describe('apps create', function () { +describe('apps', function () { const singleProductApp = path.join(__dirname, 'mocks/single_product_app') const multiProductApp = path.join(__dirname, 'mocks/multi_product_app') const requirementsOnlyApp = path.join(__dirname, 'mocks/requirements_only_app') - const successMessage = 'Successfully installed app' + const successCreateMessage = 'Successfully installed app' + const successUpdateMessage = 'Successfully updated app' const uploadAppPkgStub = sinon.stub(createAppUtils, 'uploadAppPkg') const createAppPkgStub = sinon.stub() @@ -19,103 +20,161 @@ describe('apps create', function () { createAppPkgStub.reset() }) - describe('with multiple apps', () => { - test - .stub(packageUtil, 'createAppPkg', () => createAppPkgStub) - .stub(createAppUtils, 'getManifestAppName', () => 'importantAppName') - .stub(requestUtils, 'getSubdomain', () => Promise.resolve('z3ntest')) - .stub(appConfig, 'setConfig', () => Promise.resolve()) - .env({ - ZENDESK_SUBDOMAIN: 'z3ntest', - ZENDESK_EMAIL: 'admin@z3ntest.com', - ZENDESK_PASSWORD: '123456' // the universal password - }) - .do(() => { - createAppPkgStub.onFirstCall().resolves('thePathLessFrequentlyTravelled') - uploadAppPkgStub.onFirstCall().resolves({ id: 817 }) - uploadAppPkgStub.onSecondCall().resolves({ id: 818 }) - }) - .nock('https://z3ntest.zendesk.com/', api => { - api - .post('/api/apps.json', { upload_id: 817, name: 'Test App 1' }) - .reply(200, { job_id: 127 }) - api - .post('/api/apps.json', { upload_id: 818, name: 'Test App 2' }) - .reply(200, { job_id: 128 }) - api - .get('/api/v2/apps/job_statuses/127') - .reply(200, { status: 'completed', message: 'awesome', app_id: 123456 }) - api - .get('/api/v2/apps/job_statuses/128') - .reply(200, { status: 'completed', message: 'awesome', app_id: 123458 }) - api - .post('/api/support/apps/installations.json', { app_id: '123456', settings: { name: 'Test App 1' } }) - .reply(200) - api - .post('/api/support/apps/installations.json', { app_id: '123458', settings: { name: 'Test App 2', salesForceId: 123 } }) - .reply(200) - }) - .stdout() - .command(['apps:create', singleProductApp, multiProductApp]) - .it('should create said apps', async ctx => { - expect(ctx.stdout).to.contain(successMessage) - }) - }) + describe('create', () => { + describe('with multiple apps', () => { + test + .stub(packageUtil, 'createAppPkg', () => createAppPkgStub) + .stub(createAppUtils, 'getManifestAppName', () => 'importantAppName') + .stub(requestUtils, 'getSubdomain', () => Promise.resolve('z3ntest')) + .stub(appConfig, 'setConfig', () => Promise.resolve()) + .env({ + ZENDESK_SUBDOMAIN: 'z3ntest', + ZENDESK_EMAIL: 'admin@z3ntest.com', + ZENDESK_PASSWORD: '123456' // the universal password + }) + .do(() => { + createAppPkgStub.onFirstCall().resolves('thePathLessFrequentlyTravelled') + uploadAppPkgStub.onFirstCall().resolves({ id: 817 }) + uploadAppPkgStub.onSecondCall().resolves({ id: 818 }) + }) + .nock('https://z3ntest.zendesk.com/', api => { + api + .post('/api/apps.json', { upload_id: 817, name: 'Test App 1' }) + .reply(200, { job_id: 127 }) + api + .post('/api/apps.json', { upload_id: 818, name: 'Test App 2' }) + .reply(200, { job_id: 128 }) + api + .get('/api/v2/apps/job_statuses/127') + .reply(200, { status: 'completed', message: 'awesome', app_id: 123456 }) + api + .get('/api/v2/apps/job_statuses/128') + .reply(200, { status: 'completed', message: 'awesome', app_id: 123458 }) + api + .post('/api/support/apps/installations.json', { app_id: '123456', settings: { name: 'Test App 1' } }) + .reply(200) + api + .post('/api/support/apps/installations.json', { app_id: '123458', settings: { name: 'Test App 2', salesForceId: 123 } }) + .reply(200) + }) + .stdout() + .command(['apps:create', singleProductApp, multiProductApp]) + .it('should create said apps', async ctx => { + expect(ctx.stdout).to.contain(successCreateMessage) + }) + }) + + describe('with single app', () => { + test + .stub(packageUtil, 'createAppPkg', () => createAppPkgStub) + .env({ + ZENDESK_SUBDOMAIN: 'z3ntest', + ZENDESK_EMAIL: 'admin@z3ntest.com', + ZENDESK_PASSWORD: '123456' // the universal password + }) + .do(() => { + createAppPkgStub.onFirstCall().resolves('thePathLessFrequentlyTravelled') + uploadAppPkgStub.onFirstCall().resolves({ id: 819 }) + }) + .nock('https://z3ntest.zendesk.com/', api => { + api + .post('/api/apps.json', { upload_id: 819, name: 'Test App 1' }) + .reply(200, { job_id: 129 }) + api + .get('/api/v2/apps/job_statuses/129') + .reply(200, { status: 'completed', message: 'awesome', app_id: 123456 }) + api + .post('/api/support/apps/installations.json', { app_id: '123456', settings: { name: 'Test App 1' } }) + .reply(200) + }) + .stdout() + .command(['apps:create', singleProductApp]) + .it('should create said apps', async ctx => { + expect(ctx.stdout).to.contain(successCreateMessage) + }) + }) - describe('with single app', () => { - test - .stub(packageUtil, 'createAppPkg', () => createAppPkgStub) - .env({ - ZENDESK_SUBDOMAIN: 'z3ntest', - ZENDESK_EMAIL: 'admin@z3ntest.com', - ZENDESK_PASSWORD: '123456' // the universal password - }) - .do(() => { - createAppPkgStub.onFirstCall().resolves('thePathLessFrequentlyTravelled') - uploadAppPkgStub.onFirstCall().resolves({ id: 819 }) - }) - .nock('https://z3ntest.zendesk.com/', api => { - api - .post('/api/apps.json', { upload_id: 819, name: 'Test App 1' }) - .reply(200, { job_id: 129 }) - api - .get('/api/v2/apps/job_statuses/129') - .reply(200, { status: 'completed', message: 'awesome', app_id: 123456 }) - api - .post('/api/support/apps/installations.json', { app_id: '123456', settings: { name: 'Test App 1' } }) - .reply(200) - }) - .stdout() - .command(['apps:create', singleProductApp]) - .it('should create said apps', async ctx => { - expect(ctx.stdout).to.contain(successMessage) - }) + describe('with requirements-only app', () => { + test + .stub(packageUtil, 'createAppPkg', () => createAppPkgStub) + .env({ + ZENDESK_SUBDOMAIN: 'z3ntest', + ZENDESK_EMAIL: 'admin@z3ntest.com', + ZENDESK_PASSWORD: '123456' // the universal password + }) + .do(() => { + createAppPkgStub.onFirstCall().resolves('thePathLessFrequentlyTravelled') + uploadAppPkgStub.onFirstCall().resolves({ id: 819 }) + }) + .nock('https://z3ntest.zendesk.com/', api => { + api + .post('/api/apps.json', { upload_id: 819, name: 'Test App 1' }) + .reply(200, { job_id: 129 }) + api + .get('/api/v2/apps/job_statuses/129') + .reply(200, { status: 'completed', message: 'awesome', app_id: 123456 }) + }) + .stdout() + .command(['apps:create', requirementsOnlyApp]) + .it('should create said apps', async ctx => { + expect(ctx.stdout).to.contain(successCreateMessage) + }) + }) }) - describe('with requirements-only app', () => { - test - .stub(packageUtil, 'createAppPkg', () => createAppPkgStub) - .env({ - ZENDESK_SUBDOMAIN: 'z3ntest', - ZENDESK_EMAIL: 'admin@z3ntest.com', - ZENDESK_PASSWORD: '123456' // the universal password - }) - .do(() => { - createAppPkgStub.onFirstCall().resolves('thePathLessFrequentlyTravelled') - uploadAppPkgStub.onFirstCall().resolves({ id: 819 }) - }) - .nock('https://z3ntest.zendesk.com/', api => { - api - .post('/api/apps.json', { upload_id: 819, name: 'Test App 1' }) - .reply(200, { job_id: 129 }) - api - .get('/api/v2/apps/job_statuses/129') - .reply(200, { status: 'completed', message: 'awesome', app_id: 123456 }) - }) - .stdout() - .command(['apps:create', requirementsOnlyApp]) - .it('should create said apps', async ctx => { - expect(ctx.stdout).to.contain(successMessage) - }) + describe('update', () => { + describe('with single app', () => { + test + .stub(packageUtil, 'createAppPkg', () => createAppPkgStub) + .env({ + ZENDESK_SUBDOMAIN: 'z3ntest', + ZENDESK_EMAIL: 'admin@z3ntest.com', + ZENDESK_PASSWORD: '123456' // the universal password + }) + .do(() => { + createAppPkgStub.onFirstCall().resolves('thePathLessFrequentlyTravelled') + uploadAppPkgStub.onFirstCall().resolves({ id: 819 }) + }) + .nock('https://z3ntest.zendesk.com/', api => { + api + .put('/api/v2/apps/123456', { upload_id: 819 }) + .reply(200, { job_id: 129 }) + api + .get('/api/v2/apps/job_statuses/129') + .reply(200, { status: 'completed', message: 'awesome', app_id: 123456 }) + }) + .stdout() + .command(['apps:update', singleProductApp]) + .it('should update said apps', async ctx => { + expect(ctx.stdout).to.contain(successUpdateMessage) + }) + }) + + describe('with requirements-only app', () => { + test + .stub(packageUtil, 'createAppPkg', () => createAppPkgStub) + .env({ + ZENDESK_SUBDOMAIN: 'z3ntest', + ZENDESK_EMAIL: 'admin@z3ntest.com', + ZENDESK_PASSWORD: '123456' // the universal password + }) + .do(() => { + createAppPkgStub.onFirstCall().resolves('thePathLessFrequentlyTravelled') + uploadAppPkgStub.onFirstCall().resolves({ id: 819 }) + }) + .nock('https://z3ntest.zendesk.com/', api => { + api + .put('/api/v2/apps/123456', { upload_id: 819 }) + .reply(200, { job_id: 129 }) + api + .get('/api/v2/apps/job_statuses/129') + .reply(200, { status: 'completed', message: 'awesome', app_id: 123456 }) + }) + .stdout() + .command(['apps:update', requirementsOnlyApp]) + .it('should update said apps', async ctx => { + expect(ctx.stdout).to.contain(successUpdateMessage) + }) + }) }) }) diff --git a/packages/zcli-core/src/lib/request.ts b/packages/zcli-core/src/lib/request.ts index a7e34aa9..bbe3411c 100644 --- a/packages/zcli-core/src/lib/request.ts +++ b/packages/zcli-core/src/lib/request.ts @@ -33,7 +33,7 @@ export const requestAPI = async (url: string, options: any = {}, json = false) = if (authToken && subdomain) { return axios.request({ baseURL: `https://${subdomain}.zendesk.com`, - url: url, + url, validateStatus: function (status) { return status < 500 }, ...options })