diff --git a/README.md b/README.md index d978a845..d17df80e 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ DESCRIPTION Create, run, test, and deploy Adobe I/O Apps ``` -_See code: [src/commands/app/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/index.js)_ +_See code: [src/commands/app/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/index.js)_ ## `aio app add` @@ -90,7 +90,7 @@ DESCRIPTION Add a new component to an existing Adobe I/O App ``` -_See code: [src/commands/app/add/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/add/index.js)_ +_See code: [src/commands/app/add/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/add/index.js)_ ## `aio app add action` @@ -115,7 +115,7 @@ ALIASES $ aio app add actions ``` -_See code: [src/commands/app/add/action.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/add/action.js)_ +_See code: [src/commands/app/add/action.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/add/action.js)_ ## `aio app add ci` @@ -133,7 +133,7 @@ DESCRIPTION Add CI files ``` -_See code: [src/commands/app/add/ci.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/add/ci.js)_ +_See code: [src/commands/app/add/ci.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/add/ci.js)_ ## `aio app add event` @@ -158,7 +158,7 @@ ALIASES $ aio app add events ``` -_See code: [src/commands/app/add/event.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/add/event.js)_ +_See code: [src/commands/app/add/event.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/add/event.js)_ ## `aio app add extension` @@ -184,7 +184,7 @@ ALIASES $ aio app add extensions ``` -_See code: [src/commands/app/add/extension.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/add/extension.js)_ +_See code: [src/commands/app/add/extension.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/add/extension.js)_ ## `aio app add service` @@ -208,7 +208,7 @@ ALIASES $ aio app add services ``` -_See code: [src/commands/app/add/service.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/add/service.js)_ +_See code: [src/commands/app/add/service.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/add/service.js)_ ## `aio app add web-assets` @@ -229,7 +229,7 @@ DESCRIPTION Add web assets support ``` -_See code: [src/commands/app/add/web-assets.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/add/web-assets.js)_ +_See code: [src/commands/app/add/web-assets.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/add/web-assets.js)_ ## `aio app build` @@ -259,7 +259,7 @@ DESCRIPTION Use the --force-build flag to force a build even if one already exists. ``` -_See code: [src/commands/app/build.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/build.js)_ +_See code: [src/commands/app/build.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/build.js)_ ## `aio app create [PATH]` @@ -281,7 +281,7 @@ DESCRIPTION Create a new Adobe I/O App with default parameters ``` -_See code: [src/commands/app/create.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/create.js)_ +_See code: [src/commands/app/create.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/create.js)_ ## `aio app delete` @@ -299,7 +299,7 @@ DESCRIPTION Delete a component from an existing Adobe I/O App ``` -_See code: [src/commands/app/delete/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/delete/index.js)_ +_See code: [src/commands/app/delete/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/delete/index.js)_ ## `aio app delete action [ACTION-NAME]` @@ -325,7 +325,7 @@ ALIASES $ aio app delete actions ``` -_See code: [src/commands/app/delete/action.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/delete/action.js)_ +_See code: [src/commands/app/delete/action.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/delete/action.js)_ ## `aio app delete ci` @@ -344,7 +344,7 @@ DESCRIPTION Delete existing CI files ``` -_See code: [src/commands/app/delete/ci.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/delete/ci.js)_ +_See code: [src/commands/app/delete/ci.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/delete/ci.js)_ ## `aio app delete extension` @@ -370,7 +370,7 @@ ALIASES $ aio app delete extensions ``` -_See code: [src/commands/app/delete/extension.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/delete/extension.js)_ +_See code: [src/commands/app/delete/extension.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/delete/extension.js)_ ## `aio app delete service` @@ -394,7 +394,7 @@ ALIASES $ aio app delete services ``` -_See code: [src/commands/app/delete/service.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/delete/service.js)_ +_See code: [src/commands/app/delete/service.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/delete/service.js)_ ## `aio app delete web-assets` @@ -413,7 +413,7 @@ DESCRIPTION Delete existing web assets ``` -_See code: [src/commands/app/delete/web-assets.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/delete/web-assets.js)_ +_See code: [src/commands/app/delete/web-assets.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/delete/web-assets.js)_ ## `aio app deploy` @@ -459,7 +459,7 @@ DESCRIPTION Use the --force-deploy flag to force deploy changes, regardless of production Workspace being published in Exchange. ``` -_See code: [src/commands/app/deploy.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/deploy.js)_ +_See code: [src/commands/app/deploy.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/deploy.js)_ ## `aio app get-url [ACTION]` @@ -482,7 +482,7 @@ DESCRIPTION Get action URLs ``` -_See code: [src/commands/app/get-url.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/get-url.js)_ +_See code: [src/commands/app/get-url.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/get-url.js)_ ## `aio app info` @@ -504,7 +504,7 @@ DESCRIPTION Display settings/configuration in use by an Adobe I/O App ``` -_See code: [src/commands/app/info.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/info.js)_ +_See code: [src/commands/app/info.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/info.js)_ ## `aio app init [PATH]` @@ -543,7 +543,7 @@ DESCRIPTION Create a new Adobe I/O App ``` -_See code: [src/commands/app/init.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/init.js)_ +_See code: [src/commands/app/init.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/init.js)_ ## `aio app install PATH` @@ -566,7 +566,7 @@ DESCRIPTION This command will support installing apps packaged by 'aio app pack'. ``` -_See code: [src/commands/app/install.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/install.js)_ +_See code: [src/commands/app/install.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/install.js)_ ## `aio app list` @@ -584,7 +584,7 @@ DESCRIPTION List components for Adobe I/O App ``` -_See code: [src/commands/app/list/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/list/index.js)_ +_See code: [src/commands/app/list/index.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/list/index.js)_ ## `aio app list extension` @@ -609,7 +609,7 @@ ALIASES $ aio app list extensions ``` -_See code: [src/commands/app/list/extension.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/list/extension.js)_ +_See code: [src/commands/app/list/extension.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/list/extension.js)_ ## `aio app logs` @@ -633,7 +633,7 @@ DESCRIPTION Fetch logs for an Adobe I/O App ``` -_See code: [src/commands/app/logs.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/logs.js)_ +_See code: [src/commands/app/logs.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/logs.js)_ ## `aio app pack [PATH]` @@ -655,7 +655,7 @@ DESCRIPTION This command will support packaging apps for redistribution. ``` -_See code: [src/commands/app/pack.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/pack.js)_ +_See code: [src/commands/app/pack.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/pack.js)_ ## `aio app run` @@ -679,7 +679,7 @@ DESCRIPTION Run an Adobe I/O App ``` -_See code: [src/commands/app/run.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/run.js)_ +_See code: [src/commands/app/run.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/run.js)_ ## `aio app test` @@ -707,7 +707,7 @@ DESCRIPTION If the extension has a hook called 'test' in its 'ext.config.yaml', the script specified will be run instead. ``` -_See code: [src/commands/app/test.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/test.js)_ +_See code: [src/commands/app/test.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/test.js)_ ## `aio app undeploy` @@ -732,7 +732,7 @@ DESCRIPTION Undeploys an Adobe I/O App ``` -_See code: [src/commands/app/undeploy.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/undeploy.js)_ +_See code: [src/commands/app/undeploy.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/undeploy.js)_ ## `aio app use [CONFIG_FILE_PATH]` @@ -779,5 +779,5 @@ DESCRIPTION page in https://developer.adobe.com/console/ ``` -_See code: [src/commands/app/use.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.0/src/commands/app/use.js)_ +_See code: [src/commands/app/use.js](https://github.com/adobe/aio-cli-plugin-app/blob/13.1.1/src/commands/app/use.js)_ diff --git a/package.json b/package.json index 8fcb9dfb..5454916d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@adobe/aio-cli-plugin-app", "description": "Create, Build and Deploy Adobe I/O Applications", - "version": "13.1.0", + "version": "13.1.1", "author": "Adobe Inc.", "bugs": "https://github.com/adobe/aio-cli-plugin-app/issues", "dependencies": { diff --git a/src/commands/app/deploy.js b/src/commands/app/deploy.js index ba3333bf..c9ee40b3 100644 --- a/src/commands/app/deploy.js +++ b/src/commands/app/deploy.js @@ -43,7 +43,6 @@ class Deploy extends BuildCommand { // if there are no extensions, then set publish to false flags.publish = flags.publish && !isStandaloneApp - if ( (!flags.publish && !flags['web-assets'] && !flags.actions) ) { @@ -53,7 +52,7 @@ class Deploy extends BuildCommand { try { const aioConfig = (await this.getFullConfig()).aio - const cliDetails = await getCliInfo() + const cliDetails = await getCliInfo(flags.publish) // 1. update log forwarding configuration // note: it is possible that .aio file does not exist, which means there is no local lg config @@ -65,7 +64,7 @@ class Deploy extends BuildCommand { const lfConfig = lf.getLocalConfigWithSecrets() if (lfConfig.isDefined()) { await lf.updateServerConfig(lfConfig) - spinner.succeed(chalk.green(`Log forwarding is set to '${lfConfig.getDestination()}'`)) + spinner.succeed(chalk.green(`\nLog forwarding is set to '${lfConfig.getDestination()}'`)) } else { if (flags.verbose) { spinner.info(chalk.dim('Log forwarding is not updated: no configuration is provided')) @@ -95,12 +94,21 @@ class Deploy extends BuildCommand { } // 3. send deploy log event - const logEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_DEPLOY') - if (logEvent) { - await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env) - } else { - this.log(chalk.red(chalk.bold('Warning: No valid config data found to send audit log event for deployment.'))) - } + + // TODO: We will need this code running when Once Runtime events start showing up, + // we'll decide on the fate of the 'Starting deployment/undeplpyment' messages. + // JIRA: https://jira.corp.adobe.com/browse/ACNA-3240 + + // const logEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_DEPLOY') + // if (logEvent && cliDetails?.accessToken) { + // try { + // await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env) + // } catch (error) { + // this.log(chalk.red(chalk.bold('Error: Audit Log Service Error: Failed to send audit log event for deployment.'))) + // } + // } else { + // this.log(chalk.red(chalk.bold('Warning: No valid config data found to send audit log event for deployment.'))) + // } // 4. deploy actions and web assets for each extension // Possible improvements: @@ -113,9 +121,14 @@ class Deploy extends BuildCommand { if (v.app.hasFrontend && flags['web-assets']) { const opItems = getFilesCountWithExtension(v.web.distProd) const assetDeployedLogEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_ASSETS_DEPLOYED') - if (assetDeployedLogEvent) { + if (assetDeployedLogEvent && cliDetails?.accessToken) { assetDeployedLogEvent.data.opItems = opItems - await sendAuditLogs(cliDetails.accessToken, assetDeployedLogEvent, cliDetails.env) + try { + // only send logs in case of web-assets deployment + await sendAuditLogs(cliDetails.accessToken, assetDeployedLogEvent, cliDetails.env) + } catch (error) { + this.warn('Error: Audit Log Service Error: Failed to send audit log event for deployment.') + } } } } diff --git a/src/commands/app/undeploy.js b/src/commands/app/undeploy.js index f11d25ce..bf0503b2 100644 --- a/src/commands/app/undeploy.js +++ b/src/commands/app/undeploy.js @@ -27,16 +27,21 @@ class Undeploy extends BaseCommand { const { flags } = await this.parse(Undeploy) const undeployConfigs = await this.getAppExtConfigs(flags) + + // 1. undeploy actions and web assets for each extension + const keys = Object.keys(undeployConfigs) + const values = Object.values(undeployConfigs) + + // if it is standalone app, unpublish it without token + const isStandaloneApp = (keys.length === 1 && keys[0] === 'application') + flags.unpublish = flags.unpublish && !isStandaloneApp + let libConsoleCLI if (flags.unpublish) { // force login at beginning (if required) libConsoleCLI = await this.getLibConsoleCLI() } - // 1. undeploy actions and web assets for each extension - const keys = Object.keys(undeployConfigs) - const values = Object.values(undeployConfigs) - if ( (!flags.unpublish && !flags['web-assets'] && !flags.actions) ) { @@ -46,28 +51,42 @@ class Undeploy extends BaseCommand { const spinner = ora() try { const aioConfig = (await this.getFullConfig()).aio - const cliDetails = await getCliInfo() - const logEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_UNDEPLOY') + const cliDetails = await getCliInfo(flags.unpublish) - // 1.1. send audit log event for successful undeploy - if (logEvent) { - await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env) - } else { - this.log(chalk.red(chalk.bold('Warning: No valid config data found to send audit log event for deployment.'))) - } + // TODO: We will need this code running when Once Runtime events start showing up, + // we'll decide on the fate of the 'Starting deployment/undeplpyment' messages. + // JIRA: https://jira.corp.adobe.com/browse/ACNA-3240 + + // const logEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_UNDEPLOY') + + // // 1.1. send audit log event for successful undeploy + // if (logEvent && cliDetails.accessToken) { + // try { + // await sendAuditLogs(cliDetails.accessToken, logEvent, cliDetails.env) + // } catch (error) { + // this.warn('Warning: Audit Log Service Error: Failed to send audit log event for un-deployment.') + // } + // } else { + // this.warn('Warning: No valid config data found to send audit log event for un-deployment.') + // } for (let i = 0; i < keys.length; ++i) { const k = keys[i] const v = values[i] await this.undeployOneExt(k, v, flags, spinner) const assetUndeployLogEvent = getAuditLogEvent(flags, aioConfig.project, 'AB_APP_ASSETS_UNDEPLOYED') - if (assetUndeployLogEvent) { - await sendAuditLogs(cliDetails.accessToken, assetUndeployLogEvent, cliDetails.env) + // send logs for case of web-assets undeployment + if (assetUndeployLogEvent && cliDetails?.accessToken) { + try { + await sendAuditLogs(cliDetails.accessToken, assetUndeployLogEvent, cliDetails.env) + } catch (error) { + this.warn('Warning: Audit Log Service Error: Failed to send audit log event for un-deployment.') + } } } // 1.2. unpublish extension manifest - if (flags.unpublish && !(keys.length === 1 && keys[0] === 'application')) { + if (flags.unpublish) { const payload = await this.unpublishExtensionPoints(libConsoleCLI, undeployConfigs, aioConfig, flags['force-unpublish']) this.log(chalk.blue(chalk.bold(`New Extension Point(s) in Workspace '${aioConfig.project.workspace.name}': '${Object.keys(payload.endpoints)}'`))) } else { diff --git a/src/lib/app-helper.js b/src/lib/app-helper.js index a323c2c6..9b7c3c9e 100644 --- a/src/lib/app-helper.js +++ b/src/lib/app-helper.js @@ -155,7 +155,7 @@ async function runScript (command, dir, cmdArgs = []) { aioLogger.debug(`Killing ${command} event hook long-running process (pid: ${pid})`) process.kill(pid, 'SIGTERM') } catch (_) { - // do nothing if pid not found + // do nothing if pid not found } }) } @@ -180,15 +180,30 @@ function wrapError (err) { return new Error(message) } -/** @private */ -async function getCliInfo () { - await context.setCli({ 'cli.bare-output': true }, false) // set this globally - +/** + * getCliInfo + * + * @private + * + * @param {boolean} useForce - if true, user will be forced to login if not already logged in + * @returns {Promise<{accessToken: string, env: string}>} accessToken and env +*/ +async function getCliInfo (useForce = true) { const env = getCliEnv() - - aioLogger.debug(`Retrieving CLI Token using env=${env}`) - const accessToken = await getToken(CLI) - + let accessToken + await context.setCli({ 'cli.bare-output': true }, false) // set this globally + if (useForce) { + aioLogger.debug('Retrieving CLI Token using force=true') + accessToken = await getToken(CLI) + } else { + aioLogger.debug('Retrieving CLI Token using force=false') + // in this case, the user might be logged in, but we don't want to force them + // we just check the config for the token ( we still use the cli context so we don't need to know + // the inner workings of ims-lib and where it stores access tokens) + // todo: this is a workaround, we should have a better way to check if the user is logged in (in ims-lib) + const contextConfig = await context.getCli() + accessToken = contextConfig?.access_token?.token + } return { accessToken, env } } diff --git a/src/lib/audit-logger.js b/src/lib/audit-logger.js index b434f948..c2f4d28b 100644 --- a/src/lib/audit-logger.js +++ b/src/lib/audit-logger.js @@ -12,18 +12,16 @@ const fetch = require('node-fetch') const fs = require('fs') const path = require('path') const chalk = require('chalk') -const { getCliEnv, PROD_ENV } = require('@adobe/aio-lib-env') -const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:lib-audit-logger', { provider: 'debug' }) const OPERATIONS = { AB_APP_DEPLOY: 'ab_app_deploy', AB_APP_UNDEPLOY: 'ab_app_undeploy', - AB_APP_TEST: 'ab_app_test', // todo : remove after testing + AB_APP_TEST: 'ab_app_test', AB_APP_ASSETS_DEPLOYED: 'ab_app_assets_deployed', AB_APP_ASSETS_UNDEPLOYED: 'ab_app_assets_undeployed' } -const AUDIT_SERVICE_ENPOINTS = { +const AUDIT_SERVICE_ENDPOINTS = { stage: 'https://adp-auditlog-service-stage.adobeioruntime.net/api/v1/web/audit-log-api/event-post', prod: 'https://adp-auditlog-service-prod.adobeioruntime.net/api/v1/web/audit-log-api/event-post' } @@ -35,12 +33,7 @@ const AUDIT_SERVICE_ENPOINTS = { * @param {string} env valid env stage|prod */ async function sendAuditLogs (accessToken, logEvent, env = 'prod') { - // TODO: this is blocked by the audit service only being available in stage - // remove this check once the service is available in prod - if (env !== 'stage') { - return - } - const url = AUDIT_SERVICE_ENPOINTS[env] + const url = AUDIT_SERVICE_ENDPOINTS[env] ?? AUDIT_SERVICE_ENDPOINTS.prod const payload = { event: logEvent } @@ -67,11 +60,6 @@ async function sendAuditLogs (accessToken, logEvent, env = 'prod') { * @returns {object} logEvent */ function getAuditLogEvent (flags, project, event) { - if (getCliEnv() === PROD_ENV) { - aioLogger.debug('Audit logging is currently disabled in production environment') - return null - } - let logEvent, logStrMsg if (project && project.org && project.workspace) { if (event === 'AB_APP_DEPLOY') { @@ -151,6 +139,6 @@ function getFilesCountWithExtension (directory) { module.exports = { sendAuditLogs, getAuditLogEvent, - AUDIT_SERVICE_ENPOINTS, + AUDIT_SERVICE_ENDPOINTS, getFilesCountWithExtension } diff --git a/test/commands/app/deploy.test.js b/test/commands/app/deploy.test.js index c5607f5e..c22e39d6 100644 --- a/test/commands/app/deploy.test.js +++ b/test/commands/app/deploy.test.js @@ -217,7 +217,7 @@ test('flags', async () => { expect(typeof TheCommand.flags.action).toBe('object') expect(TheCommand.flags.action.char).toBe('a') expect(typeof TheCommand.flags.action.description).toBe('string') - expect(JSON.stringify(TheCommand.flags.action.exclusive)).toStrictEqual(JSON.stringify(['extension', { name: 'publish', when: () => {} }])) + expect(JSON.stringify(TheCommand.flags.action.exclusive)).toStrictEqual(JSON.stringify(['extension', { name: 'publish', when: () => { } }])) expect(typeof TheCommand.flags['web-assets']).toBe('object') expect(typeof TheCommand.flags['web-assets'].description).toBe('string') @@ -1324,11 +1324,11 @@ describe('run', () => { expect(command.error).toHaveBeenCalledTimes(0) expect(mockRuntimeLib.deployActions).toHaveBeenCalledTimes(1) expect(mockWebLib.deployWeb).toHaveBeenCalledTimes(1) - expect(auditLogger.sendAuditLogs.mock.calls.length).toBeLessThanOrEqual(2) + expect(auditLogger.sendAuditLogs).toHaveBeenCalledTimes(1) expect(auditLogger.sendAuditLogs).toHaveBeenCalledWith(mockToken, expect.objectContaining({ orgId: mockOrg, projectId: mockProject, workspaceId: mockWorkspaceId, workspaceName: mockWorkspaceName }), mockEnv) }) - test('Do not send audit logs for successful app deploy', async () => { + test('Do not send audit logs for successful app deploy, if no logevent is present', async () => { const mockToken = 'mocktoken' const mockEnv = 'stage' const mockOrg = 'mockorg' @@ -1362,6 +1362,43 @@ describe('run', () => { expect(command.error).toHaveBeenCalledTimes(0) expect(mockRuntimeLib.deployActions).toHaveBeenCalledTimes(1) expect(mockWebLib.deployWeb).toHaveBeenCalledTimes(1) + expect(auditLogger.sendAuditLogs).toHaveBeenCalledTimes(0) + }) + + test('Do not send audit logs for successful app deploy, if case of no token', async () => { + const mockToken = null + const mockEnv = 'stage' + const mockOrg = 'mockorg' + const mockProject = 'mockproject' + const mockWorkspaceId = 'mockworkspaceid' + const mockWorkspaceName = 'mockworkspacename' + + helpers.getCliInfo.mockResolvedValueOnce({ + accessToken: mockToken, + env: mockEnv + }) + + command.getFullConfig = jest.fn().mockReturnValue({ + aio: { + project: { + id: mockProject, + org: { + id: mockOrg + }, + workspace: { + id: mockWorkspaceId, + name: mockWorkspaceName + } + } + } + }) + command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig)) + + await command.run() + expect(command.error).toHaveBeenCalledTimes(0) + expect(mockRuntimeLib.deployActions).toHaveBeenCalledTimes(1) + expect(mockWebLib.deployWeb).toHaveBeenCalledTimes(1) + expect(auditLogger.sendAuditLogs).toHaveBeenCalledTimes(0) }) test('Send audit logs for successful app deploy + web assets', async () => { @@ -1403,4 +1440,51 @@ describe('run', () => { expect(auditLogger.getFilesCountWithExtension).toHaveBeenCalledTimes(2) expect(auditLogger.sendAuditLogs).toHaveBeenCalledWith(mockToken, expect.objectContaining({ orgId: mockOrg, projectId: mockProject, workspaceId: mockWorkspaceId, workspaceName: mockWorkspaceName }), mockEnv) }) + + test('Should deploy successfully even if Audit log service is unavailable', async () => { + const mockToken = 'mocktoken' + const mockEnv = 'stage' + const mockOrg = 'mockorg' + const mockProject = 'mockproject' + const mockWorkspaceId = 'mockworkspaceid' + const mockWorkspaceName = 'mockworkspacename' + helpers.getCliInfo.mockResolvedValueOnce({ + accessToken: mockToken, + env: mockEnv + }) + command.getFullConfig = jest.fn().mockReturnValue({ + aio: { + project: { + id: mockProject, + org: { + id: mockOrg + }, + workspace: { + id: mockWorkspaceId, + name: mockWorkspaceName + } + } + } + }) + + auditLogger.sendAuditLogs.mockRejectedValue({ + message: 'Internal Server Error', + status: 500 + }) + + command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig)) + + await command.run() + expect(command.log).toHaveBeenCalledWith( + expect.stringContaining('skipping publish phase...') + ) + + expect(command.log).toHaveBeenCalledWith( + expect.stringContaining('Successful deployment 🏄') + ) + expect(auditLogger.sendAuditLogs).toHaveBeenCalledTimes(1) + expect(command.error).toHaveBeenCalledTimes(0) + expect(mockRuntimeLib.deployActions).toHaveBeenCalledTimes(1) + expect(mockWebLib.deployWeb).toHaveBeenCalledTimes(1) + }) }) diff --git a/test/commands/app/undeploy.test.js b/test/commands/app/undeploy.test.js index ee40f37e..ba908bb0 100644 --- a/test/commands/app/undeploy.test.js +++ b/test/commands/app/undeploy.test.js @@ -143,7 +143,10 @@ describe('run', () => { command.error = jest.fn() command.log = jest.fn() command.appConfig = mockConfigData - command.config = { runCommand: jest.fn(), runHook: jest.fn() } + command.config = { + runCommand: jest.fn(), + runHook: jest.fn() + } command.getLibConsoleCLI = jest.fn(() => mockLibConsoleCLI) command.getAppExtConfigs = jest.fn() command.getFullConfig = jest.fn().mockReturnValue({ @@ -189,7 +192,8 @@ describe('run', () => { command.argv = ['--no-actions', '--no-web-assets'] await command.run() - expect(command.error).toHaveBeenCalledTimes(0) + expect(command.error).toHaveBeenCalledTimes(1) + expect(command.error).toHaveBeenCalledWith('Nothing to be done 🚫') expect(mockRuntimeLib.undeployActions).toHaveBeenCalledTimes(0) expect(mockWebLib.undeployWeb).toHaveBeenCalledTimes(0) @@ -496,11 +500,41 @@ describe('run', () => { expect(command.error).toHaveBeenCalledTimes(0) expect(mockRuntimeLib.undeployActions).toHaveBeenCalledTimes(1) expect(mockWebLib.undeployWeb).toHaveBeenCalledTimes(1) - expect(auditLogger.sendAuditLogs.mock.calls.length).toBeGreaterThan(1) + expect(auditLogger.sendAuditLogs.mock.calls.length).toBe(1) expect(auditLogger.sendAuditLogs).toHaveBeenCalledWith(mockToken, expect.objectContaining({ orgId: mockOrg, projectId: mockProject, workspaceId: mockWorkspaceId, workspaceName: mockWorkspaceName }), mockEnv) }) - test('Do not Send audit logs for successful app undeploy', async () => { + test('Do not Send audit logs for successful app undeploy if case of no-token', async () => { + const mockOrg = 'mockorg' + const mockProject = 'mockproject' + const mockWorkspaceId = 'mockworkspaceid' + const mockWorkspaceName = 'mockworkspacename' + + command.getFullConfig = jest.fn().mockReturnValue({ + aio: { + project: { + id: mockProject, + org: { + id: mockOrg + }, + workspace: { + id: mockWorkspaceId, + name: mockWorkspaceName + } + } + } + }) + command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig)) + helpers.getCliInfo.mockImplementationOnce(() => null) + + await command.run() + expect(command.error).toHaveBeenCalledTimes(0) + expect(mockRuntimeLib.undeployActions).toHaveBeenCalledTimes(1) + expect(mockWebLib.undeployWeb).toHaveBeenCalledTimes(1) + expect(auditLogger.sendAuditLogs.mock.calls.length).toBe(0) + }) + + test('Do not Send audit logs for successful app undeploy, if no logevent is present', async () => { const mockOrg = 'mockorg' const mockProject = 'mockproject' const mockWorkspaceId = 'mockworkspaceid' @@ -529,6 +563,39 @@ describe('run', () => { expect(mockRuntimeLib.undeployActions).toHaveBeenCalledTimes(1) expect(mockWebLib.undeployWeb).toHaveBeenCalledTimes(1) expect(auditLogger.sendAuditLogs.mock.calls.length).toBe(0) - expect(command.log).toHaveBeenCalledWith(expect.stringMatching(/Warning: No valid config data found to send audit log event for deployment/)) + }) + + test('Should app undeploy successfully even if Audit Log Service is not available', async () => { + const mockOrg = 'mockorg' + const mockProject = 'mockproject' + const mockWorkspaceId = 'mockworkspaceid' + const mockWorkspaceName = 'mockworkspacename' + + command.getFullConfig = jest.fn().mockReturnValue({ + aio: { + project: { + id: mockProject, + org: { + id: mockOrg + }, + workspace: { + id: mockWorkspaceId, + name: mockWorkspaceName + } + } + } + }) + command.getAppExtConfigs.mockResolvedValueOnce(createAppConfig(command.appConfig)) + + auditLogger.sendAuditLogs.mockRejectedValue({ + message: 'Internal Server Error', + status: 500 + }) + + await command.run() + expect(command.error).toHaveBeenCalledTimes(0) + expect(mockRuntimeLib.undeployActions).toHaveBeenCalledTimes(1) + expect(mockWebLib.undeployWeb).toHaveBeenCalledTimes(1) + expect(auditLogger.sendAuditLogs).toHaveBeenCalledTimes(1) }) }) diff --git a/test/commands/lib/app-helper.test.js b/test/commands/lib/app-helper.test.js index cfc59a99..26b8129f 100644 --- a/test/commands/lib/app-helper.test.js +++ b/test/commands/lib/app-helper.test.js @@ -38,6 +38,10 @@ const aioConfig = require('@adobe/aio-lib-core-config') const libEnv = require('@adobe/aio-lib-env') const libIms = require('@adobe/aio-lib-ims') +jest.mock('child_process', () => ({ + exec: jest.fn() +})) + beforeEach(() => { Object.defineProperty(process, 'platform', { value: 'linux' }) execa.mockReset() @@ -293,7 +297,7 @@ test('runPackageScript logs if package.json does not have matching script', asyn test('runInProcess with script should call runScript', async () => { expect(appHelper.runInProcess).toBeDefined() expect(appHelper.runInProcess).toBeInstanceOf(Function) - execa.command.mockReturnValue({ on: () => {} }) + execa.command.mockReturnValue({ on: () => { } }) await appHelper.runInProcess('echo new command who dis?', {}) expect(mockLogger.debug).toHaveBeenCalledWith('runInProcess: error running project hook in process, running as package script instead') expect(execa.command).toHaveBeenCalledWith('echo new command who dis?', expect.any(Object)) @@ -308,7 +312,7 @@ test('runInProcess with require', async () => { ) expect(appHelper.runInProcess).toBeDefined() expect(appHelper.runInProcess).toBeInstanceOf(Function) - execa.command.mockReturnValue({ on: () => {} }) + execa.command.mockReturnValue({ on: () => { } }) await appHelper.runInProcess('does-not-exist', {}) expect(mockReq).toHaveBeenCalled() expect(mockLogger.debug).toHaveBeenCalledWith('runInProcess: running project hook in process') @@ -328,13 +332,13 @@ test('runScript with empty command', async () => { }) test('runScript with defined dir', async () => { - execa.command.mockReturnValue({ on: () => {} }) + execa.command.mockReturnValue({ on: () => { } }) await appHelper.runScript('somecommand', 'somedir') expect(execa.command).toHaveBeenCalledWith('somecommand', expect.objectContaining({ cwd: 'somedir' })) }) test('runScript with empty dir => process.cwd', async () => { - execa.command.mockReturnValue({ on: () => {} }) + execa.command.mockReturnValue({ on: () => { } }) await appHelper.runScript('somecommand', undefined) expect(execa.command).toHaveBeenCalledWith('somecommand', expect.objectContaining({ cwd: process.cwd() })) }) @@ -906,6 +910,13 @@ describe('getCliInfo', () => { { accessToken: 'stoken', env: 'stage' } ) }) + test('useForceFalse', async () => { + libEnv.getCliEnv.mockReturnValue('prod') + libIms.getToken.mockResolvedValue('asdasd') + const res = await appHelper.getCliInfo(false) + expect(res).toEqual({ accessToken: undefined, env: 'prod' }) + expect(libIms.getToken).toHaveBeenCalledTimes(0) + }) }) describe('createWebExportFilter', () => { diff --git a/test/commands/lib/audit-logger.test.js b/test/commands/lib/audit-logger.test.js index 5308c099..9847b959 100644 --- a/test/commands/lib/audit-logger.test.js +++ b/test/commands/lib/audit-logger.test.js @@ -73,7 +73,7 @@ test('sendAuditLogs with valid params', async () => { } await auditLogger.sendAuditLogs(mockToken, mockLogEvent, mockEnv) expect(fetch).toHaveBeenCalledTimes(1) - expect(fetch).toHaveBeenCalledWith(auditLogger.AUDIT_SERVICE_ENPOINTS[mockEnv], options) + expect(fetch).toHaveBeenCalledWith(auditLogger.AUDIT_SERVICE_ENDPOINTS[mockEnv], options) }) // NOTE: this test is blocked until the audit service is available in prod @@ -88,8 +88,23 @@ test('sendAuditLogs with default params', async () => { body: JSON.stringify({ event: mockLogEvent }) } await auditLogger.sendAuditLogs(mockToken, mockLogEvent) - expect(fetch).toHaveBeenCalledTimes(0) - // expect(fetch).toHaveBeenCalledWith(auditLogger.AUDIT_SERVICE_ENPOINTS.prod, options) + expect(fetch).toHaveBeenCalledTimes(1) + expect(fetch).toHaveBeenCalledWith(auditLogger.AUDIT_SERVICE_ENDPOINTS.prod, options) +}) + +test('should take prod endpoint if calling sendAuditLogs with non-exisiting env', async () => { + fetch.mockReturnValue(mockResponse) + const options = { + method: 'POST', + headers: { + Authorization: 'Bearer ' + mockToken, + 'Content-type': 'application/json' + }, + body: JSON.stringify({ event: mockLogEvent }) + } + await auditLogger.sendAuditLogs(mockToken, mockLogEvent, 'dev') + expect(fetch).toHaveBeenCalledTimes(1) + expect(fetch).toHaveBeenCalledWith(auditLogger.AUDIT_SERVICE_ENDPOINTS.prod, options) }) test('sendAuditLogs error response', async () => { @@ -104,7 +119,7 @@ test('sendAuditLogs error response', async () => { } await expect(auditLogger.sendAuditLogs(mockToken, mockLogEvent, mockEnv)).rejects.toThrow('Failed to send audit log - 400') expect(fetch).toHaveBeenCalledTimes(1) - expect(fetch).toHaveBeenCalledWith(auditLogger.AUDIT_SERVICE_ENPOINTS[mockEnv], options) + expect(fetch).toHaveBeenCalledWith(auditLogger.AUDIT_SERVICE_ENDPOINTS[mockEnv], options) }) describe('getAuditLogEvent', () => { @@ -197,13 +212,6 @@ describe('getAuditLogEvent', () => { expect(result).toBeFalsy() }) - test('should return undefined in PROD (for now)', () => { - getCliEnv.mockReturnValueOnce('prod') - const event = 'AB_APP_DEPLOY' - const result = auditLogger.getAuditLogEvent(flags, project, event) - expect(result).toBeFalsy() - }) - test('should default operation to APP_TEST if event is not found in OPERATIONS', () => { const event = 'UNKNOWN_EVENT' const result = auditLogger.getAuditLogEvent(flags, project, event)