Skip to content

Commit

Permalink
fix: Fix apps:update and apps:server for requirements-only apps
Browse files Browse the repository at this point in the history
  • Loading branch information
etanxing committed Feb 6, 2023
1 parent 1aef36d commit 72ea302
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 110 deletions.
2 changes: 1 addition & 1 deletion packages/zcli-apps/src/commands/apps/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`))
Expand Down
13 changes: 7 additions & 6 deletions packages/zcli-apps/src/commands/apps/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
18 changes: 18 additions & 0 deletions packages/zcli-apps/src/lib/buildAppJSON.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
11 changes: 8 additions & 3 deletions packages/zcli-apps/src/lib/buildAppJSON.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]) => {
Expand Down Expand Up @@ -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) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/zcli-apps/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export interface Manifest {
author: Author;
defaultLocale: string;
private?: boolean;
location: Location;
location?: Location;
version?: string;
frameworkVersion: string;
singleInstall?: boolean;
Expand All @@ -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;
Expand Down
253 changes: 156 additions & 97 deletions packages/zcli-apps/tests/functional/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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)
})
})
})
})
2 changes: 1 addition & 1 deletion packages/zcli-core/src/lib/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
Expand Down

0 comments on commit 72ea302

Please sign in to comment.