diff --git a/__tests__/__packages__/cli-test-utils/package.json b/__tests__/__packages__/cli-test-utils/package.json index 988e3ac98c..12459a1f92 100644 --- a/__tests__/__packages__/cli-test-utils/package.json +++ b/__tests__/__packages__/cli-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/cli-test-utils", - "version": "8.0.0", + "version": "8.0.1", "description": "Test utilities package for Zowe CLI plug-ins", "author": "Zowe", "license": "EPL-2.0", @@ -43,7 +43,7 @@ "devDependencies": { "@types/js-yaml": "^4.0.9", "@types/uuid": "^10.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/imperative": "^8.0.0-next" diff --git a/lerna.json b/lerna.json index 4ac911a05e..8033a82366 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "8.0.0", + "version": "8.0.1", "command": { "publish": { "ignoreChanges": [ diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 8b1363c474..0925f9cf7d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -52,7 +52,7 @@ }, "__tests__/__packages__/cli-test-utils": { "name": "@zowe/cli-test-utils", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { "find-up": "^5.0.0", @@ -63,7 +63,7 @@ "devDependencies": { "@types/js-yaml": "^4.0.9", "@types/uuid": "^10.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/imperative": "^8.0.0-next" @@ -16267,21 +16267,21 @@ }, "packages/cli": { "name": "@zowe/cli", - "version": "8.0.0", + "version": "8.0.1", "hasInstallScript": true, "license": "EPL-2.0", "dependencies": { - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0", - "@zowe/provisioning-for-zowe-sdk": "8.0.0", - "@zowe/zos-console-for-zowe-sdk": "8.0.0", - "@zowe/zos-files-for-zowe-sdk": "8.0.0", - "@zowe/zos-jobs-for-zowe-sdk": "8.0.0", - "@zowe/zos-logs-for-zowe-sdk": "8.0.0", - "@zowe/zos-tso-for-zowe-sdk": "8.0.0", - "@zowe/zos-uss-for-zowe-sdk": "8.0.0", - "@zowe/zos-workflows-for-zowe-sdk": "8.0.0", - "@zowe/zosmf-for-zowe-sdk": "8.0.0", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1", + "@zowe/provisioning-for-zowe-sdk": "8.0.1", + "@zowe/zos-console-for-zowe-sdk": "8.0.1", + "@zowe/zos-files-for-zowe-sdk": "8.0.1", + "@zowe/zos-jobs-for-zowe-sdk": "8.0.1", + "@zowe/zos-logs-for-zowe-sdk": "8.0.1", + "@zowe/zos-tso-for-zowe-sdk": "8.0.1", + "@zowe/zos-uss-for-zowe-sdk": "8.0.1", + "@zowe/zos-workflows-for-zowe-sdk": "8.0.1", + "@zowe/zosmf-for-zowe-sdk": "8.0.1", "find-process": "1.4.7", "lodash": "4.17.21", "minimatch": "9.0.5", @@ -16294,7 +16294,7 @@ "@types/diff": "^5.0.9", "@types/lodash": "^4.17.6", "@types/tar": "^6.1.11", - "@zowe/cli-test-utils": "8.0.0", + "@zowe/cli-test-utils": "8.0.1", "comment-json": "^4.2.3", "strip-ansi": "^6.0.1", "which": "^4.0.0" @@ -16350,15 +16350,15 @@ }, "packages/core": { "name": "@zowe/core-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { "comment-json": "~4.2.3", "string-width": "^4.2.3" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16369,7 +16369,7 @@ }, "packages/imperative": { "name": "@zowe/imperative", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { "@types/yargs": "^17.0.32", @@ -16563,16 +16563,16 @@ }, "packages/provisioning": { "name": "@zowe/provisioning-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { "js-yaml": "^4.1.0" }, "devDependencies": { "@types/js-yaml": "^4.0.9", - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16597,15 +16597,15 @@ }, "packages/workflows": { "name": "@zowe/zos-workflows-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.0.0" + "@zowe/zos-files-for-zowe-sdk": "8.0.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16617,12 +16617,12 @@ }, "packages/zosconsole": { "name": "@zowe/zos-console-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16634,16 +16634,16 @@ }, "packages/zosfiles": { "name": "@zowe/zos-files-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { "minimatch": "^9.0.5" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0", - "@zowe/zos-uss-for-zowe-sdk": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1", + "@zowe/zos-uss-for-zowe-sdk": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16675,15 +16675,15 @@ }, "packages/zosjobs": { "name": "@zowe/zos-jobs-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.0.0" + "@zowe/zos-files-for-zowe-sdk": "8.0.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16695,12 +16695,12 @@ }, "packages/zoslogs": { "name": "@zowe/zos-logs-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16712,12 +16712,12 @@ }, "packages/zosmf": { "name": "@zowe/zosmf-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16729,15 +16729,15 @@ }, "packages/zostso": { "name": "@zowe/zos-tso-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { - "@zowe/zosmf-for-zowe-sdk": "8.0.0" + "@zowe/zosmf-for-zowe-sdk": "8.0.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" @@ -16749,15 +16749,15 @@ }, "packages/zosuss": { "name": "@zowe/zos-uss-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "license": "EPL-2.0", "dependencies": { "ssh2": "^1.15.0" }, "devDependencies": { "@types/ssh2": "^1.11.19", - "@zowe/cli-test-utils": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/imperative": "8.0.1" }, "engines": { "node": ">=18.12.0" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 1db209be61..ba8371ed0f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to the Zowe CLI package will be documented in this file. + +## Recent Changes + +- Enhancement: Added `--stateful` flag to `zos-tso issue cmd` to allow declaring the statefulness of the address space being created. [#2240](https://github.com/zowe/zowe-cli/pull/2240) +- Enhancement: `--suppress-startup-messages` flag default value changed to `true`. [#2240](https://github.com/zowe/zowe-cli/pull/2240) + ## `8.0.0` - MAJOR: v8.0.0 Release diff --git a/packages/cli/__tests__/auth/__system__/__scripts__/create_team_cfg.sh b/packages/cli/__tests__/auth/__system__/__scripts__/create_team_cfg.sh old mode 100644 new mode 100755 diff --git a/packages/cli/__tests__/zosconsole/__integration__/issue/__scripts__/command/command_console.sh b/packages/cli/__tests__/zosconsole/__integration__/issue/__scripts__/command/command_console.sh index 6f1c161a35..b8ce1a4fcd 100755 --- a/packages/cli/__tests__/zosconsole/__integration__/issue/__scripts__/command/command_console.sh +++ b/packages/cli/__tests__/zosconsole/__integration__/issue/__scripts__/command/command_console.sh @@ -2,7 +2,7 @@ set -e # Generate console name: 6 characters + "CN" -CONSOLE_NAME=`cat /dev/urandom | tr -dc '[:upper:]' | fold -w 6 | head -n 1` +CONSOLE_NAME=`cat /dev/urandom | LC_CTYPE=C tr -dc '[:upper:]' | fold -w 6 | head -n 1` CONSOLE_NAME="${CONSOLE_NAME}CN" # Ensure that console doesn't exist diff --git a/packages/cli/__tests__/zosconsole/__integration__/issue/__scripts__/command/command_console_rfj.sh b/packages/cli/__tests__/zosconsole/__integration__/issue/__scripts__/command/command_console_rfj.sh index e269b59473..bce5b4b415 100755 --- a/packages/cli/__tests__/zosconsole/__integration__/issue/__scripts__/command/command_console_rfj.sh +++ b/packages/cli/__tests__/zosconsole/__integration__/issue/__scripts__/command/command_console_rfj.sh @@ -2,7 +2,7 @@ set -e # Generate console name: 6 characters + "CN" -CONSOLE_NAME=`cat /dev/urandom | tr -dc '[:upper:]' | fold -w 6 | head -n 1` +CONSOLE_NAME=`cat /dev/urandom | LC_CTYPE=C tr -dc '[:upper:]' | fold -w 6 | head -n 1` CONSOLE_NAME="${CONSOLE_NAME}CN" # Ensure that console doesn't exist diff --git a/packages/cli/__tests__/zosconsole/__system__/issue/__scripts__/command/command_console.sh b/packages/cli/__tests__/zosconsole/__system__/issue/__scripts__/command/command_console.sh index 6f1c161a35..b8ce1a4fcd 100755 --- a/packages/cli/__tests__/zosconsole/__system__/issue/__scripts__/command/command_console.sh +++ b/packages/cli/__tests__/zosconsole/__system__/issue/__scripts__/command/command_console.sh @@ -2,7 +2,7 @@ set -e # Generate console name: 6 characters + "CN" -CONSOLE_NAME=`cat /dev/urandom | tr -dc '[:upper:]' | fold -w 6 | head -n 1` +CONSOLE_NAME=`cat /dev/urandom | LC_CTYPE=C tr -dc '[:upper:]' | fold -w 6 | head -n 1` CONSOLE_NAME="${CONSOLE_NAME}CN" # Ensure that console doesn't exist diff --git a/packages/cli/__tests__/zosconsole/__system__/issue/__scripts__/command/command_console_rfj.sh b/packages/cli/__tests__/zosconsole/__system__/issue/__scripts__/command/command_console_rfj.sh index e269b59473..bce5b4b415 100755 --- a/packages/cli/__tests__/zosconsole/__system__/issue/__scripts__/command/command_console_rfj.sh +++ b/packages/cli/__tests__/zosconsole/__system__/issue/__scripts__/command/command_console_rfj.sh @@ -2,7 +2,7 @@ set -e # Generate console name: 6 characters + "CN" -CONSOLE_NAME=`cat /dev/urandom | tr -dc '[:upper:]' | fold -w 6 | head -n 1` +CONSOLE_NAME=`cat /dev/urandom | LC_CTYPE=C tr -dc '[:upper:]' | fold -w 6 | head -n 1` CONSOLE_NAME="${CONSOLE_NAME}CN" # Ensure that console doesn't exist diff --git a/packages/cli/__tests__/zostso/__integration__/issue/__snapshots__/cli.zos-tso.issue.cmd.integration.test.ts.snap b/packages/cli/__tests__/zostso/__integration__/issue/__snapshots__/cli.zos-tso.issue.cmd.integration.test.ts.snap index ceddc6e604..da99f287ba 100644 --- a/packages/cli/__tests__/zostso/__integration__/issue/__snapshots__/cli.zos-tso.issue.cmd.integration.test.ts.snap +++ b/packages/cli/__tests__/zostso/__integration__/issue/__snapshots__/cli.zos-tso.issue.cmd.integration.test.ts.snap @@ -34,6 +34,15 @@ exports[`zos-tso issue command should display the help 1`] = ` Suppress console messages from start of address space. + Default value: true + + --stateful | --sf (boolean) + + Statefulness of address space created for TSO command. This option is not + supported when --suppress-startup-messages is set to false. + + Default value: false + TSO ADDRESS SPACE OPTIONS ------------------------- @@ -185,8 +194,8 @@ exports[`zos-tso issue command should display the help 1`] = ` \\"success\\": true, \\"exitCode\\": 0, \\"message\\": \\"The help was constructed for command: command.\\", - \\"stdout\\": \\"\\\\n COMMAND NAME\\\\n ------------\\\\n\\\\n command | cmd\\\\n\\\\n DESCRIPTION\\\\n -----------\\\\n\\\\n Creates a TSO address space, issues a TSO command through the newly created\\\\n address space, waits for the READY prompt to print the response, and terminates\\\\n the TSO address space. All response data are returned to the user up to (but not\\\\n including) the TSO 'READY' prompt.\\\\n\\\\n USAGE\\\\n -----\\\\n\\\\n zowe zos-tso issue command [options]\\\\n\\\\n POSITIONAL ARGUMENTS\\\\n --------------------\\\\n\\\\n commandText\\\\t\\\\t (string)\\\\n\\\\n The TSO command to issue.\\\\n\\\\n OPTIONS\\\\n -------\\\\n\\\\n --suppress-startup-messages | --ssm (boolean)\\\\n\\\\n Suppress console messages from start of address space.\\\\n\\\\n TSO ADDRESS SPACE OPTIONS\\\\n -------------------------\\\\n\\\\n --account | -a (string)\\\\n\\\\n Your z/OS TSO/E accounting information.\\\\n\\\\n --character-set | --cs (string)\\\\n\\\\n Character set for address space to convert messages and responses from UTF-8 to\\\\n EBCDIC.\\\\n\\\\n Default value: 697\\\\n\\\\n --code-page | --cp (string)\\\\n\\\\n Codepage value for TSO/E address space to convert messages and responses from\\\\n UTF-8 to EBCDIC.\\\\n\\\\n Default value: 1047\\\\n\\\\n --columns | --cols (number)\\\\n\\\\n The number of columns on a screen.\\\\n\\\\n Default value: 80\\\\n\\\\n --logon-procedure | -l (string)\\\\n\\\\n The logon procedure to use when creating TSO procedures on your behalf.\\\\n\\\\n Default value: IZUFPROC\\\\n\\\\n --region-size | --rs (number)\\\\n\\\\n Region size for the TSO/E address space.\\\\n\\\\n Default value: 4096\\\\n\\\\n --rows (number)\\\\n\\\\n The number of rows on a screen.\\\\n\\\\n Default value: 24\\\\n\\\\n ZOSMF CONNECTION OPTIONS\\\\n ------------------------\\\\n\\\\n --host | -H (string)\\\\n\\\\n The z/OSMF server host name.\\\\n\\\\n --port | -P (number)\\\\n\\\\n The z/OSMF server port.\\\\n\\\\n Default value: 443\\\\n\\\\n --user | -u (string)\\\\n\\\\n Mainframe (z/OSMF) user name, which can be the same as your TSO login.\\\\n\\\\n --password | --pass | --pw (string)\\\\n\\\\n Mainframe (z/OSMF) password, which can be the same as your TSO password.\\\\n\\\\n --reject-unauthorized | --ru (boolean)\\\\n\\\\n Reject self-signed certificates.\\\\n\\\\n Default value: true\\\\n\\\\n --base-path | --bp (string)\\\\n\\\\n The base path for your API mediation layer instance. Specify this option to\\\\n prepend the base path to all z/OSMF resources when making REST requests. Do not\\\\n specify this option if you are not using an API mediation layer.\\\\n\\\\n --protocol (string)\\\\n\\\\n The protocol used (HTTP or HTTPS)\\\\n\\\\n Default value: https\\\\n Allowed values: http, https\\\\n\\\\n --cert-file (local file path)\\\\n\\\\n The file path to a certificate file to use for authentication\\\\n\\\\n --cert-key-file (local file path)\\\\n\\\\n The file path to a certificate key file to use for authentication\\\\n\\\\n PROFILE OPTIONS\\\\n ---------------\\\\n\\\\n --zosmf-profile | --zosmf-p (string)\\\\n\\\\n The name of a (zosmf) profile to load for this command execution.\\\\n\\\\n --tso-profile | --tso-p (string)\\\\n\\\\n The name of a (tso) profile to load for this command execution.\\\\n\\\\n --base-profile | --base-p (string)\\\\n\\\\n The name of a (base) profile to load for this command execution.\\\\n\\\\n BASE CONNECTION OPTIONS\\\\n -----------------------\\\\n\\\\n --token-type | --tt (string)\\\\n\\\\n The type of token to get and use for the API. Omit this option to use the\\\\n default token type, which is provided by 'zowe auth login'.\\\\n\\\\n --token-value | --tv (string)\\\\n\\\\n The value of the token to pass to the API.\\\\n\\\\n GLOBAL OPTIONS\\\\n --------------\\\\n\\\\n --show-inputs-only (boolean)\\\\n\\\\n Show command inputs and do not run the command\\\\n\\\\n --response-format-json | --rfj (boolean)\\\\n\\\\n Produce JSON formatted data from a command\\\\n\\\\n --help | -h (boolean)\\\\n\\\\n Display help text\\\\n\\\\n --help-web | --hw (boolean)\\\\n\\\\n Display HTML help in browser\\\\n\\\\n EXAMPLES\\\\n --------\\\\n\\\\n - Issue the TSO command \\\\\\"status\\\\\\" to display information about\\\\n jobs for your user ID.:\\\\n\\\\n $ zowe zos-tso issue command \\\\\\"status\\\\\\"\\\\n\\\\n\\", + \\"stdout\\": \\"\\\\n COMMAND NAME\\\\n ------------\\\\n\\\\n command | cmd\\\\n\\\\n DESCRIPTION\\\\n -----------\\\\n\\\\n Creates a TSO address space, issues a TSO command through the newly created\\\\n address space, waits for the READY prompt to print the response, and terminates\\\\n the TSO address space. All response data are returned to the user up to (but not\\\\n including) the TSO 'READY' prompt.\\\\n\\\\n USAGE\\\\n -----\\\\n\\\\n zowe zos-tso issue command [options]\\\\n\\\\n POSITIONAL ARGUMENTS\\\\n --------------------\\\\n\\\\n commandText\\\\t\\\\t (string)\\\\n\\\\n The TSO command to issue.\\\\n\\\\n OPTIONS\\\\n -------\\\\n\\\\n --suppress-startup-messages | --ssm (boolean)\\\\n\\\\n Suppress console messages from start of address space.\\\\n\\\\n Default value: true\\\\n\\\\n --stateful | --sf (boolean)\\\\n\\\\n Statefulness of address space created for TSO command. This option is not\\\\n supported when --suppress-startup-messages is set to false.\\\\n\\\\n Default value: false\\\\n\\\\n TSO ADDRESS SPACE OPTIONS\\\\n -------------------------\\\\n\\\\n --account | -a (string)\\\\n\\\\n Your z/OS TSO/E accounting information.\\\\n\\\\n --character-set | --cs (string)\\\\n\\\\n Character set for address space to convert messages and responses from UTF-8 to\\\\n EBCDIC.\\\\n\\\\n Default value: 697\\\\n\\\\n --code-page | --cp (string)\\\\n\\\\n Codepage value for TSO/E address space to convert messages and responses from\\\\n UTF-8 to EBCDIC.\\\\n\\\\n Default value: 1047\\\\n\\\\n --columns | --cols (number)\\\\n\\\\n The number of columns on a screen.\\\\n\\\\n Default value: 80\\\\n\\\\n --logon-procedure | -l (string)\\\\n\\\\n The logon procedure to use when creating TSO procedures on your behalf.\\\\n\\\\n Default value: IZUFPROC\\\\n\\\\n --region-size | --rs (number)\\\\n\\\\n Region size for the TSO/E address space.\\\\n\\\\n Default value: 4096\\\\n\\\\n --rows (number)\\\\n\\\\n The number of rows on a screen.\\\\n\\\\n Default value: 24\\\\n\\\\n ZOSMF CONNECTION OPTIONS\\\\n ------------------------\\\\n\\\\n --host | -H (string)\\\\n\\\\n The z/OSMF server host name.\\\\n\\\\n --port | -P (number)\\\\n\\\\n The z/OSMF server port.\\\\n\\\\n Default value: 443\\\\n\\\\n --user | -u (string)\\\\n\\\\n Mainframe (z/OSMF) user name, which can be the same as your TSO login.\\\\n\\\\n --password | --pass | --pw (string)\\\\n\\\\n Mainframe (z/OSMF) password, which can be the same as your TSO password.\\\\n\\\\n --reject-unauthorized | --ru (boolean)\\\\n\\\\n Reject self-signed certificates.\\\\n\\\\n Default value: true\\\\n\\\\n --base-path | --bp (string)\\\\n\\\\n The base path for your API mediation layer instance. Specify this option to\\\\n prepend the base path to all z/OSMF resources when making REST requests. Do not\\\\n specify this option if you are not using an API mediation layer.\\\\n\\\\n --protocol (string)\\\\n\\\\n The protocol used (HTTP or HTTPS)\\\\n\\\\n Default value: https\\\\n Allowed values: http, https\\\\n\\\\n --cert-file (local file path)\\\\n\\\\n The file path to a certificate file to use for authentication\\\\n\\\\n --cert-key-file (local file path)\\\\n\\\\n The file path to a certificate key file to use for authentication\\\\n\\\\n PROFILE OPTIONS\\\\n ---------------\\\\n\\\\n --zosmf-profile | --zosmf-p (string)\\\\n\\\\n The name of a (zosmf) profile to load for this command execution.\\\\n\\\\n --tso-profile | --tso-p (string)\\\\n\\\\n The name of a (tso) profile to load for this command execution.\\\\n\\\\n --base-profile | --base-p (string)\\\\n\\\\n The name of a (base) profile to load for this command execution.\\\\n\\\\n BASE CONNECTION OPTIONS\\\\n -----------------------\\\\n\\\\n --token-type | --tt (string)\\\\n\\\\n The type of token to get and use for the API. Omit this option to use the\\\\n default token type, which is provided by 'zowe auth login'.\\\\n\\\\n --token-value | --tv (string)\\\\n\\\\n The value of the token to pass to the API.\\\\n\\\\n GLOBAL OPTIONS\\\\n --------------\\\\n\\\\n --show-inputs-only (boolean)\\\\n\\\\n Show command inputs and do not run the command\\\\n\\\\n --response-format-json | --rfj (boolean)\\\\n\\\\n Produce JSON formatted data from a command\\\\n\\\\n --help | -h (boolean)\\\\n\\\\n Display help text\\\\n\\\\n --help-web | --hw (boolean)\\\\n\\\\n Display HTML help in browser\\\\n\\\\n EXAMPLES\\\\n --------\\\\n\\\\n - Issue the TSO command \\\\\\"status\\\\\\" to display information about\\\\n jobs for your user ID.:\\\\n\\\\n $ zowe zos-tso issue command \\\\\\"status\\\\\\"\\\\n\\\\n\\", \\"stderr\\": \\"\\", - \\"data\\": \\"\\\\n COMMAND NAME\\\\n ------------\\\\n\\\\n command | cmd\\\\n\\\\n DESCRIPTION\\\\n -----------\\\\n\\\\n Creates a TSO address space, issues a TSO command through the newly created\\\\n address space, waits for the READY prompt to print the response, and terminates\\\\n the TSO address space. All response data are returned to the user up to (but not\\\\n including) the TSO 'READY' prompt.\\\\n\\\\n USAGE\\\\n -----\\\\n\\\\n zowe zos-tso issue command [options]\\\\n\\\\n POSITIONAL ARGUMENTS\\\\n --------------------\\\\n\\\\n commandText\\\\t\\\\t (string)\\\\n\\\\n The TSO command to issue.\\\\n\\\\n OPTIONS\\\\n -------\\\\n\\\\n --suppress-startup-messages | --ssm (boolean)\\\\n\\\\n Suppress console messages from start of address space.\\\\n\\\\n TSO ADDRESS SPACE OPTIONS\\\\n -------------------------\\\\n\\\\n --account | -a (string)\\\\n\\\\n Your z/OS TSO/E accounting information.\\\\n\\\\n --character-set | --cs (string)\\\\n\\\\n Character set for address space to convert messages and responses from UTF-8 to\\\\n EBCDIC.\\\\n\\\\n Default value: 697\\\\n\\\\n --code-page | --cp (string)\\\\n\\\\n Codepage value for TSO/E address space to convert messages and responses from\\\\n UTF-8 to EBCDIC.\\\\n\\\\n Default value: 1047\\\\n\\\\n --columns | --cols (number)\\\\n\\\\n The number of columns on a screen.\\\\n\\\\n Default value: 80\\\\n\\\\n --logon-procedure | -l (string)\\\\n\\\\n The logon procedure to use when creating TSO procedures on your behalf.\\\\n\\\\n Default value: IZUFPROC\\\\n\\\\n --region-size | --rs (number)\\\\n\\\\n Region size for the TSO/E address space.\\\\n\\\\n Default value: 4096\\\\n\\\\n --rows (number)\\\\n\\\\n The number of rows on a screen.\\\\n\\\\n Default value: 24\\\\n\\\\n ZOSMF CONNECTION OPTIONS\\\\n ------------------------\\\\n\\\\n --host | -H (string)\\\\n\\\\n The z/OSMF server host name.\\\\n\\\\n --port | -P (number)\\\\n\\\\n The z/OSMF server port.\\\\n\\\\n Default value: 443\\\\n\\\\n --user | -u (string)\\\\n\\\\n Mainframe (z/OSMF) user name, which can be the same as your TSO login.\\\\n\\\\n --password | --pass | --pw (string)\\\\n\\\\n Mainframe (z/OSMF) password, which can be the same as your TSO password.\\\\n\\\\n --reject-unauthorized | --ru (boolean)\\\\n\\\\n Reject self-signed certificates.\\\\n\\\\n Default value: true\\\\n\\\\n --base-path | --bp (string)\\\\n\\\\n The base path for your API mediation layer instance. Specify this option to\\\\n prepend the base path to all z/OSMF resources when making REST requests. Do not\\\\n specify this option if you are not using an API mediation layer.\\\\n\\\\n --protocol (string)\\\\n\\\\n The protocol used (HTTP or HTTPS)\\\\n\\\\n Default value: https\\\\n Allowed values: http, https\\\\n\\\\n --cert-file (local file path)\\\\n\\\\n The file path to a certificate file to use for authentication\\\\n\\\\n --cert-key-file (local file path)\\\\n\\\\n The file path to a certificate key file to use for authentication\\\\n\\\\n PROFILE OPTIONS\\\\n ---------------\\\\n\\\\n --zosmf-profile | --zosmf-p (string)\\\\n\\\\n The name of a (zosmf) profile to load for this command execution.\\\\n\\\\n --tso-profile | --tso-p (string)\\\\n\\\\n The name of a (tso) profile to load for this command execution.\\\\n\\\\n --base-profile | --base-p (string)\\\\n\\\\n The name of a (base) profile to load for this command execution.\\\\n\\\\n BASE CONNECTION OPTIONS\\\\n -----------------------\\\\n\\\\n --token-type | --tt (string)\\\\n\\\\n The type of token to get and use for the API. Omit this option to use the\\\\n default token type, which is provided by 'zowe auth login'.\\\\n\\\\n --token-value | --tv (string)\\\\n\\\\n The value of the token to pass to the API.\\\\n\\\\n GLOBAL OPTIONS\\\\n --------------\\\\n\\\\n --show-inputs-only (boolean)\\\\n\\\\n Show command inputs and do not run the command\\\\n\\\\n --response-format-json | --rfj (boolean)\\\\n\\\\n Produce JSON formatted data from a command\\\\n\\\\n --help | -h (boolean)\\\\n\\\\n Display help text\\\\n\\\\n --help-web | --hw (boolean)\\\\n\\\\n Display HTML help in browser\\\\n\\\\n EXAMPLES\\\\n --------\\\\n\\\\n - Issue the TSO command \\\\\\"status\\\\\\" to display information about\\\\n jobs for your user ID.:\\\\n\\\\n $ zowe zos-tso issue command \\\\\\"status\\\\\\"\\\\n\\\\n\\" + \\"data\\": \\"\\\\n COMMAND NAME\\\\n ------------\\\\n\\\\n command | cmd\\\\n\\\\n DESCRIPTION\\\\n -----------\\\\n\\\\n Creates a TSO address space, issues a TSO command through the newly created\\\\n address space, waits for the READY prompt to print the response, and terminates\\\\n the TSO address space. All response data are returned to the user up to (but not\\\\n including) the TSO 'READY' prompt.\\\\n\\\\n USAGE\\\\n -----\\\\n\\\\n zowe zos-tso issue command [options]\\\\n\\\\n POSITIONAL ARGUMENTS\\\\n --------------------\\\\n\\\\n commandText\\\\t\\\\t (string)\\\\n\\\\n The TSO command to issue.\\\\n\\\\n OPTIONS\\\\n -------\\\\n\\\\n --suppress-startup-messages | --ssm (boolean)\\\\n\\\\n Suppress console messages from start of address space.\\\\n\\\\n Default value: true\\\\n\\\\n --stateful | --sf (boolean)\\\\n\\\\n Statefulness of address space created for TSO command. This option is not\\\\n supported when --suppress-startup-messages is set to false.\\\\n\\\\n Default value: false\\\\n\\\\n TSO ADDRESS SPACE OPTIONS\\\\n -------------------------\\\\n\\\\n --account | -a (string)\\\\n\\\\n Your z/OS TSO/E accounting information.\\\\n\\\\n --character-set | --cs (string)\\\\n\\\\n Character set for address space to convert messages and responses from UTF-8 to\\\\n EBCDIC.\\\\n\\\\n Default value: 697\\\\n\\\\n --code-page | --cp (string)\\\\n\\\\n Codepage value for TSO/E address space to convert messages and responses from\\\\n UTF-8 to EBCDIC.\\\\n\\\\n Default value: 1047\\\\n\\\\n --columns | --cols (number)\\\\n\\\\n The number of columns on a screen.\\\\n\\\\n Default value: 80\\\\n\\\\n --logon-procedure | -l (string)\\\\n\\\\n The logon procedure to use when creating TSO procedures on your behalf.\\\\n\\\\n Default value: IZUFPROC\\\\n\\\\n --region-size | --rs (number)\\\\n\\\\n Region size for the TSO/E address space.\\\\n\\\\n Default value: 4096\\\\n\\\\n --rows (number)\\\\n\\\\n The number of rows on a screen.\\\\n\\\\n Default value: 24\\\\n\\\\n ZOSMF CONNECTION OPTIONS\\\\n ------------------------\\\\n\\\\n --host | -H (string)\\\\n\\\\n The z/OSMF server host name.\\\\n\\\\n --port | -P (number)\\\\n\\\\n The z/OSMF server port.\\\\n\\\\n Default value: 443\\\\n\\\\n --user | -u (string)\\\\n\\\\n Mainframe (z/OSMF) user name, which can be the same as your TSO login.\\\\n\\\\n --password | --pass | --pw (string)\\\\n\\\\n Mainframe (z/OSMF) password, which can be the same as your TSO password.\\\\n\\\\n --reject-unauthorized | --ru (boolean)\\\\n\\\\n Reject self-signed certificates.\\\\n\\\\n Default value: true\\\\n\\\\n --base-path | --bp (string)\\\\n\\\\n The base path for your API mediation layer instance. Specify this option to\\\\n prepend the base path to all z/OSMF resources when making REST requests. Do not\\\\n specify this option if you are not using an API mediation layer.\\\\n\\\\n --protocol (string)\\\\n\\\\n The protocol used (HTTP or HTTPS)\\\\n\\\\n Default value: https\\\\n Allowed values: http, https\\\\n\\\\n --cert-file (local file path)\\\\n\\\\n The file path to a certificate file to use for authentication\\\\n\\\\n --cert-key-file (local file path)\\\\n\\\\n The file path to a certificate key file to use for authentication\\\\n\\\\n PROFILE OPTIONS\\\\n ---------------\\\\n\\\\n --zosmf-profile | --zosmf-p (string)\\\\n\\\\n The name of a (zosmf) profile to load for this command execution.\\\\n\\\\n --tso-profile | --tso-p (string)\\\\n\\\\n The name of a (tso) profile to load for this command execution.\\\\n\\\\n --base-profile | --base-p (string)\\\\n\\\\n The name of a (base) profile to load for this command execution.\\\\n\\\\n BASE CONNECTION OPTIONS\\\\n -----------------------\\\\n\\\\n --token-type | --tt (string)\\\\n\\\\n The type of token to get and use for the API. Omit this option to use the\\\\n default token type, which is provided by 'zowe auth login'.\\\\n\\\\n --token-value | --tv (string)\\\\n\\\\n The value of the token to pass to the API.\\\\n\\\\n GLOBAL OPTIONS\\\\n --------------\\\\n\\\\n --show-inputs-only (boolean)\\\\n\\\\n Show command inputs and do not run the command\\\\n\\\\n --response-format-json | --rfj (boolean)\\\\n\\\\n Produce JSON formatted data from a command\\\\n\\\\n --help | -h (boolean)\\\\n\\\\n Display help text\\\\n\\\\n --help-web | --hw (boolean)\\\\n\\\\n Display HTML help in browser\\\\n\\\\n EXAMPLES\\\\n --------\\\\n\\\\n - Issue the TSO command \\\\\\"status\\\\\\" to display information about\\\\n jobs for your user ID.:\\\\n\\\\n $ zowe zos-tso issue command \\\\\\"status\\\\\\"\\\\n\\\\n\\" }" `; diff --git a/packages/cli/__tests__/zostso/__unit__/issue/command/Command.handler.unit.test.ts b/packages/cli/__tests__/zostso/__unit__/issue/command/Command.handler.unit.test.ts index 7d82689383..b93aef6aa9 100644 --- a/packages/cli/__tests__/zostso/__unit__/issue/command/Command.handler.unit.test.ts +++ b/packages/cli/__tests__/zostso/__unit__/issue/command/Command.handler.unit.test.ts @@ -9,6 +9,7 @@ * */ +/* eslint-disable deprecation/deprecation */ jest.mock("../../../../../../zostso/lib/IssueTso"); import { IssueTso } from "@zowe/zos-tso-for-zowe-sdk"; import { IHandlerParameters, ImperativeError } from "@zowe/imperative"; @@ -24,7 +25,7 @@ const DEFAULT_PARAMETERS: IHandlerParameters = mockHandlerParameters({ ...UNIT_TEST_TSO_PROF_OPTS }, positionals: ["zos-tso", "issue", "address-space"], - definition: CommandDefinition + definition: CommandDefinition, }); describe("issue command handler tests", () => { @@ -34,9 +35,9 @@ describe("issue command handler tests", () => { }); it("should issue command", async () => { - IssueTso.issueTsoCommand = jest.fn((session, acc, cmd, prof) => { - expect(prof).toBeDefined(); - expect(prof).toMatchSnapshot(); + IssueTso.issueTsoCmd = jest.fn((session, cmd) => { + expect(session).toBeDefined(); + expect(session.ISession).toMatchSnapshot(); return StartTsoData.SAMPLE_ISSUE_RESPONSE_WITH_MSG; }); const handler = new Command.default(); @@ -44,14 +45,14 @@ describe("issue command handler tests", () => { params.arguments.acc = "acc"; params.arguments.cmd = "time"; await handler.process(params); - expect(IssueTso.issueTsoCommand).toHaveBeenCalledTimes(1); + expect(IssueTso.issueTsoCmd).toHaveBeenCalledTimes(1); }); it("should be able respond with error message", async () => { const failMessage = "IZUG1126E: z/OSMF cannot correlate the request for key \"ZOSMFAD-SYS2-55-aaakaaac\"\n" + "with an active z/OS application session."; let error; - IssueTso.issueTsoCommand = jest.fn((session, servletKey) => { + IssueTso.issueTsoCmd = jest.fn((session, servletKey) => { throw new ImperativeError({msg: failMessage}); }); const handler = new Command.default(); @@ -63,7 +64,7 @@ describe("issue command handler tests", () => { } catch (thrownError) { error = thrownError; } - expect(IssueTso.issueTsoCommand).toHaveBeenCalledTimes(1); + expect(IssueTso.issueTsoCmd).toHaveBeenCalledTimes(1); expect(error).toBeDefined(); expect(error instanceof ImperativeError).toBe(true); expect(error.message).toMatchSnapshot(); diff --git a/packages/cli/__tests__/zostso/__unit__/issue/command/__snapshots__/Command.definition.unit.test.ts.snap b/packages/cli/__tests__/zostso/__unit__/issue/command/__snapshots__/Command.definition.unit.test.ts.snap index 0b53de6316..80cef8874f 100644 --- a/packages/cli/__tests__/zostso/__unit__/issue/command/__snapshots__/Command.definition.unit.test.ts.snap +++ b/packages/cli/__tests__/zostso/__unit__/issue/command/__snapshots__/Command.definition.unit.test.ts.snap @@ -18,10 +18,20 @@ Object { "aliases": Array [ "ssm", ], + "defaultValue": true, "description": "Suppress console messages from start of address space.", "name": "suppress-startup-messages", "type": "boolean", }, + Object { + "aliases": Array [ + "sf", + ], + "defaultValue": false, + "description": "Statefulness of address space created for TSO command. This option is not supported when --suppress-startup-messages is set to false.", + "name": "stateful", + "type": "boolean", + }, Object { "aliases": Array [ "a", diff --git a/packages/cli/__tests__/zostso/__unit__/issue/command/__snapshots__/Command.handler.unit.test.ts.snap b/packages/cli/__tests__/zostso/__unit__/issue/command/__snapshots__/Command.handler.unit.test.ts.snap index 9b7887bb8f..1975c48fc9 100644 --- a/packages/cli/__tests__/zostso/__unit__/issue/command/__snapshots__/Command.handler.unit.test.ts.snap +++ b/packages/cli/__tests__/zostso/__unit__/issue/command/__snapshots__/Command.handler.unit.test.ts.snap @@ -7,13 +7,17 @@ with an active z/OS application session." exports[`issue command handler tests should issue command 1`] = ` Object { - "account": "fake", - "characterSet": undefined, - "codePage": undefined, - "columns": undefined, - "logonProcedure": undefined, - "regionSize": undefined, - "rows": undefined, + "base64EncodedAuth": "c29tZW9uZTpmYWtl", + "basePath": "", + "hostname": "somewhere.com", + "password": "fake", + "port": "43443", + "protocol": "https", + "rejectUnauthorized": true, + "secureProtocol": "SSLv23_method", + "strictSSL": true, + "type": "basic", + "user": "someone", } `; diff --git a/packages/cli/package.json b/packages/cli/package.json index bad08f2942..1084a502d9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/cli", - "version": "8.0.0", + "version": "8.0.1", "zoweVersion": "v3.0.0", "description": "Zowe CLI is a command line interface (CLI) that provides a simple and streamlined way to interact with IBM z/OS.", "author": "Zowe", @@ -58,17 +58,17 @@ "preshrinkwrap": "node ../../scripts/rewriteShrinkwrap.js" }, "dependencies": { - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0", - "@zowe/provisioning-for-zowe-sdk": "8.0.0", - "@zowe/zos-console-for-zowe-sdk": "8.0.0", - "@zowe/zos-files-for-zowe-sdk": "8.0.0", - "@zowe/zos-jobs-for-zowe-sdk": "8.0.0", - "@zowe/zos-logs-for-zowe-sdk": "8.0.0", - "@zowe/zos-tso-for-zowe-sdk": "8.0.0", - "@zowe/zos-uss-for-zowe-sdk": "8.0.0", - "@zowe/zos-workflows-for-zowe-sdk": "8.0.0", - "@zowe/zosmf-for-zowe-sdk": "8.0.0", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1", + "@zowe/provisioning-for-zowe-sdk": "8.0.1", + "@zowe/zos-console-for-zowe-sdk": "8.0.1", + "@zowe/zos-files-for-zowe-sdk": "8.0.1", + "@zowe/zos-jobs-for-zowe-sdk": "8.0.1", + "@zowe/zos-logs-for-zowe-sdk": "8.0.1", + "@zowe/zos-tso-for-zowe-sdk": "8.0.1", + "@zowe/zos-uss-for-zowe-sdk": "8.0.1", + "@zowe/zos-workflows-for-zowe-sdk": "8.0.1", + "@zowe/zosmf-for-zowe-sdk": "8.0.1", "find-process": "1.4.7", "lodash": "4.17.21", "minimatch": "9.0.5", @@ -78,7 +78,7 @@ "@types/diff": "^5.0.9", "@types/lodash": "^4.17.6", "@types/tar": "^6.1.11", - "@zowe/cli-test-utils": "8.0.0", + "@zowe/cli-test-utils": "8.0.1", "comment-json": "^4.2.3", "strip-ansi": "^6.0.1", "which": "^4.0.0" diff --git a/packages/cli/src/zostso/issue/command/Command.definition.ts b/packages/cli/src/zostso/issue/command/Command.definition.ts index b6c07bdb2a..b65d43932d 100644 --- a/packages/cli/src/zostso/issue/command/Command.definition.ts +++ b/packages/cli/src/zostso/issue/command/Command.definition.ts @@ -34,10 +34,25 @@ export const CommandDefinition: ICommandDefinition = { ], options: ([ { + // Old API behavior will be utilized upon specifying --ssm to be false, + // otherwise try new API and if it fails, fallback to old API. + + // Specifying --ssm to be false makes the value of --stateful have no impact on + // behavior since old API behavior does not utilize statefulness. name: "suppress-startup-messages", aliases: ["ssm"], type: "boolean", - description: "Suppress console messages from start of address space." + description: "Suppress console messages from start of address space.", + defaultValue: true + }, + { + // --stateful has no impact if --suppress-startup-messages is set to false. + name: "stateful", + aliases: ["sf"], + type: "boolean", + description:"Statefulness of address space created for TSO command." + + " This option is not supported when --suppress-startup-messages is set to false.", + defaultValue: false } ] as ICommandOptionDefinition[]).concat(TsoProfileConstants.TSO_PROFILE_OPTIONS), examples: [ diff --git a/packages/cli/src/zostso/issue/command/Command.handler.ts b/packages/cli/src/zostso/issue/command/Command.handler.ts index 9afde947c2..61d907093a 100644 --- a/packages/cli/src/zostso/issue/command/Command.handler.ts +++ b/packages/cli/src/zostso/issue/command/Command.handler.ts @@ -1,17 +1,21 @@ /* -* This program and the accompanying materials are made available under the terms of the -* Eclipse Public License v2.0 which accompanies this distribution, and is available at -* https://www.eclipse.org/legal/epl-v20.html -* -* SPDX-License-Identifier: EPL-2.0 -* -* Copyright Contributors to the Zowe Project. -* -*/ + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + * + */ import { IHandlerParameters } from "@zowe/imperative"; -import { IIssueResponse, IssueTso, ZosTsoBaseHandler } from "@zowe/zos-tso-for-zowe-sdk"; - +import { + IIssueResponse, + IssueTso, + ZosTsoBaseHandler, +} from "@zowe/zos-tso-for-zowe-sdk"; +import chalk = require("chalk"); /** * Handler to issue command to TSO address space * @export @@ -19,21 +23,30 @@ import { IIssueResponse, IssueTso, ZosTsoBaseHandler } from "@zowe/zos-tso-for-z * @implements {ICommandHandler} */ export default class Handler extends ZosTsoBaseHandler { - // Process the command and produce the TSO response public async processCmd(params: IHandlerParameters) { - // Issue the TSO command - const response: IIssueResponse = await IssueTso.issueTsoCommand( + const response: IIssueResponse = await IssueTso.issueTsoCmd( this.mSession, - params.arguments.account, params.arguments.commandText, - this.mTsoStart); + { + isStateful: params.arguments.stateful, + suppressStartupMessages: + params.arguments.suppressStartupMessages, + addressSpaceOptions: this.mTsoStart + } + ); // If requested, suppress the startup - if (!params.arguments.suppressStartupMessages) { + if ( + !params.arguments.suppressStartupMessages && + response.startResponse != null + ) { this.console.log(response.startResponse.messages); } + if(response. zosmfResponse?.[0]?.servletKey) + this.console.log(`${chalk.yellow("Servlet Key: ")}${response.zosmfResponse[0].servletKey}`); + this.console.log(response.commandResponse); // Return as an object when using --response-format-json this.data.setObj(response); diff --git a/packages/core/package.json b/packages/core/package.json index ca25f38371..bcfeb6dc98 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/core-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Core libraries shared by Zowe SDK packages", "author": "Zowe", "license": "EPL-2.0", @@ -49,8 +49,8 @@ "string-width": "^4.2.3" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/imperative": "^8.0.0-next" diff --git a/packages/imperative/CHANGELOG.md b/packages/imperative/CHANGELOG.md index 357a55819b..9b4d8278c8 100644 --- a/packages/imperative/CHANGELOG.md +++ b/packages/imperative/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to the Imperative package will be documented in this file. +## Recent Changes + +- Enhancement: Added the ability to specify a profile with the `zowe config secure` command. This allows the user to prompt for the secure values of the specified profile. [#1890] https://github.com/zowe/zowe-cli/issues/1890 + +## `8.0.1` + +- BugFix: Removed Secrets SDK requirement when Imperative is a bundled dependency. [#2276](https://github.com/zowe/zowe-cli/issues/2276) + ## `8.0.0` - MAJOR: v8.0.0 Release @@ -11,6 +19,7 @@ All notable changes to the Imperative package will be documented in this file. - Update: Final prerelease - Update: See `5.27.1` for details + ## `8.0.0-next.202408301809` - LTS Breaking: Removed the following obsolete V1 profile classes/functions: diff --git a/packages/imperative/package.json b/packages/imperative/package.json index e67baa4109..d9fe7a3068 100644 --- a/packages/imperative/package.json +++ b/packages/imperative/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/imperative", - "version": "8.0.0", + "version": "8.0.1", "description": "framework for building configurable CLIs", "author": "Zowe", "license": "EPL-2.0", diff --git a/packages/imperative/src/config/src/ConvertV1Profiles.ts b/packages/imperative/src/config/src/ConvertV1Profiles.ts index 4669d2f8dd..7fcc7e1bd5 100644 --- a/packages/imperative/src/config/src/ConvertV1Profiles.ts +++ b/packages/imperative/src/config/src/ConvertV1Profiles.ts @@ -20,7 +20,7 @@ import { IConfig } from "./doc/IConfig"; import { CredentialManagerFactory } from "../../security"; import { IConvertV1ProfOpts, ConvertMsg, ConvertMsgFmt, IConvertV1ProfResult } from "./doc/IConvertV1Profiles"; import { IImperativeOverrides } from "../../imperative/src/doc/IImperativeOverrides"; -import { keyring } from "@zowe/secrets-for-zowe-sdk"; +import type { keyring } from "@zowe/secrets-for-zowe-sdk"; import { AppSettings } from "../../settings"; import { ISettingsFile } from "../../settings/src/doc/ISettingsFile"; import { ImperativeConfig } from "../../utilities"; diff --git a/packages/imperative/src/imperative/__tests__/config/cmd/secure/secure.handler.unit.test.ts b/packages/imperative/src/imperative/__tests__/config/cmd/secure/secure.handler.unit.test.ts index c3194cc9c6..e4daed587d 100644 --- a/packages/imperative/src/imperative/__tests__/config/cmd/secure/secure.handler.unit.test.ts +++ b/packages/imperative/src/imperative/__tests__/config/cmd/secure/secure.handler.unit.test.ts @@ -10,7 +10,7 @@ */ import { Logger } from "../../../../../logger"; -import { Config } from "../../../../../config/src/Config"; +import { Config } from "../../../../../config"; import { IConfig, IConfigOpts, IConfigProfile } from "../../../../../config"; import { ImperativeConfig } from "../../../../../utilities"; import { IImperativeConfig } from "../../../../src/doc/IImperativeConfig"; @@ -760,7 +760,7 @@ describe("Configuration Secure command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeProjPath, JSON.stringify(compObj, null, 4)); // Config }); - it("should fail to invoke auth handler if it throws an error", async () => { + it("should only prompt for profiles that matched profile param", async () => { const eco = lodash.cloneDeep(expectedProjConfigObjectWithToken); readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -792,5 +792,112 @@ describe("Configuration Secure command handler", () => { expect(keytarSetPasswordSpy).toHaveBeenCalledTimes(0); expect(writeFileSyncSpy).toHaveBeenCalledTimes(0); }); + describe("profile param tests", () => { + let handler: SecureHandler; + let params: any; + let myPromptSpy: jest.SpyInstance; + + beforeEach(() => { + handler = new SecureHandler(); + params = getIHandlerParametersObject(); + + params.arguments.userConfig = true; + params.arguments.globalConfig = true; + + // Mock the console prompt to return an empty string + myPromptSpy = jest.spyOn(params.response.console, "prompt").mockResolvedValue(""); + + // Reset spies + keytarGetPasswordSpy.mockReturnValue(fakeSecureData); + keytarSetPasswordSpy.mockImplementation(); + keytarDeletePasswordSpy.mockImplementation(); + readFileSyncSpy = jest.spyOn(fs, "readFileSync"); + writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); + existsSyncSpy = jest.spyOn(fs, "existsSync"); + writeFileSyncSpy.mockImplementation(); + }); + + const runTest = async (profile: string, secureFields: string[], expectedPromptTimes: number, expectedSecureField: string) => { + params.arguments.profile = profile; + + // Mock fs calls + const eco = lodash.cloneDeep(expectedGlobalUserConfigObject); + eco.$schema = "./fakeapp.schema.json"; + readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); + existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); + searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); + await setupConfigToLoad(undefined, configOpts); + + // Setup mock secure fields + jest.spyOn(ImperativeConfig.instance.config.api.secure, "secureFields").mockReturnValue(secureFields); + + let caughtError; + try { + await handler.process(params); + } catch (error) { + caughtError = error; + } + + // Verify prompt count and inclusion of expected secure fields + expect(myPromptSpy).toHaveBeenCalledTimes(expectedPromptTimes); + if (expectedPromptTimes > 0) { + expect(myPromptSpy).toHaveBeenCalledWith(expect.stringContaining(expectedSecureField), { "hideText": true }); + } + expect(caughtError).toBeUndefined(); + }; + + it("should only prompt for secure values that match the profile passed in through params", async () => { + await runTest( + "GoodProfile", + [ + "profiles.noMatchProfile.properties.tokenValue", + "profiles.GoodProfile.properties.tokenValue", + "profiles.abcdefg.properties.tokenValue" + ], + 1, + "profiles.GoodProfile.properties.tokenValue" + ); + }); + + it("should only prompt for secure values that match the profile passed in through params - nested profile", async () => { + await runTest( + "lpar1.GoodProfile", + [ + "profiles.noMatchProfile.properties.tokenValue", + "profiles.lpar1.profiles.GoodProfile.properties.tokenValue", + "profiles.abcdefg.properties.tokenValue" + ], + 1, + "profiles.lpar1.profiles.GoodProfile.properties.tokenValue" + ); + }); + + it("should only prompt for secure values that match the profile passed in through params - ignore casing", async () => { + await runTest( + "gOODpROFILE", + [ + "profiles.noMatchProfile.properties.tokenValue", + "profiles.GoodProfile.properties.tokenValue", + "profiles.abcdefg.properties.tokenValue" + ], + 1, + "profiles.GoodProfile.properties.tokenValue" + ); + }); + + it("should prompt for all secure values given a profile in which no secure profile value matches", async () => { + await runTest( + "noMatchProfile", + [ + "profiles.lpar1.profiles.test.properties.tokenValue", + "profiles.GoodProfile.properties.tokenValue", + "profiles.abcdefg.properties.tokenValue" + ], + 3, + "profiles.lpar1.profiles.test.properties.tokenValue" + ); + }); + }); + }); }); diff --git a/packages/imperative/src/imperative/src/config/cmd/secure/secure.definition.ts b/packages/imperative/src/imperative/src/config/cmd/secure/secure.definition.ts index db10cab504..a89816cbfc 100644 --- a/packages/imperative/src/imperative/src/config/cmd/secure/secure.definition.ts +++ b/packages/imperative/src/imperative/src/config/cmd/secure/secure.definition.ts @@ -39,6 +39,13 @@ export const secureDefinition: ICommandDefinition = { aliases: ["p"], type: "boolean", defaultValue: false + }, + { + name: "profile", + description: "Specify the profile for which you want to configure secure values.", + type: "string", + aliases: ["pf"], + defaultValue: null } ], examples: [ diff --git a/packages/imperative/src/imperative/src/config/cmd/secure/secure.handler.ts b/packages/imperative/src/imperative/src/config/cmd/secure/secure.handler.ts index 582be79f75..dde80d4f02 100644 --- a/packages/imperative/src/imperative/src/config/cmd/secure/secure.handler.ts +++ b/packages/imperative/src/imperative/src/config/cmd/secure/secure.handler.ts @@ -1,21 +1,29 @@ /* -* This program and the accompanying materials are made available under the terms of the -* Eclipse Public License v2.0 which accompanies this distribution, and is available at -* https://www.eclipse.org/legal/epl-v20.html -* -* SPDX-License-Identifier: EPL-2.0 -* -* Copyright Contributors to the Zowe Project. -* -*/ + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + * + */ -import { ICommandArguments, ICommandHandler, IHandlerParameters } from "../../../../../cmd"; +import { + ICommandArguments, + ICommandHandler, + IHandlerParameters, +} from "../../../../../cmd"; import { Config, ConfigConstants, ConfigSchema } from "../../../../../config"; import { ConfigAutoStore } from "../../../../../config/src/ConfigAutoStore"; import { ConfigUtils } from "../../../../../config/src/ConfigUtils"; import { ImperativeError } from "../../../../../error"; import { Logger } from "../../../../../logger"; -import { ConnectionPropsForSessCfg, ISession, Session } from "../../../../../rest"; +import { + ConnectionPropsForSessCfg, + ISession, + Session, +} from "../../../../../rest"; import { ImperativeConfig } from "../../../../../utilities"; export default class SecureHandler implements ICommandHandler { @@ -23,7 +31,6 @@ export default class SecureHandler implements ICommandHandler { * The parameters object passed to the command handler. */ private params: IHandlerParameters; - /** * Process the command and input. * @@ -44,26 +51,60 @@ export default class SecureHandler implements ICommandHandler { const prunedFiles = config.api.secure.rmUnusedProps(); if (prunedFiles.length > 0) { await config.api.secure.directSave(); - params.response.console.log("Deleted secure properties for the following missing files:\n\t" + prunedFiles.join("\n\t") + "\n"); + params.response.console.log( + "Deleted secure properties for the following missing files:\n\t" + + prunedFiles.join("\n\t") + + "\n" + ); } } // Create the config, load the secure values, and activate the desired layer - config.api.layers.activate(params.arguments.userConfig, params.arguments.globalConfig); - const secureProps: string[] = config.api.secure.secureFields(); + config.api.layers.activate( + params.arguments.userConfig, + params.arguments.globalConfig + ); + let secureProps: string[] = config.api.secure.secureFields(); if (secureProps.length === 0) { - params.response.console.log("No secure properties found in your config"); + params.response.console.log( + "No secure properties found in your config" + ); return; } - + if (params.arguments.profile) { + const filteredSecureProps = secureProps.filter( + (prop) => + config.api.profiles + .getProfileNameFromPath(prop) + .toLowerCase() === + params.arguments.profile.toLowerCase() + ); + if (filteredSecureProps.length === 0 && secureProps.length > 0) { + params.response.console.log( + `No secure properties from profile '${params.arguments.profile}' found.` + ); + } else { + secureProps = filteredSecureProps; + } + } // Prompt for values designated as secure for (const propName of secureProps) { if (propName.endsWith(".tokenValue")) { - params.response.console.log(`Processing secure properties for profile: ${config.api.profiles.getProfileNameFromPath(propName)}`); - let propValue = await this.handlePromptForAuthToken(config, propName); + params.response.console.log( + `Processing secure properties for profile: ${config.api.profiles.getProfileNameFromPath( + propName + )}` + ); + let propValue = await this.handlePromptForAuthToken( + config, + propName + ); if (propValue === undefined) { - propValue = await params.response.console.prompt(`Enter ${propName} ${ConfigConstants.SKIP_PROMPT}`, {hideText: true}); + propValue = await params.response.console.prompt( + `Enter ${propName} ${ConfigConstants.SKIP_PROMPT}`, + { hideText: true } + ); } // Save the value in the config securely @@ -71,11 +112,20 @@ export default class SecureHandler implements ICommandHandler { config.set(propName, propValue, { secure: true }); } } else { - let propValue = await params.response.console.prompt(`Enter ${propName} ${ConfigConstants.SKIP_PROMPT}`, { hideText: true }); + let propValue = await params.response.console.prompt( + `Enter ${propName} ${ConfigConstants.SKIP_PROMPT}`, + { hideText: true } + ); // Save the value in the config securely if (propValue) { - propValue = ConfigUtils.coercePropValue(propValue, ConfigSchema.findPropertyType(propName, config.properties)); + propValue = ConfigUtils.coercePropValue( + propValue, + ConfigSchema.findPropertyType( + propName, + config.properties + ) + ); config.set(propName, propValue, { secure: true }); } } @@ -93,31 +143,57 @@ export default class SecureHandler implements ICommandHandler { * @param propPath JSON property path of the auth token * @returns Token value, or undefined if none was obtained */ - private async handlePromptForAuthToken(config: Config, propPath: string): Promise { + private async handlePromptForAuthToken( + config: Config, + propPath: string + ): Promise { const profilePath = propPath.slice(0, propPath.indexOf(".properties")); - const authHandlerClass = ConfigAutoStore.findAuthHandlerForProfile(profilePath, this.params.arguments); + const authHandlerClass = ConfigAutoStore.findAuthHandlerForProfile( + profilePath, + this.params.arguments + ); if (authHandlerClass != null) { const api = authHandlerClass.getAuthHandlerApi(); if (api.promptParams.serviceDescription != null) { - this.params.response.console.log(`Logging in to ${api.promptParams.serviceDescription} ${ConfigConstants.SKIP_PROMPT}`); + this.params.response.console.log( + `Logging in to ${api.promptParams.serviceDescription} ${ConfigConstants.SKIP_PROMPT}` + ); } - const profile = config.api.profiles.get(profilePath.replace(/profiles\./g, ""), false); - const sessCfg: ISession = api.createSessCfg(profile as ICommandArguments); - const sessCfgWithCreds = await ConnectionPropsForSessCfg.addPropsOrPrompt(sessCfg, profile as ICommandArguments, - { parms: this.params, doPrompting: true, requestToken: true, ...api.promptParams }); - Logger.getAppLogger().info(`Fetching ${profile.tokenType} for ${propPath}`); + const profile = config.api.profiles.get( + profilePath.replace(/profiles\./g, ""), + false + ); + const sessCfg: ISession = api.createSessCfg( + profile as ICommandArguments + ); + const sessCfgWithCreds = + await ConnectionPropsForSessCfg.addPropsOrPrompt( + sessCfg, + profile as ICommandArguments, + { + parms: this.params, + doPrompting: true, + requestToken: true, + ...api.promptParams, + } + ); + Logger.getAppLogger().info( + `Fetching ${profile.tokenType} for ${propPath}` + ); if (ConnectionPropsForSessCfg.sessHasCreds(sessCfgWithCreds)) { try { - const tokenValue = await api.sessionLogin(new Session(sessCfgWithCreds)); + const tokenValue = await api.sessionLogin( + new Session(sessCfgWithCreds) + ); this.params.response.console.log("Logged in successfully"); return tokenValue; } catch (error) { throw new ImperativeError({ msg: `Failed to fetch ${profile.tokenType} for ${propPath}: ${error.message}`, - causeErrors: error + causeErrors: error, }); } } else { diff --git a/packages/imperative/src/security/src/DefaultCredentialManager.ts b/packages/imperative/src/security/src/DefaultCredentialManager.ts index a89669c448..23b1da384d 100644 --- a/packages/imperative/src/security/src/DefaultCredentialManager.ts +++ b/packages/imperative/src/security/src/DefaultCredentialManager.ts @@ -13,7 +13,7 @@ import { AbstractCredentialManager, SecureCredential } from "./abstract/Abstract import { ImperativeError } from "../../error"; import { Logger } from "../../logger"; -import { keyring as keytar } from "@zowe/secrets-for-zowe-sdk"; // Used for typing purposes only +import type { keyring as keytar } from "@zowe/secrets-for-zowe-sdk"; // Used for typing purposes only /** * Default Credential Manager is our implementation of the Imperative Credential Manager. This manager invokes methods diff --git a/packages/provisioning/package.json b/packages/provisioning/package.json index 8dfa2b2b6d..be3814bc4b 100644 --- a/packages/provisioning/package.json +++ b/packages/provisioning/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/provisioning-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with the z/OS provisioning APIs", "author": "Zowe", "license": "EPL-2.0", @@ -49,9 +49,9 @@ }, "devDependencies": { "@types/js-yaml": "^4.0.9", - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/secrets/src/keyring/EXTENDERS.md b/packages/secrets/src/keyring/EXTENDERS.md index d2f5511045..a611962403 100644 --- a/packages/secrets/src/keyring/EXTENDERS.md +++ b/packages/secrets/src/keyring/EXTENDERS.md @@ -33,12 +33,12 @@ After the desired functions are imported, feel free to use them in the same fash ```ts getPassword("TestService", "AccountA") -.then((pw) => { + .then((pw) => { console.log("The password for TestService/AccountA is:", pw); -}) -.catch((err) => { + }) + .catch((err) => { console.error("An error occurred!", err.message); -}); + }); ``` **Examples:** @@ -63,6 +63,18 @@ await keyring.deletePassword("TestService", "AccountA"); ## Webpacking/bundling alongside your project +**Note for Webpack users:** If you do **_not_** want to use the Secrets SDK, but have Imperative modules imported that reference it (such as `ConvertV1Profiles` or `DefaultCredentialManager`), you must define the `@zowe/secrets-for-zowe-sdk` package in the `externals` section of your Webpack config: + +```json +"externals": { + "@zowe/secrets-for-zowe-sdk": "commonjs @zowe/secrets-for-zowe-sdk" +} +``` + +This will prevent Webpack from trying to dynamically resolve the Secrets SDK during compile time. This does not affect the majority of developers, as Webpack omits any unused Imperative modules during the bundling phase. + +--- + Some projects leverage a JavaScript bundler, such as Webpack or Vite, to minify and compress their Node.js packages. While the Secrets SDK does support Webpack, developers who want to bundle the Secrets SDK alongside their package should set up a `prebuilds` folder alongside their package root. @@ -78,7 +90,8 @@ your-pkg/ │ └── (node binaries for Secrets SDK) ``` -If you are using ESbuild or Webpack, consider using a copy plugin to copy the `prebuilds` folder from the Secrets SDK *node_modules* folder: +If you are using ESbuild or Webpack, consider using a copy plug-in to copy the `prebuilds` folder from the Secrets SDK _node_modules_ folder: + - ESbuild: [esbuild-copy-static-files](https://www.npmjs.com/package/esbuild-copy-static-files) - Webpack: [copy-webpack-plugin](https://www.npmjs.com/package/copy-webpack-plugin) @@ -86,9 +99,14 @@ Otherwise, use the Node.js script below (**requires Node 16.7.0 and above**). It ```js const { cpSync } = require("fs"); -cpSync("/path/to/node_modules/@zowe/secrets-for-zowe-sdk/prebuilds", "prebuilds", {force: true, recursive: true}); +cpSync( + "/path/to/node_modules/@zowe/secrets-for-zowe-sdk/prebuilds", + "prebuilds", + { force: true, recursive: true } +); ``` -**Note:** The first argument for `cpSync` will vary, depending on where the *node_modules* folder is located in your environment. + +**Note:** The first argument for `cpSync` will vary, depending on where the _node_modules_ folder is located in your environment. We recommend that developers add this logic to a `prepare` script in their `package.json` to guarantee the binaries are up-to-date after installing dependencies. Save the above script as a JavaScript file (e.g., `scripts/copyKeyringBinaries.js`), and execute the script: @@ -102,7 +120,8 @@ Save the above script as a JavaScript file (e.g., `scripts/copyKeyringBinaries.j ``` If you are bundling a VSCode extension, and are using a `.vscodeignore` file, you must allow the prebuilds folder to be packaged in the VSIX. -Add the following line to your `.vscodeignore` file: +Add the following line to your `.vscodeignore` file: + ``` !prebuilds/** ``` @@ -113,14 +132,16 @@ Some extenders might import `keytar` directly as a dependency. In these cases, e **Take caution when importing** as the import process is slightly different than `node-keytar`: -Before: +Before: + ```js const keytar = require("node-keytar"); // ES6 import: import * as keytar from "node-keytar"; ``` -After: +After: + ```js const { keyring } = require("@zowe/secrets-for-zowe-sdk"); // ES6 import: @@ -137,4 +158,4 @@ import { keyring as keytar } from "@zowe/secrets-for-zowe-sdk"; // Existing code, such as the example below, can remain unchanged with this import alias: keytar.setPassword("Hello", "World", "ExamplePassword"); -``` \ No newline at end of file +``` diff --git a/packages/workflows/package.json b/packages/workflows/package.json index 34d2dcd2d9..369884b4c6 100644 --- a/packages/workflows/package.json +++ b/packages/workflows/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-workflows-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with the z/OS workflows APIs", "author": "Zowe", "license": "EPL-2.0", @@ -45,12 +45,12 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.0.0" + "@zowe/zos-files-for-zowe-sdk": "8.0.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosconsole/package.json b/packages/zosconsole/package.json index ed68b3e5b1..5e43ef36b6 100644 --- a/packages/zosconsole/package.json +++ b/packages/zosconsole/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-console-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with the z/OS console", "author": "Zowe", "license": "EPL-2.0", @@ -45,9 +45,9 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosfiles/package.json b/packages/zosfiles/package.json index e7c0f555d9..d437229aa1 100644 --- a/packages/zosfiles/package.json +++ b/packages/zosfiles/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-files-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with files and data sets on z/OS", "author": "Zowe", "license": "EPL-2.0", @@ -49,10 +49,10 @@ "minimatch": "^9.0.5" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0", - "@zowe/zos-uss-for-zowe-sdk": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1", + "@zowe/zos-uss-for-zowe-sdk": "8.0.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosjobs/package.json b/packages/zosjobs/package.json index 0bf61c145b..2ebfaed552 100644 --- a/packages/zosjobs/package.json +++ b/packages/zosjobs/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-jobs-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with jobs on z/OS", "author": "Zowe", "license": "EPL-2.0", @@ -46,12 +46,12 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.0.0" + "@zowe/zos-files-for-zowe-sdk": "8.0.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zoslogs/package.json b/packages/zoslogs/package.json index c605806a1a..56cdd828b6 100644 --- a/packages/zoslogs/package.json +++ b/packages/zoslogs/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-logs-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with the z/OS logs", "author": "Zowe", "license": "EPL-2.0", @@ -45,9 +45,9 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosmf/CHANGELOG.md b/packages/zosmf/CHANGELOG.md index f9d4fb363a..7b261e2537 100644 --- a/packages/zosmf/CHANGELOG.md +++ b/packages/zosmf/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to the Zowe z/OSMF SDK package will be documented in this file. + +## Recent Changes + +- Enhancement: Created `isZosVersionAtLeast()` function to allow for dynamic behavior based on z/OS version. [#2240](https://github.com/zowe/zowe-cli/pull/2240) + ## `8.0.0` - MAJOR: v8.0.0 Release diff --git a/packages/zosmf/package.json b/packages/zosmf/package.json index 4ab009d7f9..d38e2c9785 100644 --- a/packages/zosmf/package.json +++ b/packages/zosmf/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zosmf-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with the z/OS Management Facility", "author": "Zowe", "license": "EPL-2.0", @@ -44,9 +44,9 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosmf/src/CheckStatus.ts b/packages/zosmf/src/CheckStatus.ts index b378c8ba2b..b9198d905c 100644 --- a/packages/zosmf/src/CheckStatus.ts +++ b/packages/zosmf/src/CheckStatus.ts @@ -36,6 +36,10 @@ export class CheckStatus { return ZosmfRestClient.getExpectJSON(session, infoEndpoint); } + public static async isZosVersionAtLeast(session: AbstractSession, version: string): Promise { + return (await CheckStatus.getZosmfInfo(session)).zosmf_version >= version; + } + /** * Get Log * @returns {Logger} applicationLogger. diff --git a/packages/zosmf/src/constants/Zosmf.constants.ts b/packages/zosmf/src/constants/Zosmf.constants.ts index f8bce03178..86d6f30b1c 100644 --- a/packages/zosmf/src/constants/Zosmf.constants.ts +++ b/packages/zosmf/src/constants/Zosmf.constants.ts @@ -67,6 +67,15 @@ export const ZosmfConstants: { [key: string]: any } = { * @type {string} */ UNABLE_TO_VERIFY_LEAF_SIGNATURE: "UNABLE_TO_VERIFY_LEAF_SIGNATURE" + }, + // https://www.ibm.com/docs/en/zos/2.5.0?topic=service-retrieve-zosmf-information + VERSIONS: { + V2R1: "24", + V2R2: "25", + V2R3: "26", + V2R4: "27", + V2R5: "28", + V3R1: "29", } }; diff --git a/packages/zostso/CHANGELOG.md b/packages/zostso/CHANGELOG.md index ce6553335f..b95b3577a2 100644 --- a/packages/zostso/CHANGELOG.md +++ b/packages/zostso/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to the Zowe z/OS TSO SDK package will be documented in this file. + +## Recent Changes + +- Enhancement: Deprecated `IssueTsoCommand()` function and replaced with `IssueTsoCmd()` for compatibility with z/OS version 2.4. [#2240](https://github.com/zowe/zowe-cli/pull/2240) +- Enhancement: Modified `IIssueReponse` to handle z/OS 2.4 and newer TSO command response. [#2240](https://github.com/zowe/zowe-cli/pull/2240) + - Old API behavior will be utilized upon specifying --ssm to be false, otherwise try new API and if it fails, fallback to old API. + - Specifying --ssm to be false makes the value of --stateful have no impact on behavior since old API behavior does not utilize statefulness. + ## `8.0.0` - MAJOR: v8.0.0 Release diff --git a/packages/zostso/__tests__/__system__/api.TsoIssue.system.test.ts b/packages/zostso/__tests__/__system__/api.TsoIssue.system.test.ts index c2bff8aa3f..22bf915403 100644 --- a/packages/zostso/__tests__/__system__/api.TsoIssue.system.test.ts +++ b/packages/zostso/__tests__/__system__/api.TsoIssue.system.test.ts @@ -9,13 +9,15 @@ * */ +/* eslint-disable deprecation/deprecation */ import { ImperativeError, Session } from "@zowe/imperative"; import { IIssueResponse, IIssueTsoParms, IssueTso, IStartTsoParms } from "../../src"; import * as fs from "fs"; import { ITestEnvironment } from "@zowe/cli-test-utils"; import { TestEnvironment } from "../../../../__tests__/__src__/environment/TestEnvironment"; import { ITestPropertiesSchema } from "../../../../__tests__/__src__/properties/ITestPropertiesSchema"; - +import { IIssueTsoCmdParms } from "../../src"; +import { IIssueTsoCmdOpts } from "../../src/doc/input/IIssueTsoCmdOpts"; let testEnvironment: ITestEnvironment; let systemProperties: ITestPropertiesSchema; let REAL_SESSION: Session; @@ -23,7 +25,8 @@ let ACCOUNT_NUMBER: string; let START_PARAMS: IStartTsoParms; let ISSUE_PARAMS: IIssueTsoParms; - +let COMMAND_PARAMS: IIssueTsoCmdParms; +let AS_OPTIONS: IIssueTsoCmdOpts; describe("IssueTso.issueTsoCommand", () => { beforeAll(async () => { @@ -49,9 +52,18 @@ describe("IssueTso.issueTsoCommand", () => { accountNumber: ACCOUNT_NUMBER, startParams: START_PARAMS }; - + COMMAND_PARAMS = { + command: "TIME" + }; + AS_OPTIONS = { + addressSpaceOptions: START_PARAMS + }; + }); + afterAll(async () => { + AS_OPTIONS = { + addressSpaceOptions: START_PARAMS + }; }); - it("should display time", async () => { let error: ImperativeError; let response: IIssueResponse; @@ -80,4 +92,29 @@ describe("IssueTso.issueTsoCommand", () => { const regex = fs.readFileSync(__dirname + "/__regex__/d_time.regex").toString(); expect(new RegExp(regex, "g").test(response.commandResponse.toString())).toBe(true); }); + it("should display time - issueTsoCmd() - new API - pass", async () => { + let error: ImperativeError; + let response: IIssueResponse; + try { + response = await IssueTso.issueTsoCmd(REAL_SESSION, "TIME", AS_OPTIONS); + } catch (thrownError) { + error = thrownError; + } + expect(error).toBeUndefined(); + expect(response).toBeDefined(); + const regex = fs.readFileSync(__dirname + "/__regex__/d_time.regex").toString(); + expect(new RegExp(regex, "g").test(response.commandResponse.toString())).toBe(true); + }); + it("should fail - issueTsoCmd() - 404 error", async () => { + REAL_SESSION.ISession.basePath = "/bad/path/to/nothing"; + let error: ImperativeError; + let response: IIssueResponse; + try { + response = await IssueTso.issueTsoCmd(REAL_SESSION, "TIME", AS_OPTIONS); + } catch (thrownError) { + error = thrownError; + } + expect(error).toBeDefined(); + expect(response).toBeUndefined(); + }); }); diff --git a/packages/zostso/__tests__/__unit__/IssueTso.unit.test.ts b/packages/zostso/__tests__/__unit__/IssueTso.unit.test.ts index fe6e071827..c381883e53 100644 --- a/packages/zostso/__tests__/__unit__/IssueTso.unit.test.ts +++ b/packages/zostso/__tests__/__unit__/IssueTso.unit.test.ts @@ -1,17 +1,29 @@ /* -* This program and the accompanying materials are made available under the terms of the -* Eclipse Public License v2.0 which accompanies this distribution, and is available at -* https://www.eclipse.org/legal/epl-v20.html -* -* SPDX-License-Identifier: EPL-2.0 -* -* Copyright Contributors to the Zowe Project. -* -*/ - -import { ImperativeError, Session } from "@zowe/imperative"; -import { IIssueTsoParms, ISendResponse, IssueTso, IStartStopResponse, IStartTsoParms, IZosmfTsoResponse, SendTso, - StartTso, StopTso } from "../../src"; + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + * + */ + +/* eslint-disable deprecation/deprecation */ +import { ImperativeConfig, ImperativeError, Session } from "@zowe/imperative"; +import { + IIssueTsoParms, + ISendResponse, + IssueTso, + IStartStopResponse, + IStartTsoParms, + IZosmfTsoResponse, + SendTso, + StartTso, + StopTso, +} from "../../src"; +import { CheckStatus } from "@zowe/zosmf-for-zowe-sdk"; +import { ZosmfRestClient } from "@zowe/core-for-zowe-sdk"; const PRETEND_SESSION = new Session({ user: "user", @@ -19,12 +31,12 @@ const PRETEND_SESSION = new Session({ hostname: "host.com", port: 443, type: "basic", - rejectUnauthorized: false + rejectUnauthorized: false, }); const SEND_RESPONSE = { success: true, zosmfResponse: {}, - commandResponse: "messages" + commandResponse: "messages", }; const PRETEND_REQUIRED_PARMS: IStartTsoParms = { logonProcedure: "IZUFPROC", @@ -33,12 +45,12 @@ const PRETEND_REQUIRED_PARMS: IStartTsoParms = { rows: "24", columns: "80", regionSize: "4096", - account: "DEFAULT" + account: "DEFAULT", }; const PRETEND_ISSUE_PARMS: IIssueTsoParms = { startParams: PRETEND_REQUIRED_PARMS, command: "COMMAND", - accountNumber: "acc" + accountNumber: "acc", }; const ZOSMF_RESPONSE: IZosmfTsoResponse = { servletKey: "ZOSMFAD-SYS2-55-aaakaaac", @@ -47,146 +59,405 @@ const ZOSMF_RESPONSE: IZosmfTsoResponse = { reused: false, timeout: false, sessionID: "0x37", - tsoData: [{ - "TSO MESSAGE": { - VERSION: "0100", - DATA: "ZOSMFAD LOGON IN PROGRESS AT 01:12:04 ON JULY 17, 2017" - } - }] + tsoData: [ + { + "TSO MESSAGE": { + VERSION: "0100", + DATA: "ZOSMFAD LOGON IN PROGRESS AT 01:12:04 ON JULY 17, 2017", + }, + }, + ], }; const START_RESPONSE: IStartStopResponse = { success: true, zosmfTsoResponse: ZOSMF_RESPONSE, - servletKey: ZOSMF_RESPONSE.servletKey + servletKey: ZOSMF_RESPONSE.servletKey, }; +describe("all tests", () => { + describe("TsoIssue issueTsoCommand - failing scenarios", () => { + it("should fail for null account number", async () => { + let error: ImperativeError; + let response: ISendResponse; + try { + response = await IssueTso.issueTsoCommand( + PRETEND_SESSION, + null, + "command" + ); + } catch (thrownError) { + error = thrownError; + } + expect(response).not.toBeDefined(); + expect(error).toBeDefined(); + }); + it("should fail for empty account number", async () => { + let error: ImperativeError; + let response: ISendResponse; -describe("TsoIssue issueTsoCommand - failing scenarios", () => { + try { + response = await IssueTso.issueTsoCommand( + PRETEND_SESSION, + "", + "command" + ); + } catch (thrownError) { + error = thrownError; + } + expect(response).not.toBeDefined(); + expect(error).toBeDefined(); + }); + it("should fail for null command text", async () => { + let error: ImperativeError; + let response: ISendResponse; - it("should fail for null account number", async () => { - let error: ImperativeError; - let response: ISendResponse; + try { + response = await IssueTso.issueTsoCommand( + PRETEND_SESSION, + "acc", + null + ); + } catch (thrownError) { + error = thrownError; + } + expect(response).not.toBeDefined(); + expect(error).toBeDefined(); + }); + it("should fail for empty command text", async () => { + let error: ImperativeError; + let response: ISendResponse; - try { - response = await IssueTso.issueTsoCommand(PRETEND_SESSION, null, "command"); - } catch (thrownError) { - error = thrownError; - } - expect(response).not.toBeDefined(); - expect(error).toBeDefined(); - }); - it("should fail for empty account number", async () => { - let error: ImperativeError; - let response: ISendResponse; - - try { - response = await IssueTso.issueTsoCommand(PRETEND_SESSION, "", "command"); - } catch (thrownError) { - error = thrownError; - } - expect(response).not.toBeDefined(); - expect(error).toBeDefined(); - }); - it("should fail for null command text", async () => { - let error: ImperativeError; - let response: ISendResponse; - - try { - response = await IssueTso.issueTsoCommand(PRETEND_SESSION, "acc", null); - } catch (thrownError) { - error = thrownError; - } - expect(response).not.toBeDefined(); - expect(error).toBeDefined(); - }); - it("should fail for empty command text", async () => { - let error: ImperativeError; - let response: ISendResponse; - - try { - response = await IssueTso.issueTsoCommand(PRETEND_SESSION, "acc", ""); - } catch (thrownError) { - error = thrownError; - } - expect(response).not.toBeDefined(); - expect(error).toBeDefined(); - }); - it("should fail when StartTSO fails", async () => { - let error: ImperativeError; - let response: ISendResponse; - - jest.spyOn(StartTso, "start").mockResolvedValueOnce({ success: false } as any); - - try { - response = await IssueTso.issueTsoCommand(PRETEND_SESSION, "acc", "command"); - } catch (thrownError) { - error = thrownError; - } - expect(StartTso.start).toHaveBeenCalledTimes(1); - expect(response).not.toBeDefined(); - expect(error).toBeDefined(); - expect(error.message).toBe("TSO address space failed to start."); + try { + response = await IssueTso.issueTsoCommand( + PRETEND_SESSION, + "acc", + "" + ); + } catch (thrownError) { + error = thrownError; + } + expect(response).not.toBeDefined(); + expect(error).toBeDefined(); + }); + it("should fail when StartTSO fails", async () => { + jest.spyOn(CheckStatus, "isZosVersionAtLeast").mockReturnValue( + Promise.resolve(false) + ); + let error: ImperativeError; + let response: ISendResponse; + + jest.spyOn(StartTso, "start").mockResolvedValueOnce({ + success: false, + } as any); + jest.spyOn(ImperativeConfig, "instance", "get").mockReturnValue({ + config: { + api: { + profiles: { defaultGet: () => ({ account: "acc" }) }, + }, + }, + } as any); + try { + response = await IssueTso.issueTsoCommand( + PRETEND_SESSION, + "acc", + "command" + ); + } catch (thrownError) { + error = thrownError; + } + expect(response).not.toBeDefined(); + expect(error).toBeDefined(); + expect(error.message).toBe("TSO address space failed to start."); + }); }); -}); -describe("TsoIssue issueTsoCommand", () => { - it("should succeed", async () => { - (StartTso.start as any) = jest.fn(() => { - return new Promise((resolve) => { - process.nextTick(() => { - resolve(START_RESPONSE); + describe("TsoIssue issueTsoCommand - Deprecated API", () => { + it("should succeed", async () => { + jest.spyOn(CheckStatus, "isZosVersionAtLeast").mockReturnValue( + Promise.resolve(false) + ); + (StartTso.start as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve(START_RESPONSE); + }); }); }); - }); - (SendTso.getAllResponses as any) = jest.fn(() => { - return new Promise((resolve) => { - process.nextTick(() => { - resolve({}); + (SendTso.getAllResponses as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve({}); + }); + }); + }); + (SendTso.sendDataToTSOCollect as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve(SEND_RESPONSE); + }); + }); + }); + (StopTso.stop as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve(null); + }); }); }); + jest.spyOn(ImperativeConfig, "instance", "get").mockReturnValue({ + config: { + api: { + profiles: { defaultGet: () => ({ account: "acc" }) }, + }, + }, + } as any); + let error: ImperativeError; + let response: ISendResponse; + try { + response = await IssueTso.issueTsoCommand( + PRETEND_SESSION, + "acc", + "command" + ); + } catch (thrownError) { + error = thrownError; + } + expect(error).not.toBeDefined(); + expect(response).toBeDefined(); }); - (SendTso.sendDataToTSOCollect as any) = jest.fn(() => { - return new Promise((resolve) => { - process.nextTick(() => { - resolve(SEND_RESPONSE); + + it("should succeed (with params)", async () => { + (IssueTso.issueTsoCommand as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve({}); + }); }); }); + jest.spyOn(ImperativeConfig, "instance", "get").mockReturnValue({ + config: { + api: { + profiles: { defaultGet: () => ({ account: "acc" }) }, + }, + }, + } as any); + let error: ImperativeError; + let response: ISendResponse; + try { + response = await IssueTso.issueTsoCommandCommon( + PRETEND_SESSION, + PRETEND_ISSUE_PARMS + ); + } catch (thrownError) { + error = thrownError; + } + expect(error).not.toBeDefined(); + expect(response).toBeDefined(); }); - (StopTso.stop as any) = jest.fn(() => { - return new Promise((resolve) => { - process.nextTick(() => { - resolve(null); + }); + + describe("TsoIssue issueTsoCmd - Revised API", () => { + it("should succeed", async () => { + (StartTso.start as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve(START_RESPONSE); + }); + }); + }); + (SendTso.getAllResponses as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve({}); + }); }); }); + (SendTso.sendDataToTSOCollect as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve(SEND_RESPONSE); + }); + }); + }); + (StopTso.stop as any) = jest.fn(() => { + return new Promise((resolve) => { + process.nextTick(() => { + resolve(null); + }); + }); + }); + + let error: ImperativeError; + let response: ISendResponse; + const zosmfResponse = { + cmdResponse: [ + { + message: + "IKJ56650I TIME-09:42:15 AM. CPU-00:00:00 SERVICE-555 SESSION-00:04:15 SEPTEMBER 4,2024", + }, + { message: "READY " }, + ], + tsoPromptReceived: "Y", + }; + jest.spyOn(ZosmfRestClient, "putExpectJSON").mockReturnValueOnce( + Promise.resolve(zosmfResponse) + ); + jest.spyOn(CheckStatus, "isZosVersionAtLeast").mockReturnValue( + Promise.resolve(true) + ); + try { + response = await IssueTso.issueTsoCmd(PRETEND_SESSION, "TEST"); + } catch (thrownError) { + error = thrownError; + } + expect(error).not.toBeDefined(); + expect(response).toBeDefined(); }); - let error: ImperativeError; - let response: ISendResponse; - try { - response = await IssueTso.issueTsoCommand(PRETEND_SESSION, "acc", "command"); - } catch (thrownError) { - error = thrownError; - } - expect(error).not.toBeDefined(); - expect(response).toBeDefined(); + it("should succeed (with params)", async () => { + let error: ImperativeError; + let response: ISendResponse; + + // Mock the CheckStatus to simulate Z/OS version check + jest.spyOn( + CheckStatus, + "isZosVersionAtLeast" + ).mockReturnValueOnce(Promise.resolve(true)); + const zosmfResponse = { + cmdResponse: [ + { + message: + "IKJ56650I TIME-09:42:15 AM. CPU-00:00:00 SERVICE-555 SESSION-00:04:15 SEPTEMBER 4,2024", + }, + { message: "READY " }, + ], + tsoPromptReceived: "Y", + }; + jest.spyOn(ZosmfRestClient, "putExpectJSON").mockReturnValueOnce( + Promise.resolve(zosmfResponse) + ); + try { + response = await IssueTso.issueTsoCmd( + PRETEND_SESSION, + "command", + { + isStateful: true, + suppressStartupMessages: false, + } + ); + } catch (thrownError) { + error = thrownError; + } + + expect(error).not.toBeDefined(); + expect(response).toBeDefined(); + }); + + it("should utilize new API logic path", async () => { + const zosmfResponse = { + cmdResponse: [ + { + message: + "IKJ56650I TIME-09:42:15 AM. CPU-00:00:00 SERVICE-555 SESSION-00:04:15 SEPTEMBER 4,2024", + }, + { message: "READY " }, + ], + tsoPromptReceived: "Y", + }; + jest.spyOn(ZosmfRestClient, "putExpectJSON").mockReturnValueOnce( + Promise.resolve(zosmfResponse) + ); + let error: ImperativeError; + let response: ISendResponse; + jest.spyOn(CheckStatus, "isZosVersionAtLeast").mockReturnValue( + Promise.resolve(true) + ); + try { + response = await IssueTso.issueTsoCmd(PRETEND_SESSION, "TIME", { + addressSpaceOptions: null, + isStateful: true, + suppressStartupMessages: true, + }); + } catch (thrownError) { + error = thrownError; + } + expect(error).not.toBeDefined(); + expect(response).toBeDefined(); + }); + + it("should throw and handle non-404 error", async () => { + + // Mock ZosmfRestClient to throw an error + jest.spyOn(ZosmfRestClient, "putExpectJSON").mockRejectedValueOnce( + new ImperativeError({ + msg: "status 403", + }) + ); + + let error: ImperativeError; + let response: ISendResponse; + + try { + response = await IssueTso.issueTsoCmd(PRETEND_SESSION, "TIME", { + addressSpaceOptions: null, + isStateful: true, + suppressStartupMessages: true, + }); + } catch (thrownError) { + error = thrownError; + } + + expect(error).toBeDefined(); + expect(error.message).toBe("status 403"); + expect(response).not.toBeDefined(); + }); }); - it("should succeed (with params)", async () => { - (IssueTso.issueTsoCommand as any) = jest.fn(() => { - return new Promise((resolve) => { - process.nextTick(() => { - resolve({}); + describe("TsoIssue issueTsoCmd - failing scenarios", () => { + it("should fail for null command text", async () => { + let error: ImperativeError; + let response: ISendResponse; + jest.spyOn(CheckStatus, "isZosVersionAtLeast").mockReturnValue( + Promise.resolve(true) + ); + try { + response = await IssueTso.issueTsoCmd( + PRETEND_SESSION, + "fake_command", + { + isStateful: true, + suppressStartupMessages: false, + } + ); + } catch (thrownError) { + error = thrownError; + } + expect(response).not.toBeDefined(); + expect(error).toBeDefined(); + }); + it("should fail for empty command text", async () => { + jest.clearAllMocks(); + let error: ImperativeError; + let response: ISendResponse; + + jest.spyOn(ZosmfRestClient, "putExpectJSON").mockRejectedValueOnce( + new ImperativeError({ + msg: "status 403", + }) + ); + jest.spyOn(CheckStatus, "isZosVersionAtLeast").mockReturnValue( + Promise.resolve(true) + ); + try { + response = await IssueTso.issueTsoCmd(PRETEND_SESSION, "", { + isStateful: true, + suppressStartupMessages: false, }); - }); + } catch (thrownError) { + error = thrownError; + } + expect(response).not.toBeDefined(); + expect(error).toBeDefined(); }); - let error: ImperativeError; - let response: ISendResponse; - try { - response = await IssueTso.issueTsoCommandCommon(PRETEND_SESSION, PRETEND_ISSUE_PARMS); - } catch (thrownError) { - error = thrownError; - } - expect(error).not.toBeDefined(); - expect(response).toBeDefined(); }); }); diff --git a/packages/zostso/package.json b/packages/zostso/package.json index fcba1700d4..6a24425eb9 100644 --- a/packages/zostso/package.json +++ b/packages/zostso/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-tso-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with TSO on z/OS", "author": "Zowe", "license": "EPL-2.0", @@ -45,12 +45,12 @@ "prepack": "node ../../scripts/prepareLicenses.js" }, "dependencies": { - "@zowe/zosmf-for-zowe-sdk": "8.0.0" + "@zowe/zosmf-for-zowe-sdk": "8.0.1" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0", - "@zowe/core-for-zowe-sdk": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/core-for-zowe-sdk": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zostso/src/IssueTso.ts b/packages/zostso/src/IssueTso.ts index 77fd62b361..5300781cec 100644 --- a/packages/zostso/src/IssueTso.ts +++ b/packages/zostso/src/IssueTso.ts @@ -1,31 +1,139 @@ /* -* This program and the accompanying materials are made available under the terms of the -* Eclipse Public License v2.0 which accompanies this distribution, and is available at -* https://www.eclipse.org/legal/epl-v20.html -* -* SPDX-License-Identifier: EPL-2.0 -* -* Copyright Contributors to the Zowe Project. -* -*/ + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + * + */ -import { AbstractSession, ImperativeError } from "@zowe/imperative"; +import { AbstractSession, Headers, ImperativeError } from "@zowe/imperative"; import { IStartTsoParms } from "./doc/input/IStartTsoParms"; -import { noAccountNumber, noCommandInput } from "./TsoConstants"; +import { noAccountNumber, noCommandInput, TsoConstants } from "./TsoConstants"; import { SendTso } from "./SendTso"; import { StartTso } from "./StartTso"; import { IIssueResponse } from "./doc/IIssueResponse"; import { StopTso } from "./StopTso"; import { TsoValidator } from "./TsoValidator"; import { IIssueTsoParms } from "./doc/input/IIssueTsoParms"; - +import { CheckStatus, ZosmfConstants } from "@zowe/zosmf-for-zowe-sdk"; +import { ZosmfRestClient } from "@zowe/core-for-zowe-sdk"; +import { IIssueTsoCmdResponse } from "./doc/IIssueTsoCmdResponse"; +import { IIssueTsoCmdOpts } from "./doc/input/IIssueTsoCmdOpts"; /** * Class to handle issue command to TSO * @class IssueTso */ export class IssueTso { + public static async issueTsoCmd( + session: AbstractSession, + command: string, + opts?: IIssueTsoCmdOpts + ): Promise { + let version: string; + opts = opts || {}; + opts.suppressStartupMessages = opts.suppressStartupMessages ?? true; + const versionCheck = await CheckStatus.isZosVersionAtLeast( + session, + ZosmfConstants.VERSIONS.V2R4 + ); + let useNewApi = + opts.addressSpaceOptions == null || + versionCheck && opts.suppressStartupMessages; + + if (useNewApi) { + version = "v1"; + try { + const endpoint = `${TsoConstants.RESOURCE}/${version}/${TsoConstants.RES_START_TSO}`; + const apiResponse = + await ZosmfRestClient.putExpectJSON( + session, + endpoint, + [Headers.APPLICATION_JSON], + { + tsoCmd: command, + cmdState: opts.isStateful + ? "stateful" + : "stateless", + } + ); + const response: IIssueResponse = { + success: true, + startReady: + apiResponse.cmdResponse[ + apiResponse.cmdResponse.length - 1 + ].message.trim() === "READY", + zosmfResponse: [apiResponse as any], + commandResponse: apiResponse.cmdResponse + .map((item) => item.message) + .join("\n"), + }; + return response; + } catch (e) { + if (e.mMessage.includes("status 404")) { + // Set useNewApi to false to handle fallback logic + useNewApi = false; + } else { + // Re-throw for other exceptions + throw e; + } + } + } + // Deprecated API Behavior [former issueTsoCommand() behavior] + if (!useNewApi) { + TsoValidator.validateSession(session); + TsoValidator.validateNotEmptyString( + opts.addressSpaceOptions?.account, + noAccountNumber.message + ); + TsoValidator.validateNotEmptyString( + command as string, + noCommandInput.message + ); + + const response: IIssueResponse = { + success: false, + startResponse: await StartTso.start( + session, + opts.addressSpaceOptions?.account, + opts.addressSpaceOptions || {} + ), + startReady: false, + zosmfResponse: null, + commandResponse: null, + stopResponse: null, + }; + + if (!response.startResponse.success) { + throw new ImperativeError({ + msg: `TSO address space failed to start.`, + additionalDetails: + response.startResponse.failureResponse?.message, + }); + } + + const sendResponse = await SendTso.sendDataToTSOCollect( + session, + response.startResponse.servletKey, + command as string + ); + response.success = sendResponse.success; + response.zosmfResponse = sendResponse.zosmfResponse; + response.commandResponse = sendResponse.commandResponse; + response.stopResponse = await StopTso.stop( + session, + response.startResponse.servletKey + ); + return response; + } else { + throw "ERROR"; + } + } /** + * @deprecated Use issueTsoCmd instead * API method to start a TSO address space, issue a command, collect responses until prompt is reached, and terminate the address space. * @param {AbstractSession} session - z/OSMF connection info * @param {string} accountNumber - accounting info for Jobs @@ -34,50 +142,36 @@ export class IssueTso { * @returns {Promise} IssueTso response object, @see {IIssueResponse} * @memberof IssueTso */ - public static async issueTsoCommand(session: AbstractSession, accountNumber: string, command: string, startParams?: IStartTsoParms) { - - TsoValidator.validateSession(session); - TsoValidator.validateNotEmptyString(accountNumber, noAccountNumber.message); - TsoValidator.validateNotEmptyString(command, noCommandInput.message); - - const response: IIssueResponse = { - success: false, - startResponse: null, - startReady: false, - zosmfResponse: null, - commandResponse: null, - stopResponse: null - }; - response.startResponse = await StartTso.start(session, accountNumber, startParams || {}); - - if (!response.startResponse.success) { - throw new ImperativeError({ - msg: `TSO address space failed to start.`, - additionalDetails: response.startResponse.failureResponse?.message - }); - } - - const sendResponse = await SendTso.sendDataToTSOCollect(session, response.startResponse.servletKey, command); - response.success = sendResponse.success; - response.zosmfResponse = sendResponse.zosmfResponse; - response.commandResponse = sendResponse.commandResponse; - response.stopResponse = await StopTso.stop(session, response.startResponse.servletKey); - return response; + public static async issueTsoCommand( + session: AbstractSession, + accountNumber: string, + command: string, + startParams?: IStartTsoParms + ): Promise { + return await IssueTso.issueTsoCmd(session, command, { + suppressStartupMessages: false, + addressSpaceOptions: { ...startParams, account: accountNumber }, + }); } /** + * @deprecated use issueTsoCmd instead * API method to start a TSO address space with provided parameters, issue a command, * collect responses until prompt is reached, and terminate the address space. * @param {AbstractSession} session - z/OSMF connection info * @param {IIssueTsoParms} commandParms - object with required parameters, @see {IIssueTsoParms} * @returns {Promise} */ - public static async issueTsoCommandCommon(session: AbstractSession, commandParms: IIssueTsoParms) { - - TsoValidator.validateSession(session); - TsoValidator.validateIssueParams(commandParms); - TsoValidator.validateNotEmptyString(commandParms.command, noCommandInput.message); - return IssueTso.issueTsoCommand(session, commandParms.accountNumber, commandParms.command, commandParms.startParams); + public static async issueTsoCommandCommon( + session: AbstractSession, + commandParms: IIssueTsoParms + ): Promise { + return await IssueTso.issueTsoCmd(session, commandParms.command, { + suppressStartupMessages: false, + addressSpaceOptions: { + ...commandParms.startParams, + account: commandParms.accountNumber, + }, + }); } - } diff --git a/packages/zostso/src/doc/IIssueResponse.ts b/packages/zostso/src/doc/IIssueResponse.ts index cc5cb4b82c..b54ab48062 100644 --- a/packages/zostso/src/doc/IIssueResponse.ts +++ b/packages/zostso/src/doc/IIssueResponse.ts @@ -30,7 +30,7 @@ export interface IIssueResponse { * @type {IStartStopResponse} * @memberof ISendResponse */ - startResponse: IStartStopResponses; + startResponse?: IStartStopResponses; /** * Indicates if started TSO containes "READY " message @@ -44,7 +44,7 @@ export interface IIssueResponse { * @type {IStartStopResponse} * @memberof IIssueResponse */ - stopResponse: IStartStopResponse; + stopResponse?: IStartStopResponse; /** * The list of zOSMF send API responses. May issue multiple requests or * to ensure that all messages are collected. Each individual response is placed here. diff --git a/packages/zostso/src/doc/IIssueTsoCmdResponse.ts b/packages/zostso/src/doc/IIssueTsoCmdResponse.ts new file mode 100644 index 0000000000..231c53bc15 --- /dev/null +++ b/packages/zostso/src/doc/IIssueTsoCmdResponse.ts @@ -0,0 +1,45 @@ +/* +* This program and the accompanying materials are made available under the terms of the +* Eclipse Public License v2.0 which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Copyright Contributors to the Zowe Project. +* +*/ + +export interface IIssueTsoCmdResponse { + + /** + * Command response + * @type {{message: string}[]} + * @memberof IIssueTsoCmdResponse + */ + cmdResponse: {message: string}[]; + + /** + * Whether the TSO PROMPT sign is received in the command response. + * "Y": TSO Prompt is Recieved. "N": TSO Prompt is not recieved yet. + * @type {"Y" | "N"} + * @memberof IIssueTsoCmdResponse + */ + tsoPromptReceived: "Y" | "N", + + /** + * Unique identifier for the servlet entry. + * It maps to the TSO/E address space in which the TSO/E command is issued. + * servletKey is returned only when cmdState is stateful for z/OS 2.4 and above + * @type {string} + * @memberof IIssueTsoCmdResponse + */ + servletKey?: string; + + /** + * The result of the response detection request. This is returned when the keyword is specified. + * "Y": Matching record in the response was found. "N": Matching record in the response was not found. + * @type {"Y" | "N"} + * @memberof IIssueTsoCmdResponse + */ + keywordDetected?: "Y" | "N", +} diff --git a/packages/zostso/src/doc/input/IIssueTsoCmdOpts.ts b/packages/zostso/src/doc/input/IIssueTsoCmdOpts.ts new file mode 100644 index 0000000000..55597a07b7 --- /dev/null +++ b/packages/zostso/src/doc/input/IIssueTsoCmdOpts.ts @@ -0,0 +1,40 @@ +/* +* This program and the accompanying materials are made available under the terms of the +* Eclipse Public License v2.0 which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Copyright Contributors to the Zowe Project. +* +*/ + +import { IStartTsoParms } from "./IStartTsoParms"; + +/** + * Interface for command options passed in from command handler + * @export + * @interface IIssueTsoCmdOpts + */ +export interface IIssueTsoCmdOpts { + /** + * address space options for TSO Command + * @type {string} + * @memberof IIssueTsoCmdOpts + */ + addressSpaceOptions?: IStartTsoParms; + + /** + * z/OS >2.4 TSO Command statefulness of address space + * @type {boolean} + * @memberof IIssueTsoCmdOpts + */ + isStateful?: boolean; + + /** + * suppressing broadcast message from TSO address space + * @type {boolean} + * @memberof IIssueTsoCmdOpts + */ + suppressStartupMessages?: boolean; +} diff --git a/packages/zostso/src/index.ts b/packages/zostso/src/index.ts index cd43f1a60a..576da8ddd6 100644 --- a/packages/zostso/src/index.ts +++ b/packages/zostso/src/index.ts @@ -28,6 +28,7 @@ export * from "./doc/zosmf/IZosmfPingResponse"; export * from "./doc/zosmf/IZosmfTsoResponse"; export * from "./doc/ICollectedResponses"; +export * from "./doc/IIssueTsoCmdResponse"; export * from "./doc/IIssueResponse"; export * from "./doc/IPingResponse"; export * from "./doc/ISendResponse"; diff --git a/packages/zosuss/package.json b/packages/zosuss/package.json index 522a06b499..d4143d92c1 100644 --- a/packages/zosuss/package.json +++ b/packages/zosuss/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/zos-uss-for-zowe-sdk", - "version": "8.0.0", + "version": "8.0.1", "description": "Zowe SDK to interact with USS on z/OS", "author": "Zowe", "license": "EPL-2.0", @@ -49,8 +49,8 @@ }, "devDependencies": { "@types/ssh2": "^1.11.19", - "@zowe/cli-test-utils": "8.0.0", - "@zowe/imperative": "8.0.0" + "@zowe/cli-test-utils": "8.0.1", + "@zowe/imperative": "8.0.1" }, "peerDependencies": { "@zowe/imperative": "^8.0.0-next"