From c18201d01af54f1d256e03c2fa21ef8c048bcec4 Mon Sep 17 00:00:00 2001 From: LucioMS Date: Tue, 13 Jun 2023 13:45:56 -0700 Subject: [PATCH 01/29] Local Credential Hijacking fix https://t.corp.amazon.com/P85169442 --- .../authorization-controller/index.js | 23 ++++- .../authorization-controller/messages.js | 2 + .../authorization-controller/questions.js | 10 +++ .../authorization-controller/ui.js | 16 ++++ .../authorization-controller/index-test.js | 85 ++++++++++++------- 5 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 lib/controllers/authorization-controller/questions.js create mode 100644 lib/controllers/authorization-controller/ui.js diff --git a/lib/controllers/authorization-controller/index.js b/lib/controllers/authorization-controller/index.js index 1f4aad65..df80f9ec 100644 --- a/lib/controllers/authorization-controller/index.js +++ b/lib/controllers/authorization-controller/index.js @@ -11,6 +11,7 @@ const Messenger = require("../../view/messenger"); const SpinnerView = require("../../view/spinner-view"); const messages = require("./messages"); +const ui = require("./ui"); module.exports = class AuthorizationController { /** @@ -147,11 +148,29 @@ module.exports = class AuthorizationController { listenSpinner.terminate(); const requestUrl = request.url; const requestQuery = url.parse(requestUrl, true).query; - server.destroy(); + if (requestUrl.startsWith("/cb?code")) { response.end(messages.ASK_SIGN_IN_SUCCESS_MESSAGE); - callback(null, requestQuery.code); + ui.confirmAllowSignIn((error, confirmSignInChoice) => { + // After confirmed or not browser sign in, closes the socket/port + // with server.destroy(). + // We need to keep the port open so a local hacker is not be able to + // open that port until we get an answer in confirmAllowSignIn + server.destroy(); + + if (error) { + return callback(error); + } + + if (!confirmSignInChoice) { + return callback(messages.STOP_UNCONFIRMED_BROWSER_SIGNIN); + } + + callback(null, requestQuery.code); + }); + return; } + server.destroy(); if (requestUrl.startsWith("/cb?error")) { response.statusCode = 403; const errorMessage = `Error: ${requestQuery.error}\nReason: ${requestQuery.error_description}`; diff --git a/lib/controllers/authorization-controller/messages.js b/lib/controllers/authorization-controller/messages.js index a9ce9185..c3380a23 100644 --- a/lib/controllers/authorization-controller/messages.js +++ b/lib/controllers/authorization-controller/messages.js @@ -56,3 +56,5 @@ module.exports.ASK_SIGN_IN_FAILURE_MESSAGE = (error) => ` `; module.exports.ASK_ENV_VARIABLES_ERROR_MESSAGE = "Could not find either of the environment variables: ASK_ACCESS_TOKEN, ASK_REFRESH_TOKEN"; + +module.exports.STOP_UNCONFIRMED_BROWSER_SIGNIN = "Stopping configuration due to unconfirmed browser sign in."; diff --git a/lib/controllers/authorization-controller/questions.js b/lib/controllers/authorization-controller/questions.js new file mode 100644 index 00000000..451eb4d2 --- /dev/null +++ b/lib/controllers/authorization-controller/questions.js @@ -0,0 +1,10 @@ +module.exports = { + CONFIRM_ALLOW_BROWSER_SIGN_IN: [ + { + message: "Do you confirm that you used the browser to sign in to Alexa Skills Kit Tools?", + type: "confirm", + name: "choice", + default: true, + }, + ], +}; diff --git a/lib/controllers/authorization-controller/ui.js b/lib/controllers/authorization-controller/ui.js new file mode 100644 index 00000000..ea429c58 --- /dev/null +++ b/lib/controllers/authorization-controller/ui.js @@ -0,0 +1,16 @@ +const inquirer = require("inquirer"); +const questions = require("./questions"); + +module.exports = { + confirmAllowSignIn, +}; +export function confirmAllowSignIn(callback) { + inquirer + .prompt(questions.CONFIRM_ALLOW_BROWSER_SIGN_IN) + .then((answer) => { + callback(null, answer.choice); + }) + .catch((error) => { + callback(error); + }); +} diff --git a/test/unit/controller/authorization-controller/index-test.js b/test/unit/controller/authorization-controller/index-test.js index 51faae43..05f01b9b 100644 --- a/test/unit/controller/authorization-controller/index-test.js +++ b/test/unit/controller/authorization-controller/index-test.js @@ -14,6 +14,7 @@ const CONSTANTS = require("../../../../lib/utils/constants"); const LocalHostServer = require("../../../../lib/utils/local-host-server"); const Messenger = require("../../../../lib/view/messenger"); const SpinnerView = require("../../../../lib/view/spinner-view"); +const ui = require("../../../../lib/controllers/authorization-controller/ui"); describe("Controller test - Authorization controller test", () => { const DEFAULT_CLIENT_ID = CONSTANTS.LWA.CLI_INTERNAL_ONLY_LWA_CLIENT.CLIENT_ID; @@ -456,40 +457,58 @@ describe("Controller test - Authorization controller test", () => { }); }); - it("| local server returns valid authCode", (done) => { - // setup - sinon.stub(LocalHostServer.prototype, "listen"); - sinon.stub(LocalHostServer.prototype, "registerEvent"); - const requestDestroyStub = sinon.stub(); - const request = { - url: "/cb?code", - socket: { - destroy: requestDestroyStub, - }, - }; - const endStub = sinon.stub(); - const response = { - on: sinon.stub().callsArgWith(1), - end: endStub, - }; - sinon.stub(SpinnerView.prototype, "terminate"); - const requestQuery = { - query: { - code: TEST_AUTH_CODE, - }, - }; - sinon.stub(url, "parse").returns(requestQuery); - sinon.stub(LocalHostServer.prototype, "destroy"); - sinon.stub(LocalHostServer.prototype, "create").callsArgWith(0, request, response); + describe("local server ", () => { + let endStub; + beforeEach(() => { + sinon.stub(LocalHostServer.prototype, "listen"); + sinon.stub(LocalHostServer.prototype, "registerEvent"); + const requestDestroyStub = sinon.stub(); + const request = { + url: "/cb?code", + socket: { + destroy: requestDestroyStub, + }, + }; + endStub = sinon.stub(); + const response = { + on: sinon.stub().callsArgWith(1), + end: endStub, + }; + sinon.stub(SpinnerView.prototype, "terminate"); + const requestQuery = { + query: { + code: TEST_AUTH_CODE, + }, + }; + sinon.stub(url, "parse").returns(requestQuery); + sinon.stub(LocalHostServer.prototype, "destroy"); + sinon.stub(LocalHostServer.prototype, "create").callsArgWith(0, request, response); + }); - // call - authorizationController._listenResponseFromLWA(TEST_PORT, (err, authCode) => { - // verify - expect(endStub.callCount).eq(1); - expect(endStub.args[0][0]).eq(messages.ASK_SIGN_IN_SUCCESS_MESSAGE); - expect(err).eq(null); - expect(authCode).eq(TEST_AUTH_CODE); - done(); + it("| returns valid authCode", (done) => { + sinon.stub(ui, "confirmAllowSignIn").callsArgWith(0, null, true); + // call + authorizationController._listenResponseFromLWA(TEST_PORT, (err, authCode) => { + // verify + expect(endStub.callCount).eq(1); + expect(endStub.args[0][0]).eq(messages.ASK_SIGN_IN_SUCCESS_MESSAGE); + expect(err).eq(null); + expect(authCode).eq(TEST_AUTH_CODE); + done(); + }); + }); + + it("| returns error for unconfirmed browser sign in", (done) => { + sinon.stub(ui, "confirmAllowSignIn").callsArgWith(0, null, false); + // call + authorizationController._listenResponseFromLWA(TEST_PORT, (err, authCode) => { + // verify + expect(endStub.callCount).eq(1); + expect(endStub.args[0][0]).eq(messages.ASK_SIGN_IN_SUCCESS_MESSAGE); + expect(err).eq(messages.STOP_UNCONFIRMED_BROWSER_SIGNIN); + expect(authCode).eq(undefined); + done(); + }); }); }); }); From 7c8107a4127fb59c94e16bab7f9fb4827e9652cd Mon Sep 17 00:00:00 2001 From: LucioMS Date: Wed, 14 Jun 2023 10:59:26 -0700 Subject: [PATCH 02/29] chore: improving a comment to refer to an issue --- lib/controllers/authorization-controller/index.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/controllers/authorization-controller/index.js b/lib/controllers/authorization-controller/index.js index df80f9ec..fa30712d 100644 --- a/lib/controllers/authorization-controller/index.js +++ b/lib/controllers/authorization-controller/index.js @@ -149,13 +149,12 @@ module.exports = class AuthorizationController { const requestUrl = request.url; const requestQuery = url.parse(requestUrl, true).query; + // Response from the browser with authentication code if (requestUrl.startsWith("/cb?code")) { response.end(messages.ASK_SIGN_IN_SUCCESS_MESSAGE); ui.confirmAllowSignIn((error, confirmSignInChoice) => { - // After confirmed or not browser sign in, closes the socket/port - // with server.destroy(). - // We need to keep the port open so a local hacker is not be able to - // open that port until we get an answer in confirmAllowSignIn + // Closing the socket port with server.destroy() only after confirmation question. + // See https://github.com/alexa/ask-cli/issues/476 server.destroy(); if (error) { From db0d51d1391cf451c459730a6997c40600df2ddd Mon Sep 17 00:00:00 2001 From: LucioMS Date: Wed, 14 Jun 2023 12:00:39 -0700 Subject: [PATCH 03/29] chore: Increasing version to 2.30.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bdb48d34..a35bb7fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ask-cli", - "version": "2.30.0", + "version": "2.30.1", "description": "Alexa Skills Kit (ASK) Command Line Interfaces", "bin": { "ask": "dist/bin/ask.js" From 767e10481fb7230c6748f784c6d5e6a483508950 Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Tue, 20 Jun 2023 21:49:13 -0700 Subject: [PATCH 04/29] fix: prevent errors to be displayed as [Error]: [object Object] --- lib/view/json-view.js | 3 +++ lib/view/messenger.js | 4 ++-- test/unit/view/messenger-test.js | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/view/json-view.js b/lib/view/json-view.js index ee4def1d..31ec163f 100644 --- a/lib/view/json-view.js +++ b/lib/view/json-view.js @@ -11,6 +11,9 @@ module.exports = { */ function toString(jsonObject) { try { + if (typeof jsonObject === 'string') { + return jsonObject; + } // handle issue when Error object serialized to {} if (jsonObject instanceof Error) { jsonObject = {message: jsonObject.message, stack: jsonObject.stack, detail: jsonObject}; diff --git a/lib/view/messenger.js b/lib/view/messenger.js index 51be0a15..e60dc03c 100644 --- a/lib/view/messenger.js +++ b/lib/view/messenger.js @@ -1,6 +1,7 @@ const chalk = require("chalk"); const path = require("path"); const fs = require("fs"); +const jsonViewer = require("./json-view"); // A map used to define message levels // and set their display styles @@ -167,8 +168,7 @@ class Messenger { * @param {*} data the message to display */ error(data) { - const msg = data.constructor.name === "Error" ? data.message : data; - + const msg = data.constructor.name === "Error" ? data.message : jsonViewer.toString(data); const operation = "ERROR"; this.buffer({ time: Messenger.getTime(), diff --git a/test/unit/view/messenger-test.js b/test/unit/view/messenger-test.js index 38951a6d..b59f1da9 100644 --- a/test/unit/view/messenger-test.js +++ b/test/unit/view/messenger-test.js @@ -4,9 +4,16 @@ const chalk = require("chalk"); const fs = require("fs"); const path = require("path"); const Messenger = require("../../../lib/view/messenger"); +const jsonView = require("../../../lib/view/json-view"); describe("View test - messenger file test", () => { const TEST_MESSAGE = "TEST_MESSAGE"; + const TEST_STACK = "TEST_STACK"; + const TEST_ERROR_WITH_STACK = { + message: TEST_MESSAGE, + stack: TEST_STACK, + foo: 2, + } const TEST_TIME = "TEST_TIME"; const TEST_ERROR_OBJ = new Error("TEST_ERROR"); @@ -147,6 +154,24 @@ describe("View test - messenger file test", () => { expect(stub.args[0][0]).equal(chalk`{bold.red [Error]: ${TEST_MESSAGE}}`); }); + it("| error function correctly push error objects to buffer and display", () => { + const stub = sinon.stub(console, "error"); + const expectedError = jsonView.toString(TEST_ERROR_WITH_STACK); + const expectedItem = { + time: TEST_TIME, + operation: "ERROR", + level: 50, + msg: expectedError, + }; + + // call + Messenger.getInstance().error(TEST_ERROR_WITH_STACK); + + // verify + expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); + expect(stub.args[0][0]).equal(chalk`{bold.red [Error]: ${expectedError}}`); + }); + it("| fatal function correctly push message to buffer and display", () => { const stub = sinon.stub(console, "error"); const expectedItem = { From bbf9e833516e901adeffbbd6a6c03b0515f6c259 Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Thu, 22 Jun 2023 10:47:34 -0700 Subject: [PATCH 05/29] chore(release): 2.30.2 --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7c48578..1bb7e24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.30.2](https://github.com/alexa/ask-cli/compare/v2.30.1...v2.30.2) (2023-06-22) + + +### Bug Fixes + +* prevent errors to be displayed as [Error]: [object Object] ([767e104](https://github.com/alexa/ask-cli/commit/767e10481fb7230c6748f784c6d5e6a483508950)) + ## [2.30.0](https://github.com/alexa/ask-cli/compare/v2.29.1...v2.30.0) (2023-05-22) diff --git a/package-lock.json b/package-lock.json index 15deab03..469468f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ask-cli", - "version": "2.30.0", + "version": "2.30.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ask-cli", - "version": "2.30.0", + "version": "2.30.2", "hasInstallScript": true, "license": "Apache 2.0", "dependencies": { diff --git a/package.json b/package.json index a35bb7fb..f25f9710 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ask-cli", - "version": "2.30.1", + "version": "2.30.2", "description": "Alexa Skills Kit (ASK) Command Line Interfaces", "bin": { "ask": "dist/bin/ask.js" From 5502acf546f46a81b4d7a2fe41100edd40fa8d9a Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:19:21 -0700 Subject: [PATCH 06/29] fix: updating axios to resolve issue #480 --- package-lock.json | 64 +++++++++++++++++++++++++++++++++++++++++------ package.json | 2 +- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 469468f1..9e223e7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "ask-smapi-sdk": "^1.3.0", "async": "^2.6.1", "aws-profile-handler": "2.0.3", - "axios": "^0.26.1", + "axios": "^1.4.0", "bunyan": "^1.8.15", "chalk": "4.0.0", "commander": "^4.1.1", @@ -128,6 +128,14 @@ "acc": "dist/cjs/acc.js" } }, + "node_modules/@alexa/acdl/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, "node_modules/@alexa/ask-expressions-spec": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/@alexa/ask-expressions-spec/-/ask-expressions-spec-0.0.2.tgz", @@ -4325,6 +4333,11 @@ "lodash": "^4.17.14" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", @@ -4379,11 +4392,13 @@ } }, "node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", "dependencies": { - "follow-redirects": "^1.14.8" + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/babel-plugin-istanbul": { @@ -5396,6 +5411,17 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -6346,6 +6372,14 @@ "node": ">=8" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -7017,6 +7051,19 @@ "node": ">=8.0.0" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/form-data-encoder": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", @@ -10344,7 +10391,6 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -10362,7 +10408,6 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -12891,6 +12936,11 @@ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/proxyquire": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/proxyquire/-/proxyquire-2.1.3.tgz", diff --git a/package.json b/package.json index f25f9710..ae552dad 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "ask-smapi-sdk": "^1.3.0", "async": "^2.6.1", "aws-profile-handler": "2.0.3", - "axios": "^0.26.1", + "axios": "^1.4.0", "bunyan": "^1.8.15", "chalk": "4.0.0", "commander": "^4.1.1", From ff180a8ea4eb5c57cd879f58e03beecb67cb5238 Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Fri, 23 Jun 2023 12:27:32 -0700 Subject: [PATCH 07/29] chore(release): 2.30.3 --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bb7e24c..97f3bb66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.30.3](https://github.com/alexa/ask-cli/compare/v2.30.2...v2.30.3) (2023-06-23) + + +### Bug Fixes + +* updating axios to resolve issue [#480](https://github.com/alexa/ask-cli/issues/480) ([5502acf](https://github.com/alexa/ask-cli/commit/5502acf546f46a81b4d7a2fe41100edd40fa8d9a)) + ### [2.30.2](https://github.com/alexa/ask-cli/compare/v2.30.1...v2.30.2) (2023-06-22) diff --git a/package-lock.json b/package-lock.json index 9e223e7d..ebcd3078 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ask-cli", - "version": "2.30.2", + "version": "2.30.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ask-cli", - "version": "2.30.2", + "version": "2.30.3", "hasInstallScript": true, "license": "Apache 2.0", "dependencies": { diff --git a/package.json b/package.json index ae552dad..7769ed28 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ask-cli", - "version": "2.30.2", + "version": "2.30.3", "description": "Alexa Skills Kit (ASK) Command Line Interfaces", "bin": { "ask": "dist/bin/ask.js" From e096f425edcd79e0233113d03961da388e9e695a Mon Sep 17 00:00:00 2001 From: LucioMS Date: Wed, 5 Jul 2023 12:01:51 -0700 Subject: [PATCH 08/29] bug: Local Credential Hijacking fix => round 2 https://github.com/alexa/ask-cli/pull/475 --- .../authorization-controller/index.js | 17 +++++++++++------ .../authorization-controller/questions.js | 7 +++++++ lib/controllers/authorization-controller/ui.js | 12 ++++++++++++ .../authorization-controller/index-test.js | 4 +++- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/lib/controllers/authorization-controller/index.js b/lib/controllers/authorization-controller/index.js index fa30712d..b2518aed 100644 --- a/lib/controllers/authorization-controller/index.js +++ b/lib/controllers/authorization-controller/index.js @@ -153,7 +153,7 @@ module.exports = class AuthorizationController { if (requestUrl.startsWith("/cb?code")) { response.end(messages.ASK_SIGN_IN_SUCCESS_MESSAGE); ui.confirmAllowSignIn((error, confirmSignInChoice) => { - // Closing the socket port with server.destroy() only after confirmation question. + // Closing the socket port with server.destroy() only after confirmation question. // See https://github.com/alexa/ask-cli/issues/476 server.destroy(); @@ -164,17 +164,22 @@ module.exports = class AuthorizationController { if (!confirmSignInChoice) { return callback(messages.STOP_UNCONFIRMED_BROWSER_SIGNIN); } - + callback(null, requestQuery.code); }); return; } - server.destroy(); + if (requestUrl.startsWith("/cb?error")) { - response.statusCode = 403; - const errorMessage = `Error: ${requestQuery.error}\nReason: ${requestQuery.error_description}`; + const errorMessage = `Error: ${requestQuery.error}\nReason: ${requestQuery.error_description}`.split("\n").join(". "); response.end(messages.ASK_SIGN_IN_FAILURE_MESSAGE(errorMessage)); - callback(errorMessage); + ui.informReceivedError((error, _) => { + // Closing the socket port with server.destroy() only after informing of error. + // See https://github.com/alexa/ask-cli/issues/476 + server.destroy(); + response.statusCode = 403; + callback(errorMessage); + }, errorMessage); } } } diff --git a/lib/controllers/authorization-controller/questions.js b/lib/controllers/authorization-controller/questions.js index 451eb4d2..70f14e3a 100644 --- a/lib/controllers/authorization-controller/questions.js +++ b/lib/controllers/authorization-controller/questions.js @@ -7,4 +7,11 @@ module.exports = { default: true, }, ], + INFORM_ERROR: { + // message is filled out in code + type: "list", + choices: ["Ok"], + name: "choice", + default: "Ok", + }, }; diff --git a/lib/controllers/authorization-controller/ui.js b/lib/controllers/authorization-controller/ui.js index ea429c58..4edeb39f 100644 --- a/lib/controllers/authorization-controller/ui.js +++ b/lib/controllers/authorization-controller/ui.js @@ -3,6 +3,7 @@ const questions = require("./questions"); module.exports = { confirmAllowSignIn, + informReceivedError }; export function confirmAllowSignIn(callback) { inquirer @@ -14,3 +15,14 @@ export function confirmAllowSignIn(callback) { callback(error); }); } + +export function informReceivedError(callback, error) { + inquirer + .prompt([{...questions.INFORM_ERROR, message: `Sign in error: ${error}.`}]) + .then((answer) => { + callback(null, answer.choice); + }) + .catch((error) => { + callback(error); + }); +} \ No newline at end of file diff --git a/test/unit/controller/authorization-controller/index-test.js b/test/unit/controller/authorization-controller/index-test.js index 05f01b9b..19d198a7 100644 --- a/test/unit/controller/authorization-controller/index-test.js +++ b/test/unit/controller/authorization-controller/index-test.js @@ -422,6 +422,7 @@ describe("Controller test - Authorization controller test", () => { // setup sinon.stub(LocalHostServer.prototype, "listen"); sinon.stub(LocalHostServer.prototype, "registerEvent"); + const informReceivedErrorStub = sinon.stub(ui, "informReceivedError").callsArgWith(0, null, "Ok"); const requestDestroyStub = sinon.stub(); const request = { url: "/cb?error", @@ -447,12 +448,13 @@ describe("Controller test - Authorization controller test", () => { // call authorizationController._listenResponseFromLWA(TEST_PORT, (err) => { // verify - const EXPECTED_ERR_MESSAGE = `Error: ${requestQuery.query.error}\nReason: ${requestQuery.query.error_description}`; + const EXPECTED_ERR_MESSAGE = `Error: ${requestQuery.query.error}\nReason: ${requestQuery.query.error_description}`.split("\n").join(". "); expect(spinnerTerminateStub.callCount).eq(1); expect(serverDestroyStub.callCount).eq(1); expect(endStub.callCount).eq(1); expect(endStub.args[0][0].includes(EXPECTED_ERR_MESSAGE)).equal(true); expect(err).eq(EXPECTED_ERR_MESSAGE); + expect(informReceivedErrorStub.called).to.be.true; done(); }); }); From 1ccbd1174e496a0f31e483ac07dae5227dffdfe4 Mon Sep 17 00:00:00 2001 From: LucioMS Date: Wed, 5 Jul 2023 13:39:10 -0700 Subject: [PATCH 09/29] chore: updating version to 2.30.4 and adding changees to changelog.md --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97f3bb66..9c733db4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +### [2.30.4](https://github.com/alexa/ask-cli/compare/v2.30.3...v2.30.4) (2023-06-23) + + +### Bug Fixes + +* fix: Local Credential Hijacking fix (part 2) [#475](https://github.com/alexa/ask-cli/pull/475) ([e096f42](https://github.com/alexa/ask-cli/pull/482/commits/e096f425edcd79e0233113d03961da388e9e695a)) + ### [2.30.3](https://github.com/alexa/ask-cli/compare/v2.30.2...v2.30.3) (2023-06-23) diff --git a/package.json b/package.json index 7769ed28..c7876916 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ask-cli", - "version": "2.30.3", + "version": "2.30.4", "description": "Alexa Skills Kit (ASK) Command Line Interfaces", "bin": { "ask": "dist/bin/ask.js" From 89c7fb183300d950314ee78a51a8210de575b7ad Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Fri, 4 Aug 2023 10:39:58 -0700 Subject: [PATCH 10/29] chore: updating the ask-cli version in the lock file --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ebcd3078..ef8b2848 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ask-cli", - "version": "2.30.3", + "version": "2.30.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ask-cli", - "version": "2.30.3", + "version": "2.30.4", "hasInstallScript": true, "license": "Apache 2.0", "dependencies": { From edeb190cf4367098e4148fa75e24a034dfbbe1bd Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Fri, 4 Aug 2023 10:51:57 -0700 Subject: [PATCH 11/29] chore: update changelog to list 2.30.0 as breaking as per #485 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c733db4..4a2bc972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,8 @@ All notable changes to this project will be documented in this file. See [standa ### Features -* add proxy support for smapi commands [#434](https://github.com/alexa/ask-cli/issues/434) ([fd20898](https://github.com/alexa/ask-cli/commit/fd20898d6d87cf30ce4dc3e7a78434aac531929b)) +- **Breaking:** add proxy support for smapi commands [#434](https://github.com/alexa/ask-cli/issues/434) ([fd20898](https://github.com/alexa/ask-cli/commit/fd20898d6d87cf30ce4dc3e7a78434aac531929b)). For more information on breaking changes see [#485](https://github.com/alexa/ask-cli/issues/485) + ### [2.29.2](https://github.com/alexa/ask-cli/compare/v2.29.1...v2.29.2) (2023-04-19) From 3a719081e613f5a03d119b2cd8e32f35df15825e Mon Sep 17 00:00:00 2001 From: Camden Foucht Date: Wed, 9 Aug 2023 09:50:31 -0700 Subject: [PATCH 12/29] fix skill-id option for legacy skills (#489) --- bin/askx-skill.ts | 12 ------------ bin/askx-smapi.ts | 19 ------------------- bin/askx-util.ts | 11 ----------- lib/commands/option-model.json | 12 +++++++----- test/unit/commands/option-validator-test.js | 7 ++++++- 5 files changed, 13 insertions(+), 48 deletions(-) delete mode 100644 bin/askx-skill.ts delete mode 100644 bin/askx-smapi.ts delete mode 100644 bin/askx-util.ts diff --git a/bin/askx-skill.ts b/bin/askx-skill.ts deleted file mode 100644 index 7a3eb98c..00000000 --- a/bin/askx-skill.ts +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env node - -import {commander, SKILL_COMMAND_MAP} from "../lib/commands/skill/skill-commander"; - -commander.parse(process.argv); - -if (!process.argv.slice(2).length) { - commander.outputHelp(); -} else if (SKILL_COMMAND_MAP[process.argv[2]] === undefined) { - console.error('Command not recognized. Please run "ask skill" to check the user instructions.'); - process.exit(1); -} diff --git a/bin/askx-smapi.ts b/bin/askx-smapi.ts deleted file mode 100644 index a8c69f36..00000000 --- a/bin/askx-smapi.ts +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env node - -import Messenger from "../lib/view/messenger"; -import jsonView from "../lib/view/json-view"; -import {makeSmapiCommander} from "../lib/commands/smapi/smapi-commander"; - -const commander = makeSmapiCommander(); - -if (!process.argv.slice(2).length) { - commander.outputHelp(); -} else { - commander - .parseAsync(process.argv) - .then((result) => Messenger.getInstance().info(result)) - .catch((err) => { - Messenger.getInstance().error(jsonView.toString(err)); - process.exit(1); - }); -} diff --git a/bin/askx-util.ts b/bin/askx-util.ts deleted file mode 100644 index e0fcd518..00000000 --- a/bin/askx-util.ts +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env node -import {commander, UTIL_COMMAND_MAP} from "../lib/commands/util/util-commander"; - -commander.parse(process.argv); - -if (!process.argv.slice(2).length) { - commander.outputHelp(); -} else if (UTIL_COMMAND_MAP[process.argv[2]] === undefined) { - console.error('Command not recognized. Please run "ask util" to check the user instructions.'); - process.exit(1); -} diff --git a/lib/commands/option-model.json b/lib/commands/option-model.json index a03a9ebf..82e6384e 100644 --- a/lib/commands/option-model.json +++ b/lib/commands/option-model.json @@ -46,10 +46,12 @@ "description": "run skill in the specified simulation mode (currently supports DEFAULT / NFI_ISOLATED_SIMULATION)", "alias": null, "stringInput": "REQUIRED", - "rule": [{ - "type": "ENUM", - "values": ["DEFAULT", "NFI_ISOLATED_SIMULATION"] - }] + "rule": [ + { + "type": "ENUM", + "values": ["DEFAULT", "NFI_ISOLATED_SIMULATION"] + } + ] }, "skill-id": { "name": "skill-id", @@ -59,7 +61,7 @@ "rule": [ { "type": "REGEX", - "regex": "^amzn\\d\\.ask\\.skill\\.\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}$" + "regex": "^amzn\\d((\\.ask\\.skill)|(\\.echo-sdk-ams\\.app))\\.\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}$" } ] }, diff --git a/test/unit/commands/option-validator-test.js b/test/unit/commands/option-validator-test.js index f8997da0..c1c1982f 100644 --- a/test/unit/commands/option-validator-test.js +++ b/test/unit/commands/option-validator-test.js @@ -111,13 +111,14 @@ describe("Command test - Option validator test", () => { const skillOptions = { optionValid: "amzn1.ask.skill.1234abcd-12ab-abcd-1234-123456789012", + optionValidLegacy: "amzn1.echo-sdk-ams.app.1234abcd-12ab-abcd-1234-123456789012", optionInvalid: "amzn1.ask.skill", }; const skillRegex = [ { type: "REGEX", - regex: "^amzn\\d\\.ask\\.skill\\.\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}$", + regex: "^amzn\\d((\\.ask\\.skill)|(\\.echo-sdk-ams\\.app))\\.\\w{8}-\\w{4}-\\w{4}-\\w{4}-\\w{12}$", }, ]; @@ -130,6 +131,10 @@ describe("Command test - Option validator test", () => { optionValidator.validateOptionRules(skillOptions, "optionValid", skillRegex); }).not.throw(); + expect(() => { + optionValidator.validateOptionRules(skillOptions, "optionValidLegacy", skillRegex); + }).not.throw(); + // invalid regex expectations expect(() => { optionValidator.validateOptionRules(skillOptions, "optionInvalid", skillRegex); From abbdc574df25757990d2101d931cc6d063f64af2 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 9 Aug 2023 12:51:26 -0400 Subject: [PATCH 13/29] fix: display warning object message correctly (#488) --- lib/view/messenger.js | 6 +-- test/unit/view/messenger-test.js | 67 +++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/lib/view/messenger.js b/lib/view/messenger.js index e60dc03c..7558e31a 100644 --- a/lib/view/messenger.js +++ b/lib/view/messenger.js @@ -152,14 +152,15 @@ class Messenger { * @param {*} data the message to display */ warn(data) { + const msg = data.constructor.name === "Error" ? data.message : jsonViewer.toString(data); const operation = "WARN"; this.buffer({ time: Messenger.getTime(), operation, level: MESSAGE_CATEGORY_MAPPING[operation].level, - msg: data, + msg, }); - this.displayMessage(operation, data); + this.displayMessage(operation, msg); } /** @@ -186,7 +187,6 @@ class Messenger { */ fatal(data) { const msg = data.constructor.name === "Error" ? data.stack.substring(7) : data; - const operation = "FATAL"; this.buffer({ time: Messenger.getTime(), diff --git a/test/unit/view/messenger-test.js b/test/unit/view/messenger-test.js index b59f1da9..e5ea2ca5 100644 --- a/test/unit/view/messenger-test.js +++ b/test/unit/view/messenger-test.js @@ -137,6 +137,42 @@ describe("View test - messenger file test", () => { expect(stub.args[0][0]).equal(chalk`{bold.yellow [Warn]: ${TEST_MESSAGE}}`); }); + it("| warn function correctly push error objects to buffer and display", () => { + const stub = sinon.stub(console, "warn"); + const expectedMessage = jsonView.toString(TEST_ERROR_WITH_STACK); + const expectedItem = { + time: TEST_TIME, + operation: "WARN", + level: 40, + msg: expectedMessage, + }; + + // call + Messenger.getInstance().warn(TEST_ERROR_WITH_STACK); + + // verify + expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); + expect(stub.args[0][0]).equal(chalk`{bold.yellow [Warn]: ${expectedMessage}}`); + }); + + it("| warn function correctly push error message to buffer and display", () => { + const stub = sinon.stub(console, "warn"); + const expectedMessage = TEST_ERROR_OBJ.message; + const expectedItem = { + time: TEST_TIME, + operation: "WARN", + level: 40, + msg: expectedMessage, + }; + + // call + Messenger.getInstance().warn(TEST_ERROR_OBJ); + + // verify + expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); + expect(stub.args[0][0]).equal(chalk`{bold.yellow [Warn]: ${expectedMessage}}`); + }); + it("| error function correctly push message to buffer and display", () => { const stub = sinon.stub(console, "error"); const expectedItem = { @@ -156,12 +192,12 @@ describe("View test - messenger file test", () => { it("| error function correctly push error objects to buffer and display", () => { const stub = sinon.stub(console, "error"); - const expectedError = jsonView.toString(TEST_ERROR_WITH_STACK); + const expectedMessage = jsonView.toString(TEST_ERROR_WITH_STACK); const expectedItem = { time: TEST_TIME, operation: "ERROR", level: 50, - msg: expectedError, + msg: expectedMessage, }; // call @@ -169,7 +205,25 @@ describe("View test - messenger file test", () => { // verify expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); - expect(stub.args[0][0]).equal(chalk`{bold.red [Error]: ${expectedError}}`); + expect(stub.args[0][0]).equal(chalk`{bold.red [Error]: ${expectedMessage}}`); + }); + + it("| error function correctly push error message to buffer and display", () => { + const stub = sinon.stub(console, "error"); + const expectedMessage = TEST_ERROR_OBJ.message; + const expectedItem = { + time: TEST_TIME, + operation: "ERROR", + level: 50, + msg: expectedMessage, + }; + + // call + Messenger.getInstance().error(TEST_ERROR_OBJ); + + // verify + expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); + expect(stub.args[0][0]).equal(chalk`{bold.red [Error]: ${expectedMessage}}`); }); it("| fatal function correctly push message to buffer and display", () => { @@ -191,11 +245,12 @@ describe("View test - messenger file test", () => { it("| fatal function correctly push error stack to buffer and display", () => { const stub = sinon.stub(console, "error"); + const expectedMessage = TEST_ERROR_OBJ.stack.substring(7); const expectedItem = { time: TEST_TIME, operation: "FATAL", level: 60, - msg: TEST_ERROR_OBJ.stack.substring(7), + msg: expectedMessage, }; // call @@ -203,7 +258,7 @@ describe("View test - messenger file test", () => { // verify expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); - expect(stub.args[0][0]).equal(chalk`{bold.rgb(128, 0, 0) [Fatal]: ${TEST_ERROR_OBJ.stack.substring(7)}}`); + expect(stub.args[0][0]).equal(chalk`{bold.rgb(128, 0, 0) [Fatal]: ${expectedMessage}}`); }); it("| trace function correctly push message to buffer and write to file with complete message", () => { @@ -229,7 +284,7 @@ describe("View test - messenger file test", () => { statusMessage: TEST_STATUS_MESSAGE, headers: TEST_HEADERS, }, - error: "TEST_ERROR", + error: TEST_ERROR, "request-id": TEST_REQUEST_ID, body: TEST_BODY, }; From 4b6ac2ff8cd8b6511bdcf42938f05518ecf31361 Mon Sep 17 00:00:00 2001 From: Jeremy Date: Wed, 9 Aug 2023 12:51:40 -0400 Subject: [PATCH 14/29] fix: deploy skill package when icon uri path relative to its asset files (#487) --- .../skill-infrastructure-controller/index.js | 45 +++++-------------- .../skill-metadata-controller/index.js | 10 ++--- lib/model/manifest.js | 10 +++++ .../aws-profile-setup-helper-test.js | 1 + .../hosted-skill-controller/index-test.js | 1 + .../skill-infrastructure-controller-test.js | 36 +++++++++++---- .../skill-metadata-controller-test.js | 11 +++-- test/unit/model/manifest-test.js | 32 +++++++++++++ 8 files changed, 96 insertions(+), 50 deletions(-) diff --git a/lib/controllers/skill-infrastructure-controller/index.js b/lib/controllers/skill-infrastructure-controller/index.js index 4870eb75..4f65c823 100644 --- a/lib/controllers/skill-infrastructure-controller/index.js +++ b/lib/controllers/skill-infrastructure-controller/index.js @@ -8,6 +8,7 @@ const MultiTasksView = require("../../view/multi-tasks-view"); const Messenger = require("../../view/messenger"); const hashUtils = require("../../utils/hash-utils"); const stringUtils = require("../../utils/string-utils"); +const profileHelper = require("../../utils/profile-helper"); const CONSTANTS = require("../../utils/constants"); const {isAcSkill, syncManifest} = require("../../utils/ac-util"); const acdl = require("@alexa/acdl"); @@ -314,46 +315,24 @@ module.exports = class SkillInfrastructureController { * @param {Function} callback */ _ensureSkillManifestGotUpdated(callback) { - let skillMetaController; + let vendorId; try { - skillMetaController = new SkillMetadataController({profile: this.profile, doDebug: this.doDebug}); + vendorId = profileHelper.resolveVendorId(this.profile); } catch (err) { return callback(err); } - skillMetaController.updateSkillManifest((err) => { - callback(err); - }); - } - - /** - * Poll skill's manifest status until the status is not IN_PROGRESS. - * - * @param {Object} smapiClient - * @param {String} skillId - * @param {Function} callback - */ - _pollSkillStatus(smapiClient, skillId, callback) { - const retryConfig = { - base: 2000, - factor: 1.12, - maxRetry: 50, - }; - const retryCall = (loopCallback) => { - smapiClient.skill.getSkillStatus(skillId, [CONSTANTS.SKILL.RESOURCES.MANIFEST], (statusErr, statusResponse) => { - if (statusErr) { - return loopCallback(statusErr); - } - if (statusResponse.statusCode >= 300) { - return loopCallback(jsonView.toString(statusResponse.body)); - } - loopCallback(null, statusResponse); + // deploy skill package if the skill manifest has icon file uri, otherwise update the skill manifest + if (Manifest.getInstance().hasIconFileUri()) { + this.skillMetaController.deploySkillPackage(vendorId, this.ignoreHash, (err) => { + callback(err); }); - }; - const shouldRetryCondition = (retryResponse) => - R.view(R.lensPath(["body", "manifest", "lastUpdateRequest", "status"]), retryResponse) === CONSTANTS.SKILL.SKILL_STATUS.IN_PROGRESS; - retryUtils.retry(retryConfig, retryCall, shouldRetryCondition, (err, res) => callback(err, err ? null : res)); + } else { + this.skillMetaController.updateSkillManifest((err) => { + callback(err); + }); + } } /** diff --git a/lib/controllers/skill-metadata-controller/index.js b/lib/controllers/skill-metadata-controller/index.js index 051abf87..3c486683 100644 --- a/lib/controllers/skill-metadata-controller/index.js +++ b/lib/controllers/skill-metadata-controller/index.js @@ -284,12 +284,11 @@ module.exports = class SkillMetadataController { * @param {error} callback */ updateSkillManifest(callback) { - const smapiClient = new SmapiClient({profile: this.profile, doDebug: this.doDebug}); const skillId = ResourcesConfig.getInstance().getSkillId(this.profile); const content = Manifest.getInstance().content; const stage = CONSTANTS.SKILL.STAGE.DEVELOPMENT; - smapiClient.skill.manifest.updateManifest(skillId, stage, content, null, (updateErr, updateResponse) => { + this.smapiClient.skill.manifest.updateManifest(skillId, stage, content, null, (updateErr, updateResponse) => { if (updateErr) { return callback(updateErr); } @@ -298,7 +297,7 @@ module.exports = class SkillMetadataController { } // poll manifest status until finish - this._pollSkillManifestStatus(smapiClient, skillId, (pollErr, pollResponse) => { + this._pollSkillManifestStatus(skillId, (pollErr, pollResponse) => { if (pollErr) { return callback(pollErr); } @@ -317,11 +316,10 @@ module.exports = class SkillMetadataController { /** * Poll skill's manifest status until the status is not IN_PROGRESS. * - * @param {Object} smapiClient * @param {String} skillId * @param {Function} callback */ - _pollSkillManifestStatus(smapiClient, skillId, callback) { + _pollSkillManifestStatus(skillId, callback) { const retryConfig = { base: 2000, factor: 1.12, @@ -329,7 +327,7 @@ module.exports = class SkillMetadataController { }; const retryCall = (loopCallback) => { - smapiClient.skill.getSkillStatus(skillId, [CONSTANTS.SKILL.RESOURCES.MANIFEST], (statusErr, statusResponse) => { + this.smapiClient.skill.getSkillStatus(skillId, [CONSTANTS.SKILL.RESOURCES.MANIFEST], (statusErr, statusResponse) => { if (statusErr) { return loopCallback(statusErr); } diff --git a/lib/model/manifest.js b/lib/model/manifest.js index 8a2f28eb..99e65f6a 100644 --- a/lib/model/manifest.js +++ b/lib/model/manifest.js @@ -128,4 +128,14 @@ module.exports = class Manifest extends ConfigFile { setEventsSubscriptions(subscriptions) { this.setProperty(["manifest", Manifest.endpointTypes.EVENTS, "subscriptions"], subscriptions); } + + /** + * Returns if skill manifest has icon file uri + * @return {Boolean} + */ + hasIconFileUri() { + return Object.values(this.getPublishingLocales()) + .flatMap((locale) => Object.entries(locale)) + .some(([key, value]) => (key === "smallIconUri" || key === "largeIconUri") && value.startsWith("file://")); + } }; diff --git a/test/unit/commands/configure/aws-profile-setup-helper-test.js b/test/unit/commands/configure/aws-profile-setup-helper-test.js index cd0a4ad7..6df24dc4 100644 --- a/test/unit/commands/configure/aws-profile-setup-helper-test.js +++ b/test/unit/commands/configure/aws-profile-setup-helper-test.js @@ -213,6 +213,7 @@ describe("Command: Configure - AWS profile setup helper test", () => { sinon.stub(ui, "addNewCredentials").callsArgWith(0, null, TEST_CREDENTIALS); sinon.stub(awsProfileHandler, "addProfile"); sinon.stub(profileHelper, "setupProfile"); + sinon.useFakeTimers().tickAsync(CONSTANTS.CONFIGURATION.OPEN_BROWSER_DELAY); // call proxyHelper.setupAwsProfile({askProfile: TEST_PROFILE, needBrowser: true}, (err, awsProfile) => { diff --git a/test/unit/controller/hosted-skill-controller/index-test.js b/test/unit/controller/hosted-skill-controller/index-test.js index 9e431f3c..b77a0b8a 100644 --- a/test/unit/controller/hosted-skill-controller/index-test.js +++ b/test/unit/controller/hosted-skill-controller/index-test.js @@ -392,6 +392,7 @@ describe("Controller test - hosted skill controller test", () => { const stubTestFunc = sinon.stub(httpClient, "request"); // stub getSkillStatus smapi request stubTestFunc.onCall(0).callsArgWith(3, null, TEST_STATUS_RESPONSE_0); stubTestFunc.onCall(1).callsArgWith(3, null, TEST_STATUS_RESPONSE_1); + sinon.useFakeTimers().tickAsync(CONSTANTS.CONFIGURATION.RETRY.MAX_RETRY_INTERVAL); // call hostedSkillController.checkSkillStatus(TEST_SKILL_ID, (err, res) => { expect(err).equal(null); diff --git a/test/unit/controller/skill-infrastructure-controller-test.js b/test/unit/controller/skill-infrastructure-controller-test.js index bf9abc46..11276d22 100644 --- a/test/unit/controller/skill-infrastructure-controller-test.js +++ b/test/unit/controller/skill-infrastructure-controller-test.js @@ -763,40 +763,60 @@ describe("Controller test - skill infrastructure controller test", () => { describe("# test class method: _ensureSkillManifestGotUpdated", () => { const skillInfraController = new SkillInfrastructureController(TEST_CONFIGURATION); + let deploySkillPackageStub, updateSkillManifestStub; beforeEach(() => { - new ResourcesConfig(FIXTURE_RESOURCES_CONFIG_FILE_PATH); new Manifest(FIXTURE_MANIFEST_FILE_PATH); - sinon.stub(fs, "writeFileSync"); + deploySkillPackageStub = sinon.stub(SkillMetadataController.prototype, "deploySkillPackage").yields(); + updateSkillManifestStub = sinon.stub(SkillMetadataController.prototype, "updateSkillManifest").yields(); }); afterEach(() => { - ResourcesConfig.dispose(); Manifest.dispose(); sinon.restore(); }); - it("| update skill manifest fails, expect error called back", (done) => { + it("| resolve vendor id fails, expect error called back", (done) => { + // setup + const TEST_ERROR = new Error("error"); + sinon.stub(profileHelper, "resolveVendorId").throws(TEST_ERROR); + // call + skillInfraController._ensureSkillManifestGotUpdated((err, res) => { + // verify + expect(res).equal(undefined); + expect(err).equal(TEST_ERROR); + expect(deploySkillPackageStub.calledOnce).equal(false); + expect(updateSkillManifestStub.calledOnce).equal(false); + done(); + }); + }); + + it("| skill manifest has no icon file uri, expect updateSkillManifest to be called with no error", (done) => { // setup sinon.stub(profileHelper, "resolveVendorId"); - sinon.stub(SkillMetadataController.prototype, "updateSkillManifest").yields("error"); + sinon.stub(Manifest.prototype, "hasIconFileUri").returns(false); // call skillInfraController._ensureSkillManifestGotUpdated((err, res) => { // verify expect(res).equal(undefined); - expect(err).equal("error"); + expect(err).equal(undefined); + expect(deploySkillPackageStub.calledOnce).equal(false); + expect(updateSkillManifestStub.calledOnce).equal(true); done(); }); }); - it("| update skill manifest succeeds, expect call back with no error", (done) => { + it("| skill manifest has icon file uri, expect deploySkillPackage to be called with no error", (done) => { + // setup sinon.stub(profileHelper, "resolveVendorId"); - sinon.stub(SkillMetadataController.prototype, "updateSkillManifest").yields(); + sinon.stub(Manifest.prototype, "hasIconFileUri").returns(true); // call skillInfraController._ensureSkillManifestGotUpdated((err, res) => { // verify expect(res).equal(undefined); expect(err).equal(undefined); + expect(deploySkillPackageStub.calledOnce).equal(true); + expect(updateSkillManifestStub.calledOnce).equal(false); done(); }); }); diff --git a/test/unit/controller/skill-metadata-controller-test.js b/test/unit/controller/skill-metadata-controller-test.js index bd7809c9..f5c4f096 100644 --- a/test/unit/controller/skill-metadata-controller-test.js +++ b/test/unit/controller/skill-metadata-controller-test.js @@ -1104,6 +1104,7 @@ describe("Controller test - skill metadata controller test", () => { it("| poll status retries with getImportStatus warnings, expects warnings logged once", (done) => { // setup + sinon.useFakeTimers().tickAsync(CONSTANTS.CONFIGURATION.RETRY.MAX_RETRY_INTERVAL); requestStub .onCall(0) .callsArgWith(3, null, smapiImportStatusResponseWithWarningsInProgress) @@ -1373,6 +1374,7 @@ describe("Controller test - skill metadata controller test", () => { it("| poll import status with multiple retries, expect callback with correct response", (done) => { // setup + sinon.useFakeTimers().tickAsync(CONSTANTS.CONFIGURATION.RETRY.MAX_RETRY_INTERVAL); requestStub .onCall(0) .callsArgWith(3, null, smapiImportStatusResponseInProgress) @@ -1424,6 +1426,7 @@ describe("Controller test - skill metadata controller test", () => { it("| poll import status with getSkillStatus build failures", (done) => { // setup + sinon.useFakeTimers().tickAsync(CONSTANTS.CONFIGURATION.RETRY.MAX_RETRY_INTERVAL); requestStub .onCall(0) .callsArgWith(3, null, smapiImportStatusResponseInProgress) @@ -1445,6 +1448,7 @@ describe("Controller test - skill metadata controller test", () => { it("| poll status smapi calls return success, expect GetSkillStatus calls", (done) => { // setup + sinon.useFakeTimers().tickAsync(CONSTANTS.CONFIGURATION.RETRY.MAX_RETRY_INTERVAL); requestStub .onCall(0) .callsArgWith(3, null, smapiImportStatusResponseInProgress) @@ -1465,6 +1469,7 @@ describe("Controller test - skill metadata controller test", () => { it("| poll status smapi calls return empty SkillID, expect no GetSkillStatus calls", (done) => { // setup + sinon.useFakeTimers().tickAsync(CONSTANTS.CONFIGURATION.RETRY.MAX_RETRY_INTERVAL); requestStub .onCall(0) .callsArgWith(3, null, smapiImportStatusResponse200EmptySkillID) @@ -1586,7 +1591,7 @@ describe("Controller test - skill metadata controller test", () => { it("| update manifest callback with error when poll skill status fails", (done) => { // setup sinon.stub(httpClient, "request").callsArgWith(3, null, {}); - sinon.stub(SkillMetadataController.prototype, "_pollSkillManifestStatus").callsArgWith(2, "TEST_ERROR"); + sinon.stub(SkillMetadataController.prototype, "_pollSkillManifestStatus").callsArgWith(1, "TEST_ERROR"); // call skillMetaController.updateSkillManifest((err, res) => { @@ -1608,7 +1613,7 @@ describe("Controller test - skill metadata controller test", () => { }, }; - sinon.stub(SkillMetadataController.prototype, "_pollSkillManifestStatus").callsArgWith(2, undefined, pollResponse); + sinon.stub(SkillMetadataController.prototype, "_pollSkillManifestStatus").callsArgWith(1, undefined, pollResponse); sinon.stub(httpClient, "request").callsArgWith(3, null, {}); // call @@ -1631,7 +1636,7 @@ describe("Controller test - skill metadata controller test", () => { }, }; - sinon.stub(SkillMetadataController.prototype, "_pollSkillManifestStatus").callsArgWith(2, undefined, pollResponse); + sinon.stub(SkillMetadataController.prototype, "_pollSkillManifestStatus").callsArgWith(1, undefined, pollResponse); sinon.stub(httpClient, "request").callsArgWith(3, null, {}); // call diff --git a/test/unit/model/manifest-test.js b/test/unit/model/manifest-test.js index a9663acb..c3190fa8 100644 --- a/test/unit/model/manifest-test.js +++ b/test/unit/model/manifest-test.js @@ -211,5 +211,37 @@ describe("Model test - manifest file test", () => { Manifest.dispose(); }); }); + + describe("# verify function hasIconFileUri", () => { + it("| icon https uri, expect false", () => { + new Manifest(MANIFEST_FILE); + Manifest.getInstance().setPublishingLocales({ + "en-US": { + name: "skillName", + summary: "one sentence description", + description: "skill description", + smallIconUri: "https://some.url.com/images/en-US_smallIcon.png", + largeIconUri: "https://some.url.com/images/en-US_largeIconUri.png", + }, + }); + expect(Manifest.getInstance().hasIconFileUri()).equal(false); + Manifest.dispose(); + }); + + it("| icon file uri relative to skill package assets, expect true", () => { + new Manifest(MANIFEST_FILE); + Manifest.getInstance().setPublishingLocales({ + "en-US": { + name: "skillName", + summary: "one sentence description", + description: "skill description", + smallIconUri: "file://assets/images/en-US_smallIcon.png", + largeIconUri: "file://assets/images/en-US_largeIconUri.png", + }, + }); + expect(Manifest.getInstance().hasIconFileUri()).equal(true); + Manifest.dispose(); + }); + }); }); }); From cd44f7270bc58a2151cdad31ac16025dbbcfc590 Mon Sep 17 00:00:00 2001 From: Camden Foucht Date: Wed, 9 Aug 2023 10:57:27 -0700 Subject: [PATCH 15/29] chore(release): 2.30.5 (#490) --- CHANGELOG.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a2bc972..114e71ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.30.5](https://github.com/alexa/ask-cli/compare/v2.30.4...v2.30.5) (2023-08-09) + + +### Bug Fixes + +* deploy skill package when icon uri path relative to its asset files ([#487](https://github.com/alexa/ask-cli/issues/487)) ([4b6ac2f](https://github.com/alexa/ask-cli/commit/4b6ac2ff8cd8b6511bdcf42938f05518ecf31361)) +* display warning object message correctly ([#488](https://github.com/alexa/ask-cli/issues/488)) ([abbdc57](https://github.com/alexa/ask-cli/commit/abbdc574df25757990d2101d931cc6d063f64af2)) +* skill-id option for legacy skills not supported ([#489](https://github.com/alexa/ask-cli/issues/489)) ([3a71908](https://github.com/alexa/ask-cli/commit/3a719081e613f5a03d119b2cd8e32f35df15825e)) ### [2.30.4](https://github.com/alexa/ask-cli/compare/v2.30.3...v2.30.4) (2023-06-23) diff --git a/package-lock.json b/package-lock.json index ef8b2848..a3f738c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ask-cli", - "version": "2.30.4", + "version": "2.30.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ask-cli", - "version": "2.30.4", + "version": "2.30.5", "hasInstallScript": true, "license": "Apache 2.0", "dependencies": { diff --git a/package.json b/package.json index c7876916..be14ab39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ask-cli", - "version": "2.30.4", + "version": "2.30.5", "description": "Alexa Skills Kit (ASK) Command Line Interfaces", "bin": { "ask": "dist/bin/ask.js" From a0d2f19faeee36523be701522585272cff2a2d6d Mon Sep 17 00:00:00 2001 From: jsetton Date: Wed, 9 Aug 2023 16:22:53 -0400 Subject: [PATCH 16/29] fix: display message property for all error instance types --- .../skill-metadata-controller/index.js | 2 +- lib/view/messenger.js | 6 ++-- test/unit/view/messenger-test.js | 33 ++++++++----------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/lib/controllers/skill-metadata-controller/index.js b/lib/controllers/skill-metadata-controller/index.js index 3c486683..c1df114d 100644 --- a/lib/controllers/skill-metadata-controller/index.js +++ b/lib/controllers/skill-metadata-controller/index.js @@ -463,7 +463,7 @@ module.exports = class SkillMetadataController { if (shouldPrintWarning && importStatusResponse.warnings.length > 0) { importStatusResponse.warnings.forEach((warning) => { - Messenger.getInstance().warn(warning); + Messenger.getInstance().warn(warning.message); }); shouldPrintWarning = false; } diff --git a/lib/view/messenger.js b/lib/view/messenger.js index 7558e31a..0247ca27 100644 --- a/lib/view/messenger.js +++ b/lib/view/messenger.js @@ -152,7 +152,7 @@ class Messenger { * @param {*} data the message to display */ warn(data) { - const msg = data.constructor.name === "Error" ? data.message : jsonViewer.toString(data); + const msg = data instanceof Error ? data.message : jsonViewer.toString(data); const operation = "WARN"; this.buffer({ time: Messenger.getTime(), @@ -169,7 +169,7 @@ class Messenger { * @param {*} data the message to display */ error(data) { - const msg = data.constructor.name === "Error" ? data.message : jsonViewer.toString(data); + const msg = data instanceof Error ? data.message : jsonViewer.toString(data); const operation = "ERROR"; this.buffer({ time: Messenger.getTime(), @@ -186,7 +186,7 @@ class Messenger { * @param {*} data the message to display */ fatal(data) { - const msg = data.constructor.name === "Error" ? data.stack.substring(7) : data; + const msg = data instanceof Error ? data.stack.substring(7) : data; const operation = "FATAL"; this.buffer({ time: Messenger.getTime(), diff --git a/test/unit/view/messenger-test.js b/test/unit/view/messenger-test.js index e5ea2ca5..76be0af4 100644 --- a/test/unit/view/messenger-test.js +++ b/test/unit/view/messenger-test.js @@ -8,14 +8,9 @@ const jsonView = require("../../../lib/view/json-view"); describe("View test - messenger file test", () => { const TEST_MESSAGE = "TEST_MESSAGE"; - const TEST_STACK = "TEST_STACK"; - const TEST_ERROR_WITH_STACK = { - message: TEST_MESSAGE, - stack: TEST_STACK, - foo: 2, - } + const TEST_OBJECT = { foo: 1, bar: 2 }; + const TEST_ERROR = new Error("TEST_ERROR"); const TEST_TIME = "TEST_TIME"; - const TEST_ERROR_OBJ = new Error("TEST_ERROR"); describe("# inspect correctness for constructor, getInstance and dispose", () => { beforeEach(() => { @@ -137,9 +132,9 @@ describe("View test - messenger file test", () => { expect(stub.args[0][0]).equal(chalk`{bold.yellow [Warn]: ${TEST_MESSAGE}}`); }); - it("| warn function correctly push error objects to buffer and display", () => { + it("| warn function correctly push stringify object to buffer and display", () => { const stub = sinon.stub(console, "warn"); - const expectedMessage = jsonView.toString(TEST_ERROR_WITH_STACK); + const expectedMessage = jsonView.toString(TEST_OBJECT); const expectedItem = { time: TEST_TIME, operation: "WARN", @@ -148,7 +143,7 @@ describe("View test - messenger file test", () => { }; // call - Messenger.getInstance().warn(TEST_ERROR_WITH_STACK); + Messenger.getInstance().warn(TEST_OBJECT); // verify expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); @@ -157,7 +152,7 @@ describe("View test - messenger file test", () => { it("| warn function correctly push error message to buffer and display", () => { const stub = sinon.stub(console, "warn"); - const expectedMessage = TEST_ERROR_OBJ.message; + const expectedMessage = TEST_ERROR.message; const expectedItem = { time: TEST_TIME, operation: "WARN", @@ -166,7 +161,7 @@ describe("View test - messenger file test", () => { }; // call - Messenger.getInstance().warn(TEST_ERROR_OBJ); + Messenger.getInstance().warn(TEST_ERROR); // verify expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); @@ -190,9 +185,9 @@ describe("View test - messenger file test", () => { expect(stub.args[0][0]).equal(chalk`{bold.red [Error]: ${TEST_MESSAGE}}`); }); - it("| error function correctly push error objects to buffer and display", () => { + it("| error function correctly push stringify object to buffer and display", () => { const stub = sinon.stub(console, "error"); - const expectedMessage = jsonView.toString(TEST_ERROR_WITH_STACK); + const expectedMessage = jsonView.toString(TEST_OBJECT); const expectedItem = { time: TEST_TIME, operation: "ERROR", @@ -201,7 +196,7 @@ describe("View test - messenger file test", () => { }; // call - Messenger.getInstance().error(TEST_ERROR_WITH_STACK); + Messenger.getInstance().error(TEST_OBJECT); // verify expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); @@ -210,7 +205,7 @@ describe("View test - messenger file test", () => { it("| error function correctly push error message to buffer and display", () => { const stub = sinon.stub(console, "error"); - const expectedMessage = TEST_ERROR_OBJ.message; + const expectedMessage = TEST_ERROR.message; const expectedItem = { time: TEST_TIME, operation: "ERROR", @@ -219,7 +214,7 @@ describe("View test - messenger file test", () => { }; // call - Messenger.getInstance().error(TEST_ERROR_OBJ); + Messenger.getInstance().error(TEST_ERROR); // verify expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); @@ -245,7 +240,7 @@ describe("View test - messenger file test", () => { it("| fatal function correctly push error stack to buffer and display", () => { const stub = sinon.stub(console, "error"); - const expectedMessage = TEST_ERROR_OBJ.stack.substring(7); + const expectedMessage = TEST_ERROR.stack.substring(7); const expectedItem = { time: TEST_TIME, operation: "FATAL", @@ -254,7 +249,7 @@ describe("View test - messenger file test", () => { }; // call - Messenger.getInstance().fatal(TEST_ERROR_OBJ); + Messenger.getInstance().fatal(TEST_ERROR); // verify expect(Messenger.getInstance()._buffer[0]).deep.equal(expectedItem); From 12c0f97afe9150e66b681aa005160e6d2fcad886 Mon Sep 17 00:00:00 2001 From: jsetton Date: Tue, 15 Aug 2023 16:49:51 -0400 Subject: [PATCH 17/29] chore: switch to package `@smithy/shared-ini-file-loader` --- lib/clients/aws-client/aws-util.ts | 2 +- package-lock.json | 75 ++++++++++++------- package.json | 2 +- test/unit/clients/aws-client/aws-util-test.ts | 2 +- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/lib/clients/aws-client/aws-util.ts b/lib/clients/aws-client/aws-util.ts index 8601150e..c54da507 100644 --- a/lib/clients/aws-client/aws-util.ts +++ b/lib/clients/aws-client/aws-util.ts @@ -1,4 +1,4 @@ -import {DEFAULT_PROFILE, parseKnownFiles} from "@aws-sdk/shared-ini-file-loader"; +import {DEFAULT_PROFILE, parseKnownFiles} from "@smithy/shared-ini-file-loader"; import fs from "fs-extra"; import os from "os"; import path from "path"; diff --git a/package-lock.json b/package-lock.json index a3f738c9..4f05aeac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,8 @@ "@aws-sdk/client-lambda": "^3.306.0", "@aws-sdk/client-s3": "^3.306.0", "@aws-sdk/credential-providers": "^3.306.0", - "@aws-sdk/shared-ini-file-loader": "^3.306.0", "@aws-sdk/types": "^3.306.0", + "@smithy/shared-ini-file-loader": "^2.0.4", "adm-zip": "^0.5.9", "archiver": "^5.3.0", "ask-smapi-model": "^1.23.0", @@ -3173,6 +3173,29 @@ "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", "dev": true }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.4.tgz", + "integrity": "sha512-091yneupXnSqvAU+vLG7h0g4QRRO6TjulpECXYVU6yW/LiNp7QE533DBpaphmbtI6tTC4EfGrhn35gTa0w+GQg==", + "dependencies": { + "@smithy/types": "^2.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.2.1.tgz", + "integrity": "sha512-6nyDOf027ZeJiQVm6PXmLm7dR+hR2YJUkr4VwUniXA8xZUGAu5Mk0zfx2BPFrt+e5YauvlIqQoH0CsrM4tLkfg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@stoplight/http-spec": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/@stoplight/http-spec/-/http-spec-5.9.3.tgz", @@ -3931,6 +3954,22 @@ "node": ">= 6" } }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -5938,8 +5977,8 @@ "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", "dev": true, "dependencies": { - "is-text-path": "^1.0.1", "JSONStream": "^1.0.4", + "is-text-path": "^1.0.1", "lodash": "^4.17.15", "meow": "^8.0.0", "split2": "^3.0.0", @@ -9093,22 +9132,6 @@ "integrity": "sha512-2bf/1crAmPpsmj1I6rDT6W0SOErkrNBpb555xNWcMVWYrX6VnXpG0GRMQ2shvOHwafpfse8q0gnzPFYVH6Tqdg==", "dev": true }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -14403,14 +14426,6 @@ "node": ">=10" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -14464,6 +14479,14 @@ "node": ">=8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/stringify-package": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", diff --git a/package.json b/package.json index be14ab39..395c037d 100644 --- a/package.json +++ b/package.json @@ -51,8 +51,8 @@ "@aws-sdk/client-lambda": "^3.306.0", "@aws-sdk/client-s3": "^3.306.0", "@aws-sdk/credential-providers": "^3.306.0", - "@aws-sdk/shared-ini-file-loader": "^3.306.0", "@aws-sdk/types": "^3.306.0", + "@smithy/shared-ini-file-loader": "^2.0.4", "adm-zip": "^0.5.9", "archiver": "^5.3.0", "ask-smapi-model": "^1.23.0", diff --git a/test/unit/clients/aws-client/aws-util-test.ts b/test/unit/clients/aws-client/aws-util-test.ts index 23b61876..d90215cc 100644 --- a/test/unit/clients/aws-client/aws-util-test.ts +++ b/test/unit/clients/aws-client/aws-util-test.ts @@ -14,7 +14,7 @@ describe("Clients test - aws util test", () => { beforeEach(() => { parseKnownFilesStub = sinon.stub(); awsUtil = proxyquire("../../../../lib/clients/aws-client/aws-util", { - "@aws-sdk/shared-ini-file-loader": { + "@smithy/shared-ini-file-loader": { DEFAULT_PROFILE: TEST_AWS_DEFAULT_PROFILE, parseKnownFiles: parseKnownFilesStub, }, From 0ff077344cc89331275f7e97c3244abbdd116521 Mon Sep 17 00:00:00 2001 From: jsetton Date: Thu, 9 Nov 2023 19:24:34 -0500 Subject: [PATCH 18/29] chore: improve s3 error handling --- .../deploy-delegates/cfn-deployer/index.js | 13 ++++++++---- test/unit/builtins/cfn-deployer/index-test.js | 21 +++++++++++++++---- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/builtins/deploy-delegates/cfn-deployer/index.js b/lib/builtins/deploy-delegates/cfn-deployer/index.js index 917b14e5..d916c04d 100644 --- a/lib/builtins/deploy-delegates/cfn-deployer/index.js +++ b/lib/builtins/deploy-delegates/cfn-deployer/index.js @@ -32,8 +32,8 @@ async function bootstrap(options, callback) { fs.writeFileSync(templateLocation, templateContent); userConfig.templatePath = `./${path.posix.join("infrastructure", path.basename(workspacePath), SKILL_STACK_PUBLIC_FILE_NAME)}`; updatedUserConfig = R.set(R.lensPath(["awsRegion"]), awsDefaultRegion, userConfig); - } catch (e) { - return callback(e.message); + } catch (err) { + return callback(err.message); } callback(null, {userConfig: updatedUserConfig}); } @@ -96,7 +96,12 @@ async function _deploy(reporter, options, deployProgress) { templateBody.includes("SkillClientId") && templateBody.includes("SkillClientSecret") ? await helper.getSkillCredentials(skillId) : {}; // s3 upload - const uploadResult = await helper.uploadToS3(bucketName, bucketKey, code.codeBuild); + let uploadResult; + try { + uploadResult = await helper.uploadToS3(bucketName, bucketKey, code.codeBuild); + } catch (err) { + throw new CliCFNDeployerError(`Failed to upload code build to S3: ${err.message}`); + } deployProgress.deployState.s3 = { bucket: bucketName, @@ -212,7 +217,7 @@ function _getUserDefinedParameters(alexaRegion, userConfig) { Object.keys(parameters).forEach((key) => { if (reservedParametersKeys.has(key)) { const message = reservedParameters[key]; - throw new CliCFNDeployerError(`Cloud Formation parameter "${key}" is reserved. ${message}`); + throw new CliCFNDeployerError(`CloudFormation parameter "${key}" is reserved. ${message}`); } }); diff --git a/test/unit/builtins/cfn-deployer/index-test.js b/test/unit/builtins/cfn-deployer/index-test.js index eec36098..c44b8357 100644 --- a/test/unit/builtins/cfn-deployer/index-test.js +++ b/test/unit/builtins/cfn-deployer/index-test.js @@ -22,7 +22,7 @@ describe("Builtins test - cfn-deployer index test", () => { const endpointUri = "some endpoint uri"; const userDefinedParamKey = "someKey"; const userDefinedParamValue = "someValue"; - let deployStackStub, getAWSProfileStub; + let deployStackStub, getAWSProfileStub, uploadToS3Stub; describe("bootstrap", () => { const bootstrapOptions = { @@ -118,7 +118,7 @@ describe("Builtins test - cfn-deployer index test", () => { }; getAWSProfileStub = sinon.stub(awsUtil, "getAWSProfile").returns("some profile"); sinon.stub(Helper.prototype, "getSkillCredentials").resolves({clientId: "id", clientSecret: "secret"}); - sinon.stub(Helper.prototype, "uploadToS3").resolves({VersionId: s3VersionId}); + uploadToS3Stub = sinon.stub(Helper.prototype, "uploadToS3").resolves({VersionId: s3VersionId}); deployStackStub = sinon.stub(Helper.prototype, "deployStack"); }); @@ -227,13 +227,26 @@ describe("Builtins test - cfn-deployer index test", () => { }); }); + it("should throw error when failed to upload to s3", (done) => { + const errorMessage = "some error"; + uploadToS3Stub.rejects(new Error(errorMessage)); + + Deployer.invoke({}, deployOptions, (err, result) => { + expectedErrorOutput.resultMessage = + `The CloudFormation deploy failed for Alexa region "${alexaRegion}": Failed to upload code build to S3: ${errorMessage}`; + expect(err).eql(null); + expect(result).eql(expectedErrorOutput); + done(); + }); + }); + it("should throw error when reserved parameter is used", (done) => { deployOptions.userConfig.cfn.parameters.SkillId = "reserved parameter value"; Deployer.invoke({}, deployOptions, (err, result) => { expectedErrorOutput.resultMessage = `The CloudFormation deploy failed for Alexa region "${alexaRegion}": ` + - 'Cloud Formation parameter "SkillId" is reserved. Please use a different name.'; + 'CloudFormation parameter "SkillId" is reserved. Please use a different name.'; expect(err).eql(null); expect(result).eql(expectedErrorOutput); done(); @@ -245,7 +258,7 @@ describe("Builtins test - cfn-deployer index test", () => { Deployer.invoke({}, deployOptions, (err, result) => { expectedErrorOutput.resultMessage = - `The CloudFormation deploy failed for Alexa region "${alexaRegion}": ` + "The template path in userConfig must be provided."; + `The CloudFormation deploy failed for Alexa region "${alexaRegion}": The template path in userConfig must be provided.`; expect(err).eql(null); expect(result).eql(expectedErrorOutput); done(); From 9e92ee5bf9aedcba1f683c1db877d0a9c1442cae Mon Sep 17 00:00:00 2001 From: jsetton Date: Thu, 9 Nov 2023 19:31:44 -0500 Subject: [PATCH 19/29] fix: use npm registry https url --- lib/utils/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/constants.js b/lib/utils/constants.js index a7df95eb..baeb641f 100644 --- a/lib/utils/constants.js +++ b/lib/utils/constants.js @@ -1,7 +1,7 @@ const path = require("path"); module.exports.APPLICATION_NAME = "ask-cli"; -module.exports.NPM_REGISTRY_URL_BASE = "http://registry.npmjs.org"; +module.exports.NPM_REGISTRY_URL_BASE = "https://registry.npmjs.org"; module.exports.METRICS = { ENDPOINT: "https://client-telemetry.amazonalexa.com", From 56dd1fbf2e9e39e56aa248a41ee72e59f66ea1bb Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:06:07 -0800 Subject: [PATCH 20/29] chore: npm audit to update some of the dependencies --- package-lock.json | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4f05aeac..e7546f8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3954,22 +3954,6 @@ "node": ">= 6" } }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - }, - "bin": { - "JSONStream": "bin.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -5977,8 +5961,8 @@ "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", "dev": true, "dependencies": { - "JSONStream": "^1.0.4", "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", "lodash": "^4.17.15", "meow": "^8.0.0", "split2": "^3.0.0", @@ -9132,6 +9116,22 @@ "integrity": "sha512-2bf/1crAmPpsmj1I6rDT6W0SOErkrNBpb555xNWcMVWYrX6VnXpG0GRMQ2shvOHwafpfse8q0gnzPFYVH6Tqdg==", "dev": true }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/just-extend": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", @@ -14426,6 +14426,14 @@ "node": ">=10" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", @@ -14479,14 +14487,6 @@ "node": ">=8" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/stringify-package": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/stringify-package/-/stringify-package-1.0.1.tgz", From 64f56391d69953005e8fc4f0335de9d1c229e531 Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Mon, 20 Nov 2023 13:14:41 -0800 Subject: [PATCH 21/29] chore: add importId deploy status and timestamps to the ask states config file --- lib/commands/deploy/index.ts | 7 +++- .../skill-infrastructure-controller/index.js | 7 ++-- .../skill-metadata-controller/index.js | 3 ++ lib/model/resources-config/ask-states.js | 32 +++++++++++++++++++ lib/model/resources-config/index.js | 32 +++++++++++++++++++ .../skill-metadata-controller-test.js | 1 + .../model/regular-proj/.ask/ask-states.json | 10 ++++-- .../resources-config/resources-config-test.js | 24 ++++++++++++++ 8 files changed, 110 insertions(+), 6 deletions(-) diff --git a/lib/commands/deploy/index.ts b/lib/commands/deploy/index.ts index 81b97861..c236f76c 100644 --- a/lib/commands/deploy/index.ts +++ b/lib/commands/deploy/index.ts @@ -38,6 +38,8 @@ export default class DeployCommand extends AbstractCommand { try { profile = profileHelper.runtimeProfile(cmd.profile); new ResourcesConfig(path.join(process.cwd(), CONSTANTS.FILE_PATH.ASK_RESOURCES_JSON_CONFIG)); + ResourcesConfig.getInstance().setDeploymentStatus(profile, "IN_PROGRESS"); + ResourcesConfig.getInstance().write(); Messenger.getInstance().info(`Deploy configuration loaded from ${CONSTANTS.FILE_PATH.ASK_RESOURCES_JSON_CONFIG}`); helper.confirmProfile(profile); this._filterAlexaHostedSkill(profile); @@ -80,7 +82,7 @@ export default class DeployCommand extends AbstractCommand { // Save the deployment type to ask states ResourcesConfig.getInstance().setSkillMetaLastDeployType(profile, deploymentType); - + ResourcesConfig.getInstance().setDeploymentStatus(profile, "COMPLETE"); // Write updates back to resources file ResourcesConfig.getInstance().write(); Manifest.getInstance().write(); @@ -105,6 +107,9 @@ export default class DeployCommand extends AbstractCommand { }); }) .catch((err) => { + ResourcesConfig.getInstance().setDeploymentStatus(profile, "COMPLETE"); + // Write updates back to resources file + ResourcesConfig.getInstance().write(); Messenger.getInstance().error(err); return reject(err); }); diff --git a/lib/controllers/skill-infrastructure-controller/index.js b/lib/controllers/skill-infrastructure-controller/index.js index 4f65c823..cf7574df 100644 --- a/lib/controllers/skill-infrastructure-controller/index.js +++ b/lib/controllers/skill-infrastructure-controller/index.js @@ -148,7 +148,7 @@ module.exports = class SkillInfrastructureController { if (error) { // update partial successful deploy results to resources config if (result && !R.isEmpty(R.keys(result))) { - this._updateResourcesConfig(regionsList, result); + this._updateResourcesConfig(regionsList, result, error); } return callback(error); } @@ -296,7 +296,7 @@ module.exports = class SkillInfrastructureController { * * @param {Object} rawDeployResult deploy result from invoke: { $region: deploy-delegate's response } */ - _updateResourcesConfig(regionsList, rawDeployResult) { + _updateResourcesConfig(regionsList, rawDeployResult, error) { const curDeployState = ResourcesConfig.getInstance().getSkillInfraDeployState(this.profile) || {}; const newDeployState = {}; regionsList.forEach((alexaRegion) => { @@ -307,6 +307,9 @@ module.exports = class SkillInfrastructureController { } }); ResourcesConfig.getInstance().setSkillInfraDeployState(this.profile, newDeployState); + if (!error) { + ResourcesConfig.getInstance().setCodeLastDeployTimestamp(this.profile, `${(new Date()).toISOString()}`); + } ResourcesConfig.getInstance().write(); } diff --git a/lib/controllers/skill-metadata-controller/index.js b/lib/controllers/skill-metadata-controller/index.js index 3c486683..12d83574 100644 --- a/lib/controllers/skill-metadata-controller/index.js +++ b/lib/controllers/skill-metadata-controller/index.js @@ -188,6 +188,9 @@ module.exports = class SkillMetadataController { const importId = path.basename(importResponse.headers.location); if (importId) { this.getImportStatusPollView().displayImportId(importId); + ResourcesConfig.getInstance().setLastImportId(this.profile, importId); + ResourcesConfig.getInstance().setLastImportTimestamp(this.profile, `${(new Date()).toISOString()}`); + ResourcesConfig.getInstance().write(); } // 3.poll for the skill package import status this._pollImportStatus(importId, (pollErr, pollResponse) => { diff --git a/lib/model/resources-config/ask-states.js b/lib/model/resources-config/ask-states.js index aa0d99ed..ee49aaa6 100644 --- a/lib/model/resources-config/ask-states.js +++ b/lib/model/resources-config/ask-states.js @@ -52,6 +52,30 @@ module.exports = class AskStates extends ConfigFile { this.setProperty(["profiles", profile, "skillId"], skillId); } + getDeploymentStatus(profile) { + return this.getProperty(["profiles", profile, "deploymentStatus"]); + } + + setDeploymentStatus(profile, status) { + this.setProperty(["profiles", profile, "deploymentStatus"], status); + } + + getLastImportId(profile) { + return this.getProperty(["profiles", profile, "skillMetadata", "lastImportId"]); + } + + setLastImportId(profile, lastImportId) { + this.setProperty(["profiles", profile, "skillMetadata", "lastImportId"], lastImportId); + } + + getLastImportTimestamp(profile) { + return this.getProperty(["profiles", profile, "skillMetadata", "lastImportTimestamp"]); + } + + setLastImportTimestamp(profile, dateTimeStamp) { + this.setProperty(["profiles", profile, "skillMetadata", "lastImportTimestamp"], dateTimeStamp); + } + // Group for the "skillMetadata" getSkillMetaLastDeployHash(profile) { return this.getProperty(["profiles", profile, "skillMetadata", "lastDeployHash"]); @@ -92,6 +116,14 @@ module.exports = class AskStates extends ConfigFile { this.setProperty(["profiles", profile, "code", region, "lastDeployHash"], hash); } + getCodeLastDeployTimestamp(profile) { + return this.getProperty(["profiles", profile, "code", "lastDeployTimestamp"]); + } + + setCodeLastDeployTimestamp(profile, dateTimestamp) { + this.setProperty(["profiles", profile, "code", "lastDeployTimestamp"], dateTimestamp); + } + getCodeBuildByRegion(projRoot, codeSrc) { if (!codeSrc) { return null; diff --git a/lib/model/resources-config/index.js b/lib/model/resources-config/index.js index 9849c630..eaa1a3d4 100644 --- a/lib/model/resources-config/index.js +++ b/lib/model/resources-config/index.js @@ -68,6 +68,30 @@ module.exports = class ResourcesConfig { AskStates.getInstance().setSkillId(profile, skillId); } + getDeploymentStatus(profile) { + return AskStates.getInstance().getDeploymentStatus(profile); + } + + setDeploymentStatus(profile, status) { + AskStates.getInstance().setDeploymentStatus(profile, status); + } + + getLastImportId(profile) { + return AskStates.getInstance().getLastImportId(profile); + } + + setLastImportId(profile, importId) { + AskStates.getInstance().setLastImportId(profile, importId); + } + + getLastImportTimestamp(profile) { + return AskStates.getInstance().getLastImportTimestamp(profile); + } + + setLastImportTimestamp(profile, dateTimestamp) { + AskStates.getInstance().setLastImportTimestamp(profile, dateTimestamp); + } + // Group for the "skillMetadata" getSkillMetaSrc(profile) { return AskResources.getInstance().getSkillMetaSrc(profile); @@ -123,6 +147,14 @@ module.exports = class ResourcesConfig { AskStates.getInstance().setCodeLastDeployHashByRegion(profile, region, hash); } + getCodeLastDeployTimestamp(profile) { + return AskStates.getInstance().getCodeLastDeployTimestamp(profile); + } + + setCodeLastDeployTimestamp(profile, dateTimestamp) { + AskStates.getInstance().setCodeLastDeployTimestamp(profile, dateTimestamp); + } + getCodeRegions(profile) { return AskResources.getInstance().getCodeRegions(profile); } diff --git a/test/unit/controller/skill-metadata-controller-test.js b/test/unit/controller/skill-metadata-controller-test.js index f5c4f096..df18b25a 100644 --- a/test/unit/controller/skill-metadata-controller-test.js +++ b/test/unit/controller/skill-metadata-controller-test.js @@ -377,6 +377,7 @@ describe("Controller test - skill metadata controller test", () => { beforeEach(() => { skillMetaController = new SkillMetadataController(TEST_CONFIGURATION); + sinon.stub(ResourcesConfig.prototype, "write").returns(""); }); diff --git a/test/unit/fixture/model/regular-proj/.ask/ask-states.json b/test/unit/fixture/model/regular-proj/.ask/ask-states.json index 3307a494..3d74fd7f 100644 --- a/test/unit/fixture/model/regular-proj/.ask/ask-states.json +++ b/test/unit/fixture/model/regular-proj/.ask/ask-states.json @@ -5,7 +5,9 @@ "skillId": "amzn1.ask.skill.1234567890", "skillMetadata": { "lastDeployHash": "hash==", - "lastDeployType": "interaction-model" + "lastDeployType": "interaction-model", + "lastImportId": "amzn1.ask-package.import.123abc", + "lastImportTimestamp": "2023-11-21T04:01:31.047Z" }, "code": { "default": { @@ -16,7 +18,8 @@ }, "NA": { "lastDeployHash": "hash==" - } + }, + "lastDeployTimestamp": "2023-11-21T03:07:07.820Z" }, "skillInfrastructure": { "@ask-cli/cfn-deployer": { @@ -47,7 +50,8 @@ } } } - } + }, + "deploymentStatus": "COMPLETE" } } } \ No newline at end of file diff --git a/test/unit/model/resources-config/resources-config-test.js b/test/unit/model/resources-config/resources-config-test.js index a1208a34..9109397e 100644 --- a/test/unit/model/resources-config/resources-config-test.js +++ b/test/unit/model/resources-config/resources-config-test.js @@ -155,6 +155,18 @@ describe("Model test - resources config test", () => { newValue: "skillId new", oldValue: TEST_ASK_STATES.profiles[TEST_PROFILE].skillId, }, + { + field: "LastImportId", + params: [TEST_PROFILE], + newValue: "LastImportId new", + oldValue: TEST_ASK_STATES.profiles[TEST_PROFILE].skillMetadata.lastImportId, + }, + { + field: "LastImportTimestamp", + params: [TEST_PROFILE], + newValue: "LastImportTimestamp new", + oldValue: TEST_ASK_STATES.profiles[TEST_PROFILE].skillMetadata.lastImportTimestamp, + }, { field: "SkillMetaSrc", params: [TEST_PROFILE], @@ -167,6 +179,18 @@ describe("Model test - resources config test", () => { newValue: "==hash", oldValue: TEST_ASK_STATES.profiles[TEST_PROFILE].skillMetadata.lastDeployHash, }, + { + field: "CodeLastDeployTimestamp", + params: [TEST_PROFILE], + newValue: "CodeLastDeployTimestamp new", + oldValue: TEST_ASK_STATES.profiles[TEST_PROFILE].code.lastDeployTimestamp, + }, + { + field: "DeploymentStatus", + params: [TEST_PROFILE], + newValue: "deploymentStatus new", + oldValue: TEST_ASK_STATES.profiles[TEST_PROFILE].deploymentStatus, + }, ].forEach(({field, params, newValue, oldValue}) => { it(`| test get${field} function successfully`, () => { expect(ResourcesConfig.getInstance()[`get${field}`](...params)).deep.equal(oldValue); From c5e207057f3b5e21b2a8568c265a90f45397d95b Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Tue, 21 Nov 2023 12:57:17 -0800 Subject: [PATCH 22/29] chore: fix hosted skill invalid default invocation name --- docs/concepts/Alexa-Hosted-Skill-Commands.md | 44 +++++++------------- lib/model/sample-template.ts | 2 +- lib/utils/constants.js | 2 +- 3 files changed, 18 insertions(+), 30 deletions(-) diff --git a/docs/concepts/Alexa-Hosted-Skill-Commands.md b/docs/concepts/Alexa-Hosted-Skill-Commands.md index 23253992..ccb0b3bf 100644 --- a/docs/concepts/Alexa-Hosted-Skill-Commands.md +++ b/docs/concepts/Alexa-Hosted-Skill-Commands.md @@ -14,33 +14,28 @@ Using `ask new`, the Alexa hosted skill service will create a new AWS account, w **debug**: Optional. Show debug messages. - - -## WORKFLOW: +## WORKFLOW Users will be asked the following questions to create a new skill: * Prompts user for `programming language` - * Select programming language - * AHS supports Python3.7 and Node12.x + * Select programming language + * AHS supports Python3.7 and Node12.x * Prompts user for a method to host your skill's backend resources - * Select `Alexa-hosted skills` + * Select `Alexa-hosted skills` * Prompts user for `skill name` - * Leave empty to use the default skill name `Hello World Skill` + * Leave empty to use the default skill name `hosted hello world` * Prompts user for a `folder name` for the skill project - * Leave empty to use the default folder name `HelloWorldSkill` + * Leave empty to use the default folder name `HelloWorldSkill` The user is not prompted for a `skill template`. The [Hello World Skill](https://github.com/alexa/skill-sample-nodejs-hello-world) is the only option for now. - - # DOWNLOAD SKILL - INIT COMMAND `ask init --hosted-skill-id ` -- download an existing Alexa-Hosted Skill to their local environment. This command initializes Alexa Hosted Skills by cloning the project from the hosted skill service, downloading the latest Alexa skill package, and provide a git-ready environment. Developers can then checkout, pull from, and push to a remote Git repository from their local machines. - **STRUCTURE OF INIT COMMAND:** `ask init [--hosted-skill-id ] [-p | --profile ] [--debug] [-h | --help]` @@ -53,16 +48,12 @@ This command initializes Alexa Hosted Skills by cloning the project from the hos **debug**: Optional. Show debug messages. - - ## GIT CREDENTIALS To access the CodeCommit repository, the Alexa hosted service uses git-credential along with ASK-CLI and SMAPI to automatically pass down temporary Git credentials. Using `ask util git-credentials-helper` in the skill root directory can retrieve the username and password of the Git credentials. - - # UPGRADE PROJECT - UPGRADE COMMANDS Skills created with CLI 1.x will need to update their project structure in order to deploy with the CLI v2. @@ -78,22 +69,21 @@ Skills created with CLI 1.x will need to update their project structure in order **debug**: Optional. Show debug messages. +## UPGRADE STEPS -## UPGRADE STEPS: 1. Using ask-cli 1.x, deploy your skill: - * `$ ask deploy` + * `$ ask deploy` 2. Install ask-cli: - * `$ npm install -g ask-cli` + * `$ npm install -g ask-cli` 3. Upgrade your skill project with ask-cli. From your project's root directory, run: - * `$ ask util upgrade-project` + * `$ ask util upgrade-project` * A hidden folder named ./legacy contains a copy of your v1 skill project. * The CLI v2 skill-package directory is downloaded to the working directory. * The CLI v2 ask-resources.json will be generated. * The dev branch is merged into the master branch. The dev branch is then removed. 4. Commit upgrade changes: - * `$ git commit -m "upgrade project for CLI v2"` - +* `$ git commit -m "upgrade project for CLI v2"` # DEPLOY SKILL - GIT PUSH @@ -101,25 +91,25 @@ Skills created with CLI 1.x will need to update their project structure in order Unlike in CLI v1, running `ask deploy` on hosted skills will no longer trigger skill deployment in CLI v2. Instead, `$ git push` sends the latest source code to Lambda. It also deploys skill-package changes such as the interaction model, skill manifest and in-skill products. -## DEPLOYMENT STEPS: +## DEPLOYMENT STEPS + * Push to deploy skill code and resources: - * `$ git push` + * `$ git push` * Pushing to `master` branch deploys "lambda" folder to user's `development` stage AWS Lambda function, and deploys "skill-package" folder as skill's JSON files * Pushing to `prod` branch deploys "lambda" folder to user's `live` stage AWS Lambda function, and deploys "skill-package" folder as skill's JSON files - Note: dev branch is only used to be displayed in the web console. You only need to push to dev branch if you want to sync version that is displayed in the web console. When you receive the following warning: "The master branch of your skill is ahead of the dev version. To re-enable the code editor, you need to use the ASK CLI to merge the remote master branch to dev and push.", you need to use following steps to sync your master branch back to dev: -``` +```shell git checkout dev git pull --rebase git merge master # fix any merge conflicts if you have git push --no-verify ``` - + ## DEVELOPMENT CONSOLE, BRANCHES AND STAGES * dev branch corresponds to the code you see in the web console editor. @@ -129,5 +119,3 @@ git push --no-verify ## Git Pre-push Hook CLI v2 supports Alexa Hosted skill, a git-native service, to track deployment states using the pre-push hook. When $ git push executes, the pre-push hook prints the skill portal URL to allow users to track deployment states. - - diff --git a/lib/model/sample-template.ts b/lib/model/sample-template.ts index b5b85348..d6fb4f4f 100644 --- a/lib/model/sample-template.ts +++ b/lib/model/sample-template.ts @@ -18,7 +18,7 @@ export type SampleTemplateFilterValues = IM | AC | ALEXA_HOSTED | CLOUDFORMATION * "stack": "ac", * "deploy": "lambda", * "lang": "node", - * "name": "Hello world", + * "name": "hello world", * "url": "https://github.com/alexa/skill-sample-nodejs-hello-world.git", * "desc": "Alexa's hello world skill to send the greetings to the world!" * } diff --git a/lib/utils/constants.js b/lib/utils/constants.js index baeb641f..a278ddeb 100644 --- a/lib/utils/constants.js +++ b/lib/utils/constants.js @@ -37,7 +37,7 @@ module.exports.DEPLOY_TARGET = { }; module.exports.HOSTED_SKILL = { - DEFAULT_SKILL_NAME: "Hello World Skill", + DEFAULT_SKILL_NAME: "hosted hello world", DEFAULT_LOCALE: "en-US", LOCALES: [ "de-DE", From 9066ce3b37b5a1a708fa4b07fbd2c16635a1411d Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:06:45 -0800 Subject: [PATCH 23/29] chore: update the github pr auto assign list --- .github/auto-assign-config.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/auto-assign-config.yml b/.github/auto-assign-config.yml index 52f6c024..6e4d849d 100644 --- a/.github/auto-assign-config.yml +++ b/.github/auto-assign-config.yml @@ -3,6 +3,5 @@ addReviewers: true addAssignees: author reviewers: - - doiron - tydonelson - - LucioMS + - CamdenFoucht From 0fb3185d78ddaa49720e36e3f0e3ca02eee03879 Mon Sep 17 00:00:00 2001 From: Mario Doiron <5252025+doiron@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:28:57 -0800 Subject: [PATCH 24/29] chore(release): 2.30.6 --- CHANGELOG.md | 11 +++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 114e71ae..f5854d5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.30.6](https://github.com/alexa/ask-cli/compare/v2.30.5...v2.30.6) (2023-12-09) + +### Features + +* add ASK_FORCE_ENABLE to allow skipping domain validation during enablement ([487f8db](https://github.com/alexa/ask-cli/commit/487f8dbcdc9429d04b6fd475aa4a2a8430831f1f)) + +### Bug Fixes + +* display message property for all error instance types ([a0d2f19](https://github.com/alexa/ask-cli/commit/a0d2f19faeee36523be701522585272cff2a2d6d)) +* use npm registry https url ([9e92ee5](https://github.com/alexa/ask-cli/commit/9e92ee5bf9aedcba1f683c1db877d0a9c1442cae)) + ### [2.30.5](https://github.com/alexa/ask-cli/compare/v2.30.4...v2.30.5) (2023-08-09) diff --git a/package-lock.json b/package-lock.json index e7546f8c..b851d1ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ask-cli", - "version": "2.30.5", + "version": "2.30.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ask-cli", - "version": "2.30.5", + "version": "2.30.6", "hasInstallScript": true, "license": "Apache 2.0", "dependencies": { diff --git a/package.json b/package.json index 395c037d..fe8470bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ask-cli", - "version": "2.30.5", + "version": "2.30.6", "description": "Alexa Skills Kit (ASK) Command Line Interfaces", "bin": { "ask": "dist/bin/ask.js" From 8c297bf9cd57beb364d978c5259b8eef3a01eb48 Mon Sep 17 00:00:00 2001 From: Mario <11095804+doiron@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:32:06 -0800 Subject: [PATCH 25/29] feat: add ASK_FORCE_ENABLE to allow skipping domain validation during enablement (#500) Co-authored-by: Mario Doiron <5252025+doiron@users.noreply.github.com> --- .../skill-metadata-controller/index.js | 5 ++++ .../skill-metadata-controller-test.js | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/controllers/skill-metadata-controller/index.js b/lib/controllers/skill-metadata-controller/index.js index d10c35b8..e3d50d4c 100644 --- a/lib/controllers/skill-metadata-controller/index.js +++ b/lib/controllers/skill-metadata-controller/index.js @@ -111,6 +111,11 @@ module.exports = class SkillMetadataController { * Validates domain info */ validateDomain() { + if (process.env.ASK_FORCE_ENABLE) { + Messenger.getInstance().warn("The ASK_FORCE_ENABLE environment variable is set. Skipping domain validation.\n"); + return; + } + const domainInfo = Manifest.getInstance().getApis(); if (!domainInfo || R.isEmpty(domainInfo)) { throw new CLiError('Skill information is not valid. Please make sure "apis" field in the skill.json is not empty.'); diff --git a/test/unit/controller/skill-metadata-controller-test.js b/test/unit/controller/skill-metadata-controller-test.js index df18b25a..042a09db 100644 --- a/test/unit/controller/skill-metadata-controller-test.js +++ b/test/unit/controller/skill-metadata-controller-test.js @@ -61,6 +61,7 @@ describe("Controller test - skill metadata controller test", () => { }); afterEach(() => { + delete process.env.ASK_FORCE_ENABLE; ResourcesConfig.dispose(); Manifest.dispose(); sinon.restore(); @@ -263,6 +264,28 @@ describe("Controller test - skill metadata controller test", () => { expect(() => skillMetaController.validateDomain()).to.throw(CliWarn, expectedErrMessage); }); + it("| skips validation when ASK_FORCE_ENABLE is set", () => { + // setup + process.env.ASK_FORCE_ENABLE = 1; + Manifest.getInstance().setApis({ + custom: {}, + unknownApi: {}, + smartHome: {}, + }); + const getApisSpy = sinon.spy(Manifest.prototype, "getApis"); + const warnStub = sinon.stub(); + sinon.stub(Messenger, "getInstance").returns({ + warn: warnStub, + }); + const expectedWarningMessage = "The ASK_FORCE_ENABLE environment variable is set. Skipping domain validation.\n" + // call + skillMetaController.validateDomain(); + // verify + expect(getApisSpy.callCount).eq(0); + expect(warnStub.callCount).equal(1); + expect(warnStub.args[0][0]).equal(expectedWarningMessage); + }); + it("| callback error when getSkillEnablement return error", (done) => { // setup sinon.stub(httpClient, "request").callsArgWith(3, "getSkillEnablementError"); // stub smapi request From 6b15c518acbbdd2a50628b94b1e9709bfd8386c6 Mon Sep 17 00:00:00 2001 From: skrotha <141290843+skrotha@users.noreply.github.com> Date: Wed, 27 Dec 2023 15:08:52 -0700 Subject: [PATCH 26/29] chore: Update hosted skills default python version to 3.8 (#501) * feat: Change Alexa Hosted Skill runtime to Python 3.8 * feat: Update Alexa Hosted Skill docs default run times --- docs/concepts/Alexa-Hosted-Skill-Commands.md | 2 +- lib/utils/constants.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/concepts/Alexa-Hosted-Skill-Commands.md b/docs/concepts/Alexa-Hosted-Skill-Commands.md index ccb0b3bf..bb0d8dd8 100644 --- a/docs/concepts/Alexa-Hosted-Skill-Commands.md +++ b/docs/concepts/Alexa-Hosted-Skill-Commands.md @@ -20,7 +20,7 @@ Users will be asked the following questions to create a new skill: * Prompts user for `programming language` * Select programming language - * AHS supports Python3.7 and Node12.x + * AHS supports Python3.8 and Node16.x * Prompts user for a method to host your skill's backend resources * Select `Alexa-hosted skills` * Prompts user for `skill name` diff --git a/lib/utils/constants.js b/lib/utils/constants.js index a278ddeb..88096a8f 100644 --- a/lib/utils/constants.js +++ b/lib/utils/constants.js @@ -63,7 +63,7 @@ module.exports.HOSTED_SKILL = { }, DEFAULT_RUNTIME: { NodeJS: "NODE_16_X", - Python: "PYTHON_3_7", + Python: "PYTHON_3_8", }, SIGNIN_PATH: "/ap/signin", PERMISSION_ENUM: { From 3067f13dabaf60b02c7a60ff07566520dfa1c339 Mon Sep 17 00:00:00 2001 From: Bruce Mbudi <97241945+bruceowenga@users.noreply.github.com> Date: Tue, 6 Feb 2024 22:24:20 +0300 Subject: [PATCH 27/29] fix: grammatical error in deploy message (#502) Co-authored-by: Bruce --- lib/commands/run/index.ts | 2 +- test/unit/commands/run/index-test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/commands/run/index.ts b/lib/commands/run/index.ts index 16f3ee3c..8974421c 100644 --- a/lib/commands/run/index.ts +++ b/lib/commands/run/index.ts @@ -45,7 +45,7 @@ export default class RunCommand extends AbstractCommand { new ResourcesConfig(path.join(process.cwd(), CONSTANTS.FILE_PATH.ASK_RESOURCES_JSON_CONFIG)); skillId = ResourcesConfig.getInstance().getSkillId(profile); if (!stringUtils.isNonBlankString(skillId)) { - throw new CliError(`Failed to obtain skill-id for the given profile - ${profile}` + ". Please deploy you skill project first."); + throw new CliError(`Failed to obtain skill-id for the given profile - ${profile}` + ". Please deploy your skill project first."); } } catch (error) { Messenger.getInstance().error(error); diff --git a/test/unit/commands/run/index-test.ts b/test/unit/commands/run/index-test.ts index ffe993ef..c41fb30f 100644 --- a/test/unit/commands/run/index-test.ts +++ b/test/unit/commands/run/index-test.ts @@ -78,7 +78,7 @@ describe("Commands Run test - command class test", () => { sinon.stub(path, "join").returns(INVALID_RESOURCES_CONFIG_JSON_PATH); // call await expect(instance.handle(TEST_CMD_WITH_VALUES)).rejectedWith( - `Failed to obtain skill-id for the given profile - ${TEST_PROFILE}. Please deploy you skill project first.`, + `Failed to obtain skill-id for the given profile - ${TEST_PROFILE}. Please deploy your skill project first.`, ); }); From 65d769e495802767e829e0968415974191be3482 Mon Sep 17 00:00:00 2001 From: Kailash Subramanian Date: Wed, 7 Feb 2024 09:45:31 -0800 Subject: [PATCH 28/29] chore(release): 2.30.7 --- CHANGELOG.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5854d5b..930b8748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [2.30.7](https://github.com/alexa/ask-cli/compare/v2.30.6...v2.30.7) (2024-02-07) + + +### Bug Fixes + +* grammatical error in deploy message ([#502](https://github.com/alexa/ask-cli/issues/502)) ([3067f13](https://github.com/alexa/ask-cli/commit/3067f13dabaf60b02c7a60ff07566520dfa1c339)) +* update hosted skills default python version to 3.8 ([#501](https://github.com/alexa/ask-cli/pull/501)) ([6b15c51](https://github.com/alexa/ask-cli/commit/6b15c518acbbdd2a50628b94b1e9709bfd8386c6)) + ### [2.30.6](https://github.com/alexa/ask-cli/compare/v2.30.5...v2.30.6) (2023-12-09) ### Features diff --git a/package-lock.json b/package-lock.json index b851d1ca..dfdaf4ed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ask-cli", - "version": "2.30.6", + "version": "2.30.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ask-cli", - "version": "2.30.6", + "version": "2.30.7", "hasInstallScript": true, "license": "Apache 2.0", "dependencies": { diff --git a/package.json b/package.json index fe8470bb..8920b57b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ask-cli", - "version": "2.30.6", + "version": "2.30.7", "description": "Alexa Skills Kit (ASK) Command Line Interfaces", "bin": { "ask": "dist/bin/ask.js" From d93715cc6d1247e36e4bb7c3bb7af1cbc52bcc95 Mon Sep 17 00:00:00 2001 From: Kailash Subramanian Date: Mon, 26 Feb 2024 10:28:37 -0800 Subject: [PATCH 29/29] fix: skill status should only poll when in progress --- .../hosted-skill-controller/helper.js | 19 ++++--- .../hosted-skill-controller/index-test.js | 52 +++++++++++++++++++ 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/lib/controllers/hosted-skill-controller/helper.js b/lib/controllers/hosted-skill-controller/helper.js index 49d0c747..bec595db 100644 --- a/lib/controllers/hosted-skill-controller/helper.js +++ b/lib/controllers/hosted-skill-controller/helper.js @@ -67,15 +67,18 @@ function pollingSkillStatus(smapiClient, skillId, callback) { response, ); } + + const empty = (data) => !data || R.isEmpty(data); + return ( - !statusTracker.manifest || - !statusTracker.interactionModel || - !statusTracker.hostedSkillProvisioning || - !( - statusTracker.manifest === CONSTANTS.HOSTED_SKILL.MANIFEST_STATUS.SUCCESS && - statusTracker.interactionModel === CONSTANTS.HOSTED_SKILL.INTERACTION_MODEL_STATUS.SUCCESS && - statusTracker.hostedSkillProvisioning === CONSTANTS.HOSTED_SKILL.PROVISIONING_STATUS.SUCCESS - ) + // retry if one of manifest, interaction model, provisioning don't exist + empty(statusTracker.manifest) || + empty(statusTracker.interactionModel) || + empty(statusTracker.hostedSkillProvisioning) || + // retry if one of manifest, interaction model, provisioning are in progress + statusTracker.manifest === CONSTANTS.HOSTED_SKILL.MANIFEST_STATUS.IN_PROGRESS || + statusTracker.interactionModel === CONSTANTS.HOSTED_SKILL.INTERACTION_MODEL_STATUS.IN_PROGRESS || + statusTracker.hostedSkillProvisioning === CONSTANTS.HOSTED_SKILL.PROVISIONING_STATUS.IN_PROGRESS ); }; retryUtils.retry(retryConfig, retryCall, shouldRetryCondition, (err) => callback(err, err ? null : statusTracker)); diff --git a/test/unit/controller/hosted-skill-controller/index-test.js b/test/unit/controller/hosted-skill-controller/index-test.js index b77a0b8a..d1bdca83 100644 --- a/test/unit/controller/hosted-skill-controller/index-test.js +++ b/test/unit/controller/hosted-skill-controller/index-test.js @@ -401,6 +401,58 @@ describe("Controller test - hosted skill controller test", () => { }); }); + it("| polling to get skill status until one resource FAILED, expect error thrown ", (done) => { + // setup + const TEST_STATUS_RESPONSE_0 = { + statusCode: 200, + headers: {}, + body: { + [CONSTANTS.HOSTED_SKILL.RESOURCES.MANIFEST]: { + lastUpdateRequest: { + status: "SUCCEEDED", + }, + }, + }, + }; + const TEST_STATUS_RESPONSE_1 = { + statusCode: 200, + headers: {}, + body: { + [CONSTANTS.HOSTED_SKILL.RESOURCES.MANIFEST]: { + lastUpdateRequest: { + status: "SUCCEEDED", + }, + }, + [CONSTANTS.HOSTED_SKILL.RESOURCES.INTERACTION_MODEL]: { + [TEST_LOCALE]: { + lastUpdateRequest: { + status: "SUCCEEDED", + }, + }, + }, + [CONSTANTS.HOSTED_SKILL.RESOURCES.PROVISIONING]: { + lastUpdateRequest: { + status: "FAILED", + }, + }, + }, + }; + const stubTestFunc = sinon.stub(httpClient, "request"); // stub getSkillStatus smapi request + stubTestFunc.onCall(0).callsArgWith(3, null, TEST_STATUS_RESPONSE_0); + stubTestFunc.onCall(1).callsArgWith(3, null, TEST_STATUS_RESPONSE_1); + sinon.useFakeTimers().tickAsync(CONSTANTS.CONFIGURATION.RETRY.MAX_RETRY_INTERVAL); + const callback = (err, res) => { + expect(err).equal( + "Check skill status failed for the following reason:\n" + + "Skill provisioning step failed.\nInfrastructure provision for the hosted skill failed. Please try again.", + ); + expect(res).equal(undefined); + done(); + }; + // call + hostedSkillController.checkSkillStatus(TEST_SKILL_ID, callback); + }); + it("| polling to get skill status response is null, expect error thrown ", (done) => { // setup const TEST_RESPONSE = null;