diff --git a/.github/updateReleaseMarkdown.js b/.github/updateReleaseMarkdown.js new file mode 100644 index 0000000000..56406f6c0c --- /dev/null +++ b/.github/updateReleaseMarkdown.js @@ -0,0 +1,65 @@ +const fs = require('fs'); +const path = require('path'); + +const mdFilePath = path.join(__dirname, '../RELEASE_HISTORY.md'); + +// Build the new row to be added +const newRow = `| v${process.env.NEW_VERSION} | ${new Date().toISOString().split('T')[0].slice(0, 7)} | **Active** | [Release Notes](https://docs.zowe.org/stable/whats-new/release-notes/v${process.env.NEW_VERSION.replace(/\./g, '_')}) |`; + +// Read, Update and Write to Markdown File +function updateReleaseHistory(newRow) { + fs.readFile(mdFilePath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading the file:', err); + return; + } + + // Find the table and insert the new row after the second row + const lines = data.split('\n'); + let tableLineCount = 0; + for (let i = 0; i < lines.length; i++) { + if (lines[i].startsWith('|') && lines[i].endsWith('|')) { + tableLineCount++; + if (tableLineCount === 2) { + // Insert the new row after the second row + lines.splice(i + 1, 0, newRow); + break; + } + } + } + + fs.writeFile(mdFilePath, lines.join('\n'), 'utf8', (err) => { + if (err) { + console.error('Error writing the file:', err); + return; + } + console.log('Markdown file updated successfully.'); + }); + }); +} + +// Update the zoweVersion in package.json +function updatePackageJsonVersion(newVersion) { + const packageJsonPath = path.join(__dirname, '../packages/cli/package.json'); + fs.readFile(packageJsonPath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading package.json:', err); + return; + } + + let packageJson = JSON.parse(data); + packageJson.zoweVersion = `v${newVersion}`; + + fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf8', (err) => { + if (err) { + console.error('Error writing to package.json:', err); + return; + } + console.log('package.json updated successfully.'); + }); + }); +} + +// Execute the functions +updatePackageJsonVersion(process.env.NEW_VERSION); +updateReleaseHistory(newRow); \ No newline at end of file diff --git a/.github/updateReleaseMarkdown_BROKEN.js b/.github/updateReleaseMarkdown_BROKEN.js new file mode 100644 index 0000000000..a8d3d1b674 --- /dev/null +++ b/.github/updateReleaseMarkdown_BROKEN.js @@ -0,0 +1,111 @@ +const fs = require('fs'); +const path = require('path'); +const https = require('https'); + +// This script is attempting to add whatever list of the CLI team is shown in the TSC repo https://raw.githubusercontent.com/zowe/community/master/COMMITTERS.md. +// Despite efforts, this is not working. Adam will always be cut off (possibly just whoever is first in the table). +// Leaving this code present and hoping that the problem with this file can be solved in the future. + +// URL of the raw markdown file to be appended to RELEASE_HISTORY.md +const url = 'https://raw.githubusercontent.com/zowe/community/master/COMMITTERS.md'; + +// Build the new row to be added +const newVersion = process.env.NEW_VERSION; +const newRow = `| v${newVersion} | ${new Date().toISOString().split('T')[0].slice(0, 7)} | **Active** | [Release Notes](https://docs.zowe.org/stable/whats-new/release-notes/v${newVersion.replace(/\./g, '_')}) |`; + +const mdFilePath = path.join(__dirname, '../RELEASE_HISTORY.md'); + +// Function to fetch CLI team from a URL +function fetchCliTeam(url) { + return new Promise((resolve, reject) => { + https.get(url, (res) => { + let data = ''; + + // A chunk of data has been received + res.on('data', (chunk) => { + data += chunk; + }); + + // The whole response has been received + res.on('end', () => { + // Extract only the CLI contributors section + const cliSectionMatch = data.match(/### Zowe CLI Squad[\s\S]*?(?=###|$)/); + const cliSection = cliSectionMatch ? cliSectionMatch[0] : ''; + resolve(cliSection); + }); + }).on('error', (err) => { + reject(err); + }); + }); +} + +// Function to remove existing CLI team section and append new one +function updateCliTeamInMd(cliTeam) { + // Read the current content of the markdown file + fs.readFile(mdFilePath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading the file:', err); + return; + } + + // Remove the old CLI squad section and replace it with the new one + const updatedData = data.replace(/### Zowe CLI Squad[\s\S]*?(?=###|$)/, cliTeam + '\n'); + + // Write the updated data back to the file + fs.writeFile(mdFilePath, updatedData, 'utf8', (err) => { + if (err) { + console.error('Error writing the file:', err); + return; + } + console.log('CLI team has been updated in RELEASE_HISTORY.md successfully.'); + }); + }); +} + +// Main function to fetch CLI team and update RELEASE_HISTORY +async function appendCliTeam() { + try { + const cliTeam = await fetchCliTeam(url); + updateCliTeamInMd(cliTeam); + } catch (error) { + console.error('Error fetching CLI team:', error); + } +} + +// Read, Update and Write to Markdown File +function updateReleaseHistory(newRow) { + fs.readFile(mdFilePath, 'utf8', (err, data) => { + if (err) { + console.error('Error reading the file:', err); + return; + } + + // Find the table and insert the new row after the second row + const lines = data.split('\n'); + let tableLineCount = 0; + for (let i = 0; i < lines.length; i++) { + if (lines[i].startsWith('|') && lines[i].endsWith('|')) { + tableLineCount++; + if (tableLineCount === 2) { + // Insert the new row after the second row + lines.splice(i + 1, 0, newRow); + break; + } + } + } + + fs.writeFile(mdFilePath, lines.join('\n'), 'utf8', (err) => { + if (err) { + console.error('Error writing the file:', err); + return; + } + console.log('Markdown file updated successfully.'); + }); + }); +} + +// Execute the two main functions +(async () => { + await appendCliTeam(); + updateReleaseHistory(newRow); +})(); \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 94718d6832..5fca5be3c0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,10 +31,10 @@ jobs: uses: actions/checkout@v4 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} config-file: ./.github/resources/codeql-config.yml - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/zoweReleaseVersion.yaml b/.github/workflows/zoweReleaseVersion.yaml new file mode 100644 index 0000000000..0ec1e0e2b8 --- /dev/null +++ b/.github/workflows/zoweReleaseVersion.yaml @@ -0,0 +1,74 @@ +name: Update Zowe Version and Create PR + +on: + workflow_dispatch: + inputs: + major_version: + description: 'Major version (default is 3)' + required: true + default: '3' + minor_version: + description: 'Minor version' + required: true + patch_version: + description: 'Patch version' + required: true + +jobs: + update_versions_and_create_pr: + runs-on: ubuntu-latest + + steps: + - name: Checkout CLI Repo + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Set Variables and Ensure Increase in Semver + id: get_zowe_versions + run: | + current_version=$(jq -r '.zoweVersion' packages/cli/package.json) + current_version="${current_version#v}" # Strip the 'v' prefix + echo "current_version=$current_version" >> $GITHUB_ENV + new_version="${{ github.event.inputs.major_version }}.${{ github.event.inputs.minor_version }}.${{ github.event.inputs.patch_version }}" + echo "new_version=$new_version" >> $GITHUB_ENV + echo "PACKAGE_JSON_PATH=packages/cli/package.json" >> $GITHUB_ENV + if [[ "$new_version" < "$current_version" ]]; then + echo "Error: New version $new_version is not greater than current version $current_version." + exit 1 + fi + + - name: Create Branch + run: | + branch_name="update-version-v${{ env.new_version }}" + echo "branch_name=$branch_name" >> $GITHUB_ENV + git checkout -b "$branch_name" + + - name: Update Zowe Version in cli/package.json + run: | + jq ".zoweVersion = \"v${{ env.new_version }}\"" ${{ env.PACKAGE_JSON_PATH }} > package.tmp.json && mv package.tmp.json ${{ env.PACKAGE_JSON_PATH }} + + - name: Update RELEASE_HISTORY.md + run: node ".github/updateReleaseMarkdown.js" + env: + NEW_VERSION: ${{ env.new_version }} + + - name: Commit and Push Changes + run: | + git config --global user.name "${{ secrets.ZOWE_ROBOT_USER }}" + git config --global user.email "${{ secrets.ZOWE_ROBOT_EMAIL }}" + git add "${{ env.PACKAGE_JSON_PATH }}" + git add "RELEASE_HISTORY.md" + git commit -sm "Update version to ${{ env.new_version }}" + git push origin "$branch_name" + + - name: Create Pull Request + run: | + pr_title="Update CLI version to ${{ env.new_version }}" + base_branch="${{ github.ref_name }}" + gh pr create -t "$pr_title" -b "$base_branch" --head "$branch_name" --body "Updating Zowe CLI version to ${{ env.new_version }}" --reviewer zFernand0,t1m0thyj,awharn,gejohnston,traeok,jace-roell,ATorrise + env: + GH_TOKEN: ${{ secrets.ZOWE_ROBOT_TOKEN }} diff --git a/RELEASE_HISTORY.md b/RELEASE_HISTORY.md new file mode 100644 index 0000000000..0b2797c4e5 --- /dev/null +++ b/RELEASE_HISTORY.md @@ -0,0 +1,69 @@ +# Zowe CLI Releases + +Zowe follows a regular release schedule with major versions released every two years and minor versions approximately every six weeks. Full details regarding the Zowe release schedule are available on [zowe.org](https://www.zowe.org/download#timeline) and the Zowe Community [Github](https://github.com/zowe/community/blob/master/Technical-Steering-Committee/release.md). + +## Zowe Release Schedule + +### Zowe v3.x LTS Releases +| Version | Release Date | Status | Release Notes | +|:--------:|:------------:|:----------:|:-------------:| +| v3.0.0-prerelease | 2024-08 | **Under Development** | | + +### Major Release Timeline + +![Zowe Major Releases](https://raw.githubusercontent.com/zowe/zowe.github.io/master/assets/img/major_releases.webp) + +### Version Timeframes + +- **Active**: Each major version remains in this phase for 2 years, receiving regular updates and new features. +- **Maintenance**: Following the Active phase, each major version remains in this phase for an additional 2.5 years, receiving only critical fixes and security patches. +- **Under Development**: The pre-Active phase where the next major version is prepared. This phase varies in length and is not available for general consumption. + +### Guarantees + +- **Critical Defects Fixes**: The community will fix critical defects. The criteria for what constitutes a critical defect can be found [here](https://github.com/zowe/community/blob/master/Technical-Steering-Committee/release.md#active-release). +- **Extender Conformance**: Extenders achieving Zowe conformance for the long-term support version will not need to modify their product to remain functional when Zowe updates are provided within the same major version. + +### Recommendations + +- **Production**: Use **Active** or **Maintenance** releases for production due to the guaranteed stability and the community’s commitment to fixing critical defects. +- **Nightly Builds**: Available for integration testing. Use at your own risk. + +## Zowe Release Process + +### Short Summary + +1. Code Freeze date is hit. +2. Each [squad](https://github.com/zowe/community/blob/master/Technical-Steering-Committee/squads.md) provides a version of the code to integrate into the Release Candidate (RC). +3. RC is built and tested with an automatic test suite. +4. RC is deployed and tested by squads. +5. RC is approved by the TSC vote. +6. Release is published. +7. Documentation is published. +8. Release retrospective is held. + +### Release Numbering + +Releases follow [semantic versioning](https://semver.org/) guidelines (MAJOR.MINOR.PATCH). + +- **MAJOR**: Incompatible API changes. +- **MINOR**: Backwards-compatible functionality additions. +- **PATCH**: Backwards-compatible bug fixes. + +### Release Content + +The following [components of the release](https://github.com/zowe/community/blob/master/Technical-Steering-Committee/release.md#release-content) are managed by the CLI squad: + +- **CLI Component** + - CLI Core + - CLI Plugins +- **Client SDKs** + - Node.js Client SDK + - Python Client SDK + + +## Zowe CLI Squad +- https://github.com/zowe/community/blob/master/COMMITTERS.md#zowe-cli-squad +- https://github.com/orgs/zowe/teams/zowe-cli-administrators +- https://github.com/orgs/zowe/teams/zowe-cli-committers +- https://github.com/orgs/zowe/teams/zowe-cli-contributors \ No newline at end of file diff --git a/__tests__/__integration__/__snapshots__/imperative.secure.integration.test.ts.snap b/__tests__/__integration__/__snapshots__/imperative.secure.integration.test.ts.snap index b2f87f5e74..bf7ee4dda4 100644 --- a/__tests__/__integration__/__snapshots__/imperative.secure.integration.test.ts.snap +++ b/__tests__/__integration__/__snapshots__/imperative.secure.integration.test.ts.snap @@ -2,6 +2,7 @@ exports[`Imperative Secure Tests imperative-test-cli config profiles should list profiles 1`] = ` "secured -base +project_base +global_base " `; diff --git a/__tests__/__packages__/cli-test-utils/CHANGELOG.md b/__tests__/__packages__/cli-test-utils/CHANGELOG.md index b30ede1459..47c3b98053 100644 --- a/__tests__/__packages__/cli-test-utils/CHANGELOG.md +++ b/__tests__/__packages__/cli-test-utils/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the Zowe CLI test utils package will be documented in this file. +## `8.0.0-next.202407262216` + +- Update: See `7.28.2` for details + ## `8.0.0-next.202407021516` - BugFix: Updated dependencies for technical currency [#2188](https://github.com/zowe/zowe-cli/pull/2188) @@ -14,6 +18,10 @@ All notable changes to the Zowe CLI test utils package will be documented in thi - Major: First major version bump for V3 +## `7.28.2` + +- BugFix: Improved the error message shown on Windows when `runCliScript` method cannot find `sh` executable on PATH. [#2208](https://github.com/zowe/zowe-cli/issues/2208) + ## `7.18.11` - BugFix: Fix types error from an incorrect jstree type during compilation diff --git a/__tests__/__packages__/cli-test-utils/package.json b/__tests__/__packages__/cli-test-utils/package.json index ecea743ca4..541ebfc4ec 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255" + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/imperative": "^8.0.0-next" diff --git a/__tests__/__packages__/cli-test-utils/src/TestUtils.ts b/__tests__/__packages__/cli-test-utils/src/TestUtils.ts index bd5d70d36d..bda522b7b1 100644 --- a/__tests__/__packages__/cli-test-utils/src/TestUtils.ts +++ b/__tests__/__packages__/cli-test-utils/src/TestUtils.ts @@ -10,7 +10,7 @@ */ import * as fs from "fs"; -import { spawnSync, SpawnSyncReturns } from "child_process"; +import { spawnSync, SpawnSyncReturns, ExecFileException } from "child_process"; import { ITestEnvironment } from "./environment/doc/response/ITestEnvironment"; import { CommandProfiles, ICommandDefinition, IHandlerParameters } from "@zowe/imperative"; @@ -37,11 +37,16 @@ export function runCliScript(scriptPath: string, testEnvironment: ITestEnvironme if (process.platform === "win32") { // Execute the command synchronously - return spawnSync("sh", [`${scriptPath}`].concat(args), { + const response = spawnSync("sh", [scriptPath].concat(args), { cwd: testEnvironment.workingDir, - env: childEnv, - encoding: "buffer" + encoding: "buffer", + env: childEnv }); + if ((response.error as ExecFileException)?.code === "ENOENT") { + throw new Error(`"sh" is missing from your PATH. Check that Git Bash is installed with the option to ` + + `"Use Git and Unix Tools from Windows Command Prompt".`); + } + return response; } // Check to see if the file is executable diff --git a/lerna.json b/lerna.json index 0baaffd899..d6b2fa252c 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "command": { "publish": { "ignoreChanges": [ diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index cc6384aa94..e139b22c6a 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255" + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/imperative": "^8.0.0-next" @@ -6032,6 +6032,7 @@ }, "node_modules/brace-expansion": { "version": "1.1.11", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -6826,6 +6827,7 @@ }, "node_modules/concat-map": { "version": "0.0.1", + "dev": true, "license": "MIT" }, "node_modules/concordance": { @@ -8687,6 +8689,7 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", + "dev": true, "license": "ISC" }, "node_modules/function-arguments": { @@ -8805,6 +8808,7 @@ }, "node_modules/glob": { "version": "7.2.3", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -9209,6 +9213,7 @@ }, "node_modules/inflight": { "version": "1.0.6", + "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -9217,6 +9222,7 @@ }, "node_modules/inherits": { "version": "2.0.4", + "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -11409,6 +11415,7 @@ }, "node_modules/minimatch": { "version": "3.1.2", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -12871,6 +12878,7 @@ }, "node_modules/once": { "version": "1.4.0", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -13502,6 +13510,7 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -14247,7 +14256,9 @@ "license": "ISC" }, "node_modules/requirejs": { - "version": "2.3.6", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.3.7.tgz", + "integrity": "sha512-DouTG8T1WanGok6Qjg2SXuCMzszOo0eHeH9hDZ5Y4x8Je+9JB38HdTLT4/VA8OaUhBa0JPVHJ0pyBkM1z+pDsw==", "dev": true, "license": "MIT", "bin": { @@ -14840,6 +14851,7 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/ssh2": { @@ -16136,6 +16148,7 @@ }, "node_modules/wrappy": { "version": "1.0.2", + "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -16249,25 +16262,6 @@ "node": ">= 14" } }, - "node_modules/yamljs": { - "version": "0.3.0", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "glob": "^7.0.5" - }, - "bin": { - "json2yaml": "bin/json2yaml", - "yaml2json": "bin/yaml2json" - } - }, - "node_modules/yamljs/node_modules/argparse": { - "version": "1.0.10", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, "node_modules/yargs": { "version": "17.7.2", "license": "MIT", @@ -16341,21 +16335,21 @@ }, "packages/cli": { "name": "@zowe/cli", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "hasInstallScript": true, "license": "EPL-2.0", "dependencies": { - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255", - "@zowe/provisioning-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-console-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-jobs-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-logs-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-tso-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-uss-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-workflows-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zosmf-for-zowe-sdk": "8.0.0-next.202407181255", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544", + "@zowe/provisioning-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-console-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-jobs-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-logs-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-tso-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-uss-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-workflows-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zosmf-for-zowe-sdk": "8.0.0-next.202407311544", "find-process": "1.4.7", "lodash": "4.17.21", "minimatch": "9.0.5", @@ -16368,7 +16362,7 @@ "@types/diff": "^5.0.9", "@types/lodash": "^4.17.6", "@types/tar": "^6.1.11", - "@zowe/cli-test-utils": "8.0.0-next.202407181255", + "@zowe/cli-test-utils": "8.0.0-next.202407311544", "comment-json": "^4.2.3", "strip-ansi": "^6.0.1", "which": "^4.0.0" @@ -16377,7 +16371,7 @@ "node": ">=18.12.0" }, "optionalDependencies": { - "@zowe/secrets-for-zowe-sdk": "8.0.0-next.202407181255" + "@zowe/secrets-for-zowe-sdk": "8.0.0-next.202407311544" } }, "packages/cli/node_modules/brace-expansion": { @@ -16424,15 +16418,15 @@ }, "packages/core": { "name": "@zowe/core-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "dependencies": { "comment-json": "~4.2.3", "string-width": "^4.2.3" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16443,7 +16437,7 @@ }, "packages/imperative": { "name": "@zowe/imperative", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "dependencies": { "@types/yargs": "^17.0.32", @@ -16481,7 +16475,6 @@ "strip-ansi": "^6.0.1", "which": "^4.0.0", "wrap-ansi": "^7.0.0", - "yamljs": "^0.3.0", "yargs": "^17.7.2" }, "devDependencies": { @@ -16497,7 +16490,7 @@ "@types/pacote": "^11.1.8", "@types/progress": "^2.0.7", "@types/stack-trace": "^0.0.33", - "@zowe/secrets-for-zowe-sdk": "8.0.0-next.202407181255", + "@zowe/secrets-for-zowe-sdk": "8.0.0-next.202407311544", "concurrently": "^8.0.0", "cowsay": "^1.6.0", "deep-diff": "^1.0.0", @@ -16646,16 +16639,16 @@ }, "packages/provisioning": { "name": "@zowe/provisioning-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "dependencies": { "js-yaml": "^4.1.0" }, "devDependencies": { "@types/js-yaml": "^4.0.9", - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16667,7 +16660,7 @@ }, "packages/secrets": { "name": "@zowe/secrets-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "hasInstallScript": true, "license": "EPL-2.0", "devDependencies": { @@ -16680,15 +16673,15 @@ }, "packages/workflows": { "name": "@zowe/zos-workflows-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407181255" + "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407311544" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16700,12 +16693,12 @@ }, "packages/zosconsole": { "name": "@zowe/zos-console-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16717,16 +16710,16 @@ }, "packages/zosfiles": { "name": "@zowe/zos-files-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "dependencies": { "minimatch": "^9.0.5" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255", - "@zowe/zos-uss-for-zowe-sdk": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544", + "@zowe/zos-uss-for-zowe-sdk": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16758,15 +16751,15 @@ }, "packages/zosjobs": { "name": "@zowe/zos-jobs-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "dependencies": { - "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407181255" + "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407311544" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16778,12 +16771,12 @@ }, "packages/zoslogs": { "name": "@zowe/zos-logs-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16795,12 +16788,12 @@ }, "packages/zosmf": { "name": "@zowe/zosmf-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16812,15 +16805,15 @@ }, "packages/zostso": { "name": "@zowe/zos-tso-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "dependencies": { - "@zowe/zosmf-for-zowe-sdk": "8.0.0-next.202407181255" + "@zowe/zosmf-for-zowe-sdk": "8.0.0-next.202407311544" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" @@ -16832,15 +16825,15 @@ }, "packages/zosuss": { "name": "@zowe/zos-uss-for-zowe-sdk", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "license": "EPL-2.0", "dependencies": { "ssh2": "^1.15.0" }, "devDependencies": { "@types/ssh2": "^1.11.19", - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 19cf6048a6..1100e5c139 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the Zowe CLI package will be documented in this file. +## `8.0.0-next.202407181904` + +- Enhancement: The 'zowe config auto-init' command now generates a base profile name of 'global_base' or 'project_base', depending on whether a global or project configuration file is being generated. Related to Zowe Explorer issue https://github.com/zowe/zowe-explorer-vscode/issues/2682. + ## `8.0.0-next.202407021516` - BugFix: Updated dependencies for technical currency [#2188](https://github.com/zowe/zowe-cli/pull/2188) diff --git a/packages/cli/__tests__/config/auto-init/__system__/cli.config.auto-init.system.test.ts b/packages/cli/__tests__/config/auto-init/__system__/cli.config.auto-init.system.test.ts index e41c275794..863f93d0ef 100644 --- a/packages/cli/__tests__/config/auto-init/__system__/cli.config.auto-init.system.test.ts +++ b/packages/cli/__tests__/config/auto-init/__system__/cli.config.auto-init.system.test.ts @@ -93,7 +93,7 @@ describe("config auto-init without profile", () => { let config = fs.readFileSync(path.join(TEST_ENVIRONMENT.workingDir, "zowe.config.json")).toString(); // Typecasting because of this issue: https://github.com/kaelzhang/node-comment-json/issues/42 const configJson = JSONC.parse(config) as any; - configJson.profiles.base.properties = {}; + configJson.profiles.project_base.properties = {}; config = JSONC.stringify(configJson, null, 4); fs.writeFileSync(path.join(TEST_ENVIRONMENT.workingDir, "zowe.config.json"), config); @@ -239,8 +239,8 @@ describe("config auto-init without profile and with certificates", () => { let config = fs.readFileSync(path.join(TEST_ENVIRONMENT.workingDir, "zowe.config.json")).toString(); // Typecasting because of this issue: https://github.com/kaelzhang/node-comment-json/issues/42 const configJson = JSONC.parse(config) as any; - configJson.profiles.base.properties = {}; - configJson.profiles.base.secure = []; + configJson.profiles.project_base.properties = {}; + configJson.profiles.project_base.secure = []; config = JSONC.stringify(configJson, null, 4); fs.writeFileSync(path.join(TEST_ENVIRONMENT.workingDir, "zowe.config.json"), config); diff --git a/packages/cli/__tests__/config/auto-init/__unit__/ApimlAutoInitHandler.unit.test.ts b/packages/cli/__tests__/config/auto-init/__unit__/ApimlAutoInitHandler.unit.test.ts index eaf064effa..73c731ca5b 100644 --- a/packages/cli/__tests__/config/auto-init/__unit__/ApimlAutoInitHandler.unit.test.ts +++ b/packages/cli/__tests__/config/auto-init/__unit__/ApimlAutoInitHandler.unit.test.ts @@ -29,10 +29,10 @@ function mockConfigApi(properties: IConfig | undefined): any { }, profiles: { getProfilePathFromName: (name: string) => `profiles.${name}`, - get: jest.fn().mockReturnValue({}) + get: jest.fn().mockReturnValue(properties.profiles.base?.properties) }, secure: { - securePropsForProfile: jest.fn().mockReturnValue([]) + securePropsForProfile: jest.fn().mockReturnValue(properties.profiles.base?.secure) } }, exists: true, @@ -86,16 +86,17 @@ describe("ApimlAutoInitHandler", () => { }, { arguments: { $0: "fake", - _: ["fake"] + _: ["fake"], } }); expect(mockGetPluginApimlConfigs).toHaveBeenCalledTimes(1); expect(mockGetServicesByConfig).toHaveBeenCalledTimes(1); expect(mockConvertApimlProfileInfoToProfileConfig).toHaveBeenCalledTimes(1); expect(mockLogin).toHaveBeenCalledTimes(1); - expect(response.profiles.base.secure).toContain("tokenValue"); - expect(response.profiles.base.properties.tokenType).toEqual(SessConstants.TOKEN_TYPE_APIML); - expect(response.profiles.base.properties.tokenValue).toEqual("fakeToken"); + const baseProfName = "project_base"; + expect(response.profiles[baseProfName].secure).toContain("tokenValue"); + expect(response.profiles[baseProfName].properties.tokenType).toEqual(SessConstants.TOKEN_TYPE_APIML); + expect(response.profiles[baseProfName].properties.tokenValue).toEqual("fakeToken"); }); it("should not have changed - tokenType and tokenValue", async () => { @@ -141,9 +142,11 @@ describe("ApimlAutoInitHandler", () => { expect(mockGetServicesByConfig).toHaveBeenCalledTimes(1); expect(mockConvertApimlProfileInfoToProfileConfig).toHaveBeenCalledTimes(1); expect(mockLogin).toHaveBeenCalledTimes(0); - expect(response.profiles.base.secure).toContain("tokenValue"); - expect(response.profiles.base.properties.tokenType).toEqual(SessConstants.TOKEN_TYPE_APIML); - expect(response.profiles.base.properties.tokenValue).toEqual("fakeToken"); + + const baseProfName = "project_base"; + expect(response.profiles[baseProfName].secure).toContain("tokenValue"); + expect(response.profiles[baseProfName].properties.tokenType).toEqual(SessConstants.TOKEN_TYPE_APIML); + expect(response.profiles[baseProfName].properties.tokenValue).toEqual("fakeToken"); }); it("should not have changed - PEM Certificates", async () => { @@ -190,9 +193,82 @@ describe("ApimlAutoInitHandler", () => { expect(mockGetServicesByConfig).toHaveBeenCalledTimes(1); expect(mockConvertApimlProfileInfoToProfileConfig).toHaveBeenCalledTimes(1); expect(mockLogin).toHaveBeenCalledTimes(1); - expect(response.profiles.base.secure).toContain("tokenValue"); - expect(response.profiles.base.properties.tokenType).toEqual(SessConstants.TOKEN_TYPE_APIML); - expect(response.profiles.base.properties.tokenValue).toEqual("fakeToken"); + + const baseProfName = "project_base"; + expect(response.profiles[baseProfName].secure).toContain("tokenValue"); + expect(response.profiles[baseProfName].properties.tokenType).toEqual(SessConstants.TOKEN_TYPE_APIML); + expect(response.profiles[baseProfName].properties.tokenValue).toEqual("fakeToken"); + }); + + it("should not have changed - secure fields with existing non-default base profile", async () => { + // NOTE: Token type and token value will be stored, but user and password will still be present in the base profile + const mockCreateZosmfSession = jest.fn(); + const mockGetPluginApimlConfigs = jest.fn().mockReturnValue([]); + const mockGetServicesByConfig = jest.fn().mockResolvedValue([]); + jest.spyOn(ConfigUtils, "getActiveProfileName").mockReturnValueOnce("base"); + const mockConfigValue: any = { + defaults: {}, + profiles: { + "base": { + properties: { + host: "fake", + port: 12345, + user: "fake", + password: "fake" + }, + secure: [ + "host", + "user", + "password" + ], + profiles: {} + } + }, + plugins: [] + }; + const mockConvertApimlProfileInfoToProfileConfig = jest.fn().mockReturnValue(mockConfigValue); + const mockLogin = jest.fn().mockResolvedValue("fakeToken"); + jest.spyOn(ImperativeConfig.instance, "config", "get").mockReturnValue(mockConfigApi(mockConfigValue)); + + ZosmfSession.createSessCfgFromArgs = mockCreateZosmfSession; + Services.getPluginApimlConfigs = mockGetPluginApimlConfigs; + Services.getServicesByConfig = mockGetServicesByConfig; + Services.convertApimlProfileInfoToProfileConfig = mockConvertApimlProfileInfoToProfileConfig; + Login.apimlLogin = mockLogin; + + const handler: any = new ApimlAutoInitHandler(); + expect(handler.mProfileType).toBe("base"); + + handler.createSessCfgFromArgs(); + expect(mockCreateZosmfSession).toHaveBeenCalledTimes(1); + + const response = await handler.doAutoInit( + { + ISession: { + hostname: "fake", + port: 1234, + user: "fake", + password: "fake", + type: SessConstants.AUTH_TYPE_BASIC, + tokenType: undefined + } + }, { + arguments: { + $0: "fake", + _: ["fake"], + "base-profile": "base" + } + }); + expect(mockGetPluginApimlConfigs).toHaveBeenCalledTimes(1); + expect(mockGetServicesByConfig).toHaveBeenCalledTimes(1); + expect(mockConvertApimlProfileInfoToProfileConfig).toHaveBeenCalledTimes(1); + expect(mockLogin).toHaveBeenCalledTimes(1); + expect(response.profiles.base.secure).toEqual(["host", "tokenValue"]); + expect(response.profiles.base.properties.tokenType).toBeDefined(); + expect(response.profiles.base.properties.tokenValue).toBeDefined(); + expect(response.profiles.base.properties.user).toBeUndefined(); + expect(response.profiles.base.properties.password).toBeUndefined(); + expect(response.defaults.base).toBe("base"); }); it("should not have changed - user & password with existing base profile", async () => { @@ -202,14 +278,19 @@ describe("ApimlAutoInitHandler", () => { const mockGetServicesByConfig = jest.fn().mockResolvedValue([]); jest.spyOn(ConfigUtils, "getActiveProfileName").mockReturnValueOnce("base"); const mockConfigValue: any = { - defaults: { base: "base"}, + defaults: { base: "base" }, profiles: { "base": { properties: { host: "fake", - port: 12345 + port: 12345, + user: "fake", + password: "fake" }, - secure: [], + secure: [ + "user", + "password" + ], profiles: {} } }, @@ -254,6 +335,9 @@ describe("ApimlAutoInitHandler", () => { expect(response.profiles.base.secure).toContain("tokenValue"); expect(response.profiles.base.properties.tokenType).toBeDefined(); expect(response.profiles.base.properties.tokenValue).toBeDefined(); + expect(response.profiles.base.properties.user).toBeUndefined(); + expect(response.profiles.base.properties.password).toBeUndefined(); + expect(response.defaults.base).toBe("base"); }); it("should not have changed - rejectUnauthorized flag true", async () => { @@ -301,7 +385,7 @@ describe("ApimlAutoInitHandler", () => { expect(mockGetServicesByConfig).toHaveBeenCalledTimes(1); expect(mockConvertApimlProfileInfoToProfileConfig).toHaveBeenCalledTimes(1); expect(mockLogin).toHaveBeenCalledTimes(1); - expect(response.profiles.base.properties.rejectUnauthorized).toEqual(true); + expect(response.profiles["project_base"].properties.rejectUnauthorized).toEqual(true); }); it("should not have changed - rejectUnauthorized flag false", async () => { @@ -349,7 +433,7 @@ describe("ApimlAutoInitHandler", () => { expect(mockGetServicesByConfig).toHaveBeenCalledTimes(1); expect(mockConvertApimlProfileInfoToProfileConfig).toHaveBeenCalledTimes(1); expect(mockLogin).toHaveBeenCalledTimes(1); - expect(response.profiles.base.properties.rejectUnauthorized).toEqual(false); + expect(response.profiles["project_base"].properties.rejectUnauthorized).toEqual(false); }); it("should not have changed - a condition that shouldn't ever happen", async () => { @@ -393,9 +477,11 @@ describe("ApimlAutoInitHandler", () => { expect(mockGetServicesByConfig).toHaveBeenCalledTimes(1); expect(mockConvertApimlProfileInfoToProfileConfig).toHaveBeenCalledTimes(1); expect(mockLogin).toHaveBeenCalledTimes(0); - expect(response.profiles.base.secure).not.toContain("tokenValue"); - expect(response.profiles.base.properties.tokenType).not.toBeDefined(); - expect(response.profiles.base.properties.tokenValue).not.toBeDefined(); + + const baseProfName = "project_base"; + expect(response.profiles[baseProfName].secure).not.toContain("tokenValue"); + expect(response.profiles[baseProfName].properties.tokenType).not.toBeDefined(); + expect(response.profiles[baseProfName].properties.tokenValue).not.toBeDefined(); }); it("should throw an error if an error 403 is experienced", async () => { diff --git a/packages/cli/package.json b/packages/cli/package.json index 8ecd76886d..5b2323d8c2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@zowe/cli", - "version": "8.0.0-next.202407181255", - "zoweVersion": "V3", + "version": "8.0.0-next.202407311544", + "zoweVersion": "v3.0.0-prerelease", "description": "Zowe CLI is a command line interface (CLI) that provides a simple and streamlined way to interact with IBM z/OS.", "author": "Zowe", "license": "EPL-2.0", @@ -58,17 +58,17 @@ "preshrinkwrap": "node ../../scripts/rewriteShrinkwrap.js" }, "dependencies": { - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255", - "@zowe/provisioning-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-console-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-jobs-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-logs-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-tso-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-uss-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zos-workflows-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/zosmf-for-zowe-sdk": "8.0.0-next.202407181255", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544", + "@zowe/provisioning-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-console-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-jobs-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-logs-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-tso-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-uss-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zos-workflows-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/zosmf-for-zowe-sdk": "8.0.0-next.202407311544", "find-process": "1.4.7", "lodash": "4.17.21", "minimatch": "9.0.5", @@ -78,13 +78,13 @@ "@types/diff": "^5.0.9", "@types/lodash": "^4.17.6", "@types/tar": "^6.1.11", - "@zowe/cli-test-utils": "8.0.0-next.202407181255", + "@zowe/cli-test-utils": "8.0.0-next.202407311544", "comment-json": "^4.2.3", "strip-ansi": "^6.0.1", "which": "^4.0.0" }, "optionalDependencies": { - "@zowe/secrets-for-zowe-sdk": "8.0.0-next.202407181255" + "@zowe/secrets-for-zowe-sdk": "8.0.0-next.202407311544" }, "engines": { "node": ">=18.12.0" diff --git a/packages/cli/src/config/auto-init/ApimlAutoInitHandler.ts b/packages/cli/src/config/auto-init/ApimlAutoInitHandler.ts index 9e1896a817..0078731ad7 100644 --- a/packages/cli/src/config/auto-init/ApimlAutoInitHandler.ts +++ b/packages/cli/src/config/auto-init/ApimlAutoInitHandler.ts @@ -15,7 +15,7 @@ import * as lodash from "lodash"; import { ZosmfSession } from "@zowe/zosmf-for-zowe-sdk"; import { BaseAutoInitHandler, AbstractSession, ICommandArguments, IConfig, IConfigProfile, ISession, IHandlerResponseApi, IHandlerParameters, SessConstants, ImperativeConfig, - ImperativeError, RestClientError, TextUtils, Config + ImperativeError, RestClientError, TextUtils, Config, ConfigUtils } from "@zowe/imperative"; import { IApimlProfileInfo, IAutoInitRpt, IProfileRpt, Login, Services } from "@zowe/core-for-zowe-sdk"; @@ -108,9 +108,15 @@ export default class ApimlAutoInitHandler extends BaseAutoInitHandler { // Check to see if there is an active base profile to avoid creating a new one named "base" let activeBaseProfile = params.arguments[`${this.mProfileType}-profile`] || config.properties.defaults[this.mProfileType]; let baseProfileCreated = false; + // Populate the config with base profile information if (activeBaseProfile == null) { - profileConfig.profiles[this.mProfileType] = { + + // Name our base profile differently in a global config vs a project config + const globalConfig: boolean = params.arguments?.globalConfig ? true : false; + activeBaseProfile = ConfigUtils.formGlobOrProjProfileNm(this.mProfileType, globalConfig); + + profileConfig.profiles[activeBaseProfile] = { type: this.mProfileType, properties: { host: session.ISession.hostname, @@ -119,7 +125,6 @@ export default class ApimlAutoInitHandler extends BaseAutoInitHandler { }, secure: [] }; - activeBaseProfile = this.mProfileType; baseProfileCreated = true; } else { const oldBaseProfile = this.getOldBaseProfileProps(config, activeBaseProfile); @@ -149,7 +154,7 @@ export default class ApimlAutoInitHandler extends BaseAutoInitHandler { // Report whether or not we created a base profile in this auto-init execution this.mAutoInitReport.profileRpts.push({ - profName: this.mProfileType, + profName: activeBaseProfile, profType: this.mProfileType, changeForProf: baseProfileCreated ? "created" : "modified", basePath: null, diff --git a/packages/core/package.json b/packages/core/package.json index 3dfd2f0972..c122589664 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/imperative": "^8.0.0-next" diff --git a/packages/imperative/CHANGELOG.md b/packages/imperative/CHANGELOG.md index efceabeebe..548639899f 100644 --- a/packages/imperative/CHANGELOG.md +++ b/packages/imperative/CHANGELOG.md @@ -7,6 +7,19 @@ All notable changes to the Imperative package will be documented in this file. - BugFix: Resolved bug that resulted in user not being prompted for a keyPassphrase if in the secure array of ssh profile. [#1770](https://github.com/zowe/zowe-cli/issues/1770) - Enhancement: SshBaseHandler will now prompt user up to 3 times to enter the correct keyPassphrase in the case that the stored value is incorrect or no value is stored. [#1770](https://github.com/zowe/zowe-cli/issues/1770) +## `8.0.0-next.202407262216` + +- Update: See `5.26.1` for details + +## `8.0.0-next.202407232256` + +- Enhancement: Allowed boolean value (`false`) to be provided to the Credential Manager related function. [zowe-explorer-vscode#2622](https://github.com/zowe/zowe-explorer-vscode/issues/2622) +- Update: See `5.26.0` for details + +## `8.0.0-next.202407181904` + +- Enhancement: Added the function ConfigUtils.formGlobOrProjProfileNm and modified the function ConfigBuilder.build so that the 'zowe config init' command now generates a base profile name of 'global_base' or 'project_base', depending on whether a global or project configuration file is being generated. Related to Zowe Explorer issue https://github.com/zowe/zowe-explorer-vscode/issues/2682. + ## `8.0.0-next.202407181255` - BugFix: Resolved bug that resulted in each plug-in to have identical public registries regardless of actual installation location/reference. [#2189](https://github.com/zowe/zowe-cli/pull/2189) @@ -29,7 +42,6 @@ All notable changes to the Imperative package will be documented in this file. ## `8.0.0-next.202407021516` - BugFix: Updated dependencies for technical currency [#2188](https://github.com/zowe/zowe-cli/pull/2188) - - Update: See `5.25.0` for details ## `8.0.0-next.202406201950` @@ -419,6 +431,14 @@ All notable changes to the Imperative package will be documented in this file. - Major: First major version bump for V3 +## `5.26.1` + +- BugFix: Fixed missing export for `Proxy` class in Imperative package. [#2205](https://github.com/zowe/zowe-cli/pull/2205) + +## `5.26.0` + +- Enhancement: Updated `ProfileInfo.updateProperty` function to support updating properties in typeless profiles. [#2196](https://github.com/zowe/zowe-cli/issues/2196) + ## `5.25.0` - Enhancement: Added `ProfileInfo.profileManagerWillLoad` function to verify the credential manager can load. [#2111](https://github.com/zowe/zowe-cli/issues/2111) diff --git a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/__resources__/expectedObjects.ts b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/__resources__/expectedObjects.ts index 294d32cffc..9f231d37c9 100644 --- a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/__resources__/expectedObjects.ts +++ b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/__resources__/expectedObjects.ts @@ -196,7 +196,7 @@ export const expectedSchemaObject = { } }; -export const expectedConfigObject: IConfig = { +export const expectedGlobalConfigObject: IConfig = { $schema: "./imperative-test-cli.schema.json", profiles: { secured: { @@ -206,7 +206,7 @@ export const expectedConfigObject: IConfig = { }, secure: [] }, - base: { + global_base: { type: "base", properties: {}, secure: ["secret"] @@ -214,12 +214,12 @@ export const expectedConfigObject: IConfig = { }, defaults: { secured: "secured", - base: "base" + base: "global_base" }, autoStore: true }; -export const expectedUserConfigObject: IConfig = { +export const expectedGlobalUserConfigObject: IConfig = { $schema: "./imperative-test-cli.schema.json", profiles: { secured: { @@ -227,10 +227,51 @@ export const expectedUserConfigObject: IConfig = { properties: {}, secure: [] }, - base: { + global_base: { type: "base", properties: {}, + secure: ["secret"] + }, + }, + defaults: {}, + autoStore: true +}; + +export const expectedProjectConfigObject: IConfig = { + $schema: "./imperative-test-cli.schema.json", + profiles: { + secured: { + type: "secured", + properties: { + info: "" + }, + secure: [] + }, + project_base: { + type: "base", + properties: {}, + secure: ["secret"] + }, + }, + defaults: { + secured: "secured", + base: "project_base" + }, + autoStore: true +}; + +export const expectedProjectUserConfigObject: IConfig = { + $schema: "./imperative-test-cli.schema.json", + profiles: { + secured: { + type: "secured", + properties: {}, secure: [] + }, + project_base: { + type: "base", + properties: {}, + secure: ["secret"] } }, defaults: {}, diff --git a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/init/cli.imperative-test-cli.config.init.integration.subtest.ts b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/init/cli.imperative-test-cli.config.init.integration.subtest.ts index 2927259cab..1ee04363eb 100644 --- a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/init/cli.imperative-test-cli.config.init.integration.subtest.ts +++ b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/init/cli.imperative-test-cli.config.init.integration.subtest.ts @@ -12,15 +12,26 @@ import { ITestEnvironment } from "../../../../../../../__src__/environment/doc/response/ITestEnvironment"; import { SetupTestEnvironment } from "../../../../../../../__src__/environment/SetupTestEnvironment"; import { runCliScript } from "../../../../../../../src/TestUtil"; -import { expectedSchemaObject, expectedConfigObject, expectedUserConfigObject } from "../__resources__/expectedObjects"; +import { + expectedSchemaObject, + expectedGlobalConfigObject, expectedGlobalUserConfigObject, + expectedProjectConfigObject, expectedProjectUserConfigObject +} from "../__resources__/expectedObjects"; import * as fs from "fs"; import * as path from "path"; - +import * as lodash from "lodash"; // Test Environment populated in the beforeAll(); let TEST_ENVIRONMENT: ITestEnvironment; describe("imperative-test-cli config init", () => { + // config-init creates user base profiles with an empty secure array + const expectedGlobalUserJson = lodash.cloneDeep(expectedGlobalUserConfigObject); + expectedGlobalUserJson.profiles.global_base.secure = []; + + const expectedProjectUserJson = lodash.cloneDeep(expectedProjectUserConfigObject); + expectedProjectUserJson.profiles.project_base.secure = []; + // Create the test environment beforeAll(async () => { TEST_ENVIRONMENT = await SetupTestEnvironment.createTestEnv({ @@ -51,132 +62,102 @@ describe("imperative-test-cli config init", () => { it("should initialize a project config", () => { const response = runCliScript(__dirname + "/__scripts__/init_config.sh", TEST_ENVIRONMENT.workingDir, ["--prompt false"]); - const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.json"); + const expectedProjectConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.json"); const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.schema.json"); expect(response.output.toString()).toContain(`Saved config template to`); - expect(response.output.toString()).toContain(expectedConfigLocation); - expect(fs.existsSync(expectedConfigLocation)).toEqual(true); - expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedConfigObject); + expect(response.output.toString()).toContain(expectedProjectConfigLocation); + expect(fs.existsSync(expectedProjectConfigLocation)).toEqual(true); + expect(fs.existsSync(expectedProjectConfigLocation)).toEqual(true); + expect(JSON.parse(fs.readFileSync(expectedProjectConfigLocation).toString())).toEqual(expectedProjectConfigObject); expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); }); - it("should initialize a user project config", () => { + it("should initialize a project user config", () => { const response = runCliScript(__dirname + "/__scripts__/init_config.sh", TEST_ENVIRONMENT.workingDir, ["--user-config --prompt false"]); - const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); + const expectedProjectUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.schema.json"); expect(response.output.toString()).toContain(`Saved config template to`); - expect(response.output.toString()).toContain(expectedConfigLocation); - expect(fs.existsSync(expectedConfigLocation)).toEqual(true); + expect(response.output.toString()).toContain(expectedProjectUserConfigLocation); + expect(fs.existsSync(expectedProjectUserConfigLocation)).toEqual(true); expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedUserConfigObject); + expect(JSON.parse(fs.readFileSync(expectedProjectUserConfigLocation).toString())).toEqual(expectedProjectUserJson); expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); }); it("should initialize a global config", () => { const response = runCliScript(__dirname + "/__scripts__/init_config.sh", TEST_ENVIRONMENT.workingDir, ["--global-config --prompt false"]); - const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); + const expectedGlobalConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.schema.json"); expect(response.output.toString()).toContain(`Saved config template to`); - expect(response.output.toString()).toContain(expectedConfigLocation); - expect(fs.existsSync(expectedConfigLocation)).toEqual(true); + expect(response.output.toString()).toContain(expectedGlobalConfigLocation); + expect(fs.existsSync(expectedGlobalConfigLocation)).toEqual(true); expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedConfigObject); + expect(JSON.parse(fs.readFileSync(expectedGlobalConfigLocation).toString())).toEqual(expectedGlobalConfigObject); expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); }); it("should initialize a user global config", () => { const response = runCliScript(__dirname + "/__scripts__/init_config.sh", TEST_ENVIRONMENT.workingDir, ["--global-config --user-config --prompt false"]); - const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.user.json"); + const expectedGlobalUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.user.json"); const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.schema.json"); expect(response.output.toString()).toContain(`Saved config template to`); - expect(response.output.toString()).toContain(expectedConfigLocation); - expect(fs.existsSync(expectedConfigLocation)).toEqual(true); - expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedUserConfigObject); + expect(response.output.toString()).toContain(expectedGlobalUserConfigLocation); + expect(fs.existsSync(expectedGlobalUserConfigLocation)).toEqual(true); + expect(fs.existsSync(expectedGlobalUserConfigLocation)).toEqual(true); + expect(JSON.parse(fs.readFileSync(expectedGlobalUserConfigLocation).toString())).toEqual(expectedGlobalUserJson); expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); }); it("should initialize a project config with prompting", () => { const response = runCliScript(__dirname + "/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, [""]); - const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.json"); + const expectedProjectConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.json"); const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.schema.json"); expect(response.output.toString()).not.toContain("Unable to securely save credentials"); expect(response.output.toString()).toContain(`Saved config template to`); - expect(response.output.toString()).toContain(expectedConfigLocation); - expect(fs.existsSync(expectedConfigLocation)).toEqual(true); + expect(response.output.toString()).toContain(expectedProjectConfigLocation); + expect(fs.existsSync(expectedProjectConfigLocation)).toEqual(true); expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedConfigObject); + expect(JSON.parse(fs.readFileSync(expectedProjectConfigLocation).toString())).toEqual(expectedProjectConfigObject); expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); }); - it("should initialize a user project config with prompting", () => { + it("should initialize a project user config with prompting", () => { const response = runCliScript(__dirname + "/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--user-config"]); - const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); + const expectedProjectUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.schema.json"); expect(response.output.toString()).not.toContain("Unable to securely save credentials"); expect(response.output.toString()).toContain(`Saved config template to`); - expect(response.output.toString()).toContain(expectedConfigLocation); - expect(fs.existsSync(expectedConfigLocation)).toEqual(true); + expect(response.output.toString()).toContain(expectedProjectUserConfigLocation); + expect(fs.existsSync(expectedProjectUserConfigLocation)).toEqual(true); expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedUserConfigObject); + expect(JSON.parse(fs.readFileSync(expectedProjectUserConfigLocation).toString())).toEqual(expectedProjectUserJson); expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); }); it("should initialize a global config with prompting", () => { const response = runCliScript(__dirname + "/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--global-config"]); - const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); + const expectedGlobalConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.schema.json"); expect(response.output.toString()).not.toContain("Unable to securely save credentials"); expect(response.output.toString()).toContain(`Saved config template to`); - expect(response.output.toString()).toContain(expectedConfigLocation); - expect(fs.existsSync(expectedConfigLocation)).toEqual(true); + expect(response.output.toString()).toContain(expectedGlobalConfigLocation); + expect(fs.existsSync(expectedGlobalConfigLocation)).toEqual(true); expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedConfigObject); + expect(JSON.parse(fs.readFileSync(expectedGlobalConfigLocation).toString())).toEqual(expectedGlobalConfigObject); expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); }); it("should initialize a user global config with prompting", () => { const response = runCliScript(__dirname + "/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--global-config --user-config"]); - const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.user.json"); + const expectedGlobalUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.user.json"); const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.schema.json"); expect(response.output.toString()).not.toContain("Unable to securely save credentials"); expect(response.output.toString()).toContain(`Saved config template to`); - expect(response.output.toString()).toContain(expectedConfigLocation); - expect(fs.existsSync(expectedConfigLocation)).toEqual(true); + expect(response.output.toString()).toContain(expectedGlobalUserConfigLocation); + expect(fs.existsSync(expectedGlobalUserConfigLocation)).toEqual(true); expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedUserConfigObject); + expect(JSON.parse(fs.readFileSync(expectedGlobalUserConfigLocation).toString())).toEqual(expectedGlobalUserJson); expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); }); - // eslint-disable-next-line jest/no-commented-out-tests - // it("should create a profile of a specified name", () => { - // const response = runCliScript(__dirname + "/__scripts__/init_config.sh", - // TEST_ENVIRONMENT.workingDir, ["--profile lpar.service --prompt false"]); - // const expectedConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); - // const expectedSchemaLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.schema.json"); - // const expectedConfigObject: IConfig = { - // $schema: "./imperative-test-cli.schema.json", - // profiles: { - // lpar: { - // properties: {}, - // profiles: { - // service: { - // properties: {} - // } - // } - // } - // }, - // defaults: {}, - // secure: [] - // }; - // expect(response.output.toString()).toContain(`Saved config template to`); - // expect(response.output.toString()).toContain(expectedConfigLocation); - // expect(fs.existsSync(expectedConfigLocation)).toEqual(true); - // expect(fs.existsSync(expectedSchemaLocation)).toEqual(true); - // expect(JSON.parse(fs.readFileSync(expectedConfigLocation).toString())).toEqual(expectedConfigObject); - // expect(JSON.parse(fs.readFileSync(expectedSchemaLocation).toString())).toEqual(expectedSchemaObject); - // runCliScript(__dirname + "/../__scripts__/delete_configs.sh", TEST_ENVIRONMENT.workingDir, - // ["imperative-test-cli.config.json imperative-test-cli.schema.json"]); - // }); }); diff --git a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/list/__snapshots__/cli.imperative-test-cli.config.list.integration.test.ts.snap b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/list/__snapshots__/cli.imperative-test-cli.config.list.integration.test.ts.snap index cbcc855125..58b90e28c6 100644 --- a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/list/__snapshots__/cli.imperative-test-cli.config.list.integration.test.ts.snap +++ b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/list/__snapshots__/cli.imperative-test-cli.config.list.integration.test.ts.snap @@ -8,14 +8,19 @@ exports[`imperative-test-cli config list should list the configuration 1`] = ` info: secure: (empty array) - base: + project_base: + type: base + properties: + secure: + - secret + global_base: type: base properties: secure: - secret defaults: secured: secured - base: base + base: project_base autoStore: true " `; @@ -28,22 +33,27 @@ exports[`imperative-test-cli config list should list the configuration without s info: secure: (empty array) - base: + project_base: type: base properties: secret: (secure value) secure: - secret + global_base: + type: base + properties: + secure: + - secret defaults: secured: secured - base: base + base: project_base autoStore: true " `; exports[`imperative-test-cli config list should list the defaults configuration property 1`] = ` "secured: secured -base: base +base: project_base " `; @@ -54,7 +64,12 @@ exports[`imperative-test-cli config list should list the profiles configuration info: secure: (empty array) -base: +project_base: + type: base + properties: + secure: + - secret +global_base: type: base properties: secure: diff --git a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/list/cli.imperative-test-cli.config.list.integration.test.ts b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/list/cli.imperative-test-cli.config.list.integration.test.ts index f55c962825..538d952fc4 100644 --- a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/list/cli.imperative-test-cli.config.list.integration.test.ts +++ b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/list/cli.imperative-test-cli.config.list.integration.test.ts @@ -12,18 +12,21 @@ import { ITestEnvironment } from "../../../../../../../__src__/environment/doc/response/ITestEnvironment"; import { SetupTestEnvironment } from "../../../../../../../__src__/environment/SetupTestEnvironment"; import { runCliScript } from "../../../../../../../src/TestUtil"; -import { expectedConfigObject } from "../__resources__/expectedObjects"; +import { + expectedGlobalConfigObject, expectedGlobalUserConfigObject, + expectedProjectConfigObject, expectedProjectUserConfigObject +} from "../__resources__/expectedObjects"; import * as path from "path"; -import * as lodash from "lodash"; // Test Environment populated in the beforeAll(); let TEST_ENVIRONMENT: ITestEnvironment; describe("imperative-test-cli config list", () => { - let expectedGlobalProjectConfigLocation: string; + let expectedGlobalConfigLocation: string; let expectedGlobalUserConfigLocation: string; let expectedProjectConfigLocation: string; - let expectedUserConfigLocation: string; + let expectedProjectUserConfigLocation: string; + // Create the test environment beforeAll(async () => { TEST_ENVIRONMENT = await SetupTestEnvironment.createTestEnv({ @@ -36,14 +39,15 @@ describe("imperative-test-cli config list", () => { runCliScript(__dirname + "/../init/__scripts__/init_config.sh", TEST_ENVIRONMENT.workingDir, ["--user-config --global-config --prompt false"]); expectedGlobalUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.user.json"); - expectedGlobalProjectConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); - expectedUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); + expectedGlobalConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); + expectedProjectUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); expectedProjectConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.json"); }); afterAll(() => { runCliScript(__dirname + "/../__scripts__/delete_configs.sh", TEST_ENVIRONMENT.workingDir, ["-rf imperative-test-cli.config.user.json imperative-test-cli.config.json test imperative-test-cli.schema.json"]); }); + it("should display the help", () => { const response = runCliScript(__dirname + "/../__scripts__/get_help.sh", TEST_ENVIRONMENT.workingDir, ["list"]); @@ -69,7 +73,12 @@ describe("imperative-test-cli config list", () => { const expectedResponse = { data: { profiles: { - base: { + project_base: { + type: "base", + properties: {}, + secure: ["secret"] + }, + global_base: { properties: {}, type: "base", secure: ["secret"] @@ -84,7 +93,7 @@ describe("imperative-test-cli config list", () => { }, defaults: { secured: "secured", - base: "base" + base: "project_base" }, autoStore: true } @@ -97,8 +106,8 @@ describe("imperative-test-cli config list", () => { it("should list the configurations based on location", () => { const response = runCliScript(__dirname + "/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--locations"]); expect(response.stdout.toString()).toContain(expectedProjectConfigLocation); - expect(response.stdout.toString()).toContain(expectedUserConfigLocation); - expect(response.stdout.toString()).toContain(expectedGlobalProjectConfigLocation); + expect(response.stdout.toString()).toContain(expectedProjectUserConfigLocation); + expect(response.stdout.toString()).toContain(expectedGlobalConfigLocation); expect(response.stdout.toString()).toContain(expectedGlobalUserConfigLocation); expect(response.stdout.toString()).toContain("defaults:"); expect(response.stdout.toString()).toContain("profiles:"); @@ -113,31 +122,22 @@ describe("imperative-test-cli config list", () => { it("should list the configurations based on location in RFJ", () => { const response = runCliScript(__dirname + "/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--locations --rfj"]); const parsedResponse = JSON.parse(response.stdout.toString()); - const expectedUserConfig = { - $schema: "./imperative-test-cli.schema.json", - profiles: { - secured: { - properties: {}, - type: "secured", - secure: [] as string[] - }, - base: { - properties: {}, - type: "base", - secure: [] as string[] - } - }, - defaults: {}, - autoStore: true - }; - const expectedProjectConfig = lodash.cloneDeep(expectedConfigObject); + const expectedResponse = { data: {} as any }; - expectedResponse.data[expectedUserConfigLocation] = expectedUserConfig; - expectedResponse.data[expectedGlobalUserConfigLocation] = expectedUserConfig; - expectedResponse.data[expectedGlobalProjectConfigLocation] = expectedProjectConfig; - expectedResponse.data[expectedProjectConfigLocation] = expectedProjectConfig; + + // config-init of a user config creates no entries in the properties object or in the secure array. + // So, empty the secure arrays in the user configs. + expectedResponse.data[expectedProjectUserConfigLocation] = expectedProjectUserConfigObject; + expectedResponse.data[expectedProjectUserConfigLocation].profiles.project_base.secure = []; + + expectedResponse.data[expectedGlobalUserConfigLocation] = expectedGlobalUserConfigObject; + expectedResponse.data[expectedGlobalUserConfigLocation].profiles.global_base.secure = []; + + expectedResponse.data[expectedProjectConfigLocation] = expectedProjectConfigObject; + expectedResponse.data[expectedGlobalConfigLocation] = expectedGlobalConfigObject; + expect(parsedResponse.success).toEqual(true); expect(parsedResponse.stderr).toEqual(""); expect(parsedResponse.exitCode).toEqual(0); @@ -162,8 +162,8 @@ describe("imperative-test-cli config list", () => { it("should get a list of config file paths 1", () => { const response = runCliScript(__dirname + "/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--locations --root"]); expect(response.stdout.toString()).toContain(expectedProjectConfigLocation); - expect(response.stdout.toString()).toContain(expectedUserConfigLocation); - expect(response.stdout.toString()).toContain(expectedGlobalProjectConfigLocation); + expect(response.stdout.toString()).toContain(expectedProjectUserConfigLocation); + expect(response.stdout.toString()).toContain(expectedGlobalConfigLocation); expect(response.stdout.toString()).toContain(expectedGlobalUserConfigLocation); expect(response.stderr.toString()).toEqual(""); expect(response.error).toBeFalsy(); @@ -171,8 +171,8 @@ describe("imperative-test-cli config list", () => { it("should get a list of config file paths 2", () => { const response = runCliScript(__dirname + "/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--locations --name-only"]); expect(response.stdout.toString()).toContain(expectedProjectConfigLocation); - expect(response.stdout.toString()).toContain(expectedUserConfigLocation); - expect(response.stdout.toString()).toContain(expectedGlobalProjectConfigLocation); + expect(response.stdout.toString()).toContain(expectedProjectUserConfigLocation); + expect(response.stdout.toString()).toContain(expectedGlobalConfigLocation); expect(response.stdout.toString()).toContain(expectedGlobalUserConfigLocation); expect(response.stderr.toString()).toEqual(""); expect(response.error).toBeFalsy(); @@ -193,12 +193,14 @@ describe("imperative-test-cli config list", () => { const response = runCliScript(__dirname + "/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["defaults"]); expect(response.stdout.toString()).toMatchSnapshot(); expect(response.stdout.toString()).toContain("secured: secured"); - expect(response.stdout.toString()).toContain("base: base"); + expect(response.stdout.toString()).toContain("base: project_base"); expect(response.stderr.toString()).toEqual(""); expect(response.error).toBeFalsy(); }); it("should list the configuration without showing secure values", () => { - runCliScript(__dirname + "/../set/__scripts__/set_secure.sh", TEST_ENVIRONMENT.workingDir, ["profiles.base.properties.secret", "area51"]); + runCliScript(__dirname + "/../set/__scripts__/set_secure.sh", TEST_ENVIRONMENT.workingDir, + ["profiles.project_base.properties.secret", "area51"] + ); const response = runCliScript(__dirname + "/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, [""]); expect(response.stdout.toString()).toMatchSnapshot(); expect(response.stdout.toString()).toContain("secured: secured"); diff --git a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/secure/cli.imperative-test-cli.config.secure.integration.subtest.ts b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/secure/cli.imperative-test-cli.config.secure.integration.subtest.ts index 8740412a69..9137c012fa 100644 --- a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/secure/cli.imperative-test-cli.config.secure.integration.subtest.ts +++ b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/secure/cli.imperative-test-cli.config.secure.integration.subtest.ts @@ -12,9 +12,12 @@ import { ITestEnvironment } from "../../../../../../../__src__/environment/doc/response/ITestEnvironment"; import { SetupTestEnvironment } from "../../../../../../../__src__/environment/SetupTestEnvironment"; import { runCliScript } from "../../../../../../../src/TestUtil"; -import { expectedConfigObject, expectedUserConfigObject } from "../__resources__/expectedObjects"; +import { + expectedGlobalConfigObject, expectedGlobalUserConfigObject, + expectedProjectConfigObject, expectedProjectUserConfigObject +} from "../__resources__/expectedObjects"; import * as fs from "fs"; -import { keyring as keytar } from "@zowe/secrets-for-zowe-sdk"; +import { keyring } from "@zowe/secrets-for-zowe-sdk"; import * as path from "path"; import * as lodash from "lodash"; import { IConfigProfile } from "../../../../../../../../src"; @@ -24,18 +27,28 @@ let TEST_ENVIRONMENT: ITestEnvironment; describe("imperative-test-cli config secure", () => { const service = "imperative-test-cli"; - let expectedProjectConfigLocation: string; - let expectedUserConfigLocation: string; - let expectedGlobalProjectConfigLocation: string; + let expectedGlobalConfigLocation: string; let expectedGlobalUserConfigLocation: string; + let expectedProjectConfigLocation: string; + let expectedProjectUserConfigLocation: string; + + const expectedGlobalConfig = lodash.cloneDeep(expectedGlobalConfigObject); + delete expectedGlobalConfig.$schema; + expectedGlobalConfig.profiles.global_base.properties.secret = "(secure value)"; + expectedGlobalConfig.profiles.global_base.secure = ["secret"]; + + const expectedGlobalUserConfig = lodash.cloneDeep(expectedGlobalUserConfigObject); + delete expectedGlobalUserConfig.$schema; + expectedGlobalUserConfig.profiles.global_base.secure = []; // config-init creates user base profiles with an empty secure array - const expectedJson = lodash.cloneDeep(expectedConfigObject); - delete expectedJson.$schema; - expectedJson.profiles.base.properties.secret = "(secure value)"; - expectedJson.profiles.base.secure = ["secret"]; + const expectedProjectConfig = lodash.cloneDeep(expectedProjectConfigObject); + delete expectedProjectConfig.$schema; + expectedProjectConfig.profiles.project_base.properties.secret = "(secure value)"; + expectedProjectConfig.profiles.project_base.secure = ["secret"]; - const expectedUserJson = lodash.cloneDeep(expectedUserConfigObject); - delete expectedUserJson.$schema; + const expectedProjectUserConfig = lodash.cloneDeep(expectedProjectUserConfigObject); + delete expectedProjectUserConfig.$schema; + expectedProjectUserConfig.profiles.project_base.secure = []; // config-init creates user base profiles with an empty secure array // Create the test environment beforeAll(async () => { @@ -44,15 +57,15 @@ describe("imperative-test-cli config secure", () => { testName: "imperative_test_cli_test_config_secure_command" }); expectedGlobalUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.user.json"); - expectedGlobalProjectConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); - expectedUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); + expectedGlobalConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); + expectedProjectUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); expectedProjectConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.json"); }); afterEach(async () => { runCliScript(__dirname + "/../__scripts__/delete_configs.sh", TEST_ENVIRONMENT.workingDir, ["-rf imperative-test-cli.config.user.json imperative-test-cli.config.json test schema.json"]); - await keytar.deletePassword(service, "secure_config_props"); + await keyring.deletePassword(service, "secure_config_props"); }); afterAll(() => { @@ -71,62 +84,62 @@ describe("imperative-test-cli config secure", () => { const fileContents = JSON.parse(fs.readFileSync(expectedProjectConfigLocation).toString()); const config = runCliScript(__dirname + "/../list/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--rfj"]).stdout.toString(); const configJson = JSON.parse(config); - const securedValue = await keytar.getPassword(service, "secure_config_props"); + const securedValue = await keyring.getPassword(service, "secure_config_props"); const securedValueJson = JSON.parse(Buffer.from(securedValue, "base64").toString()); const expectedSecuredValueJson: any = {}; expectedSecuredValueJson[expectedProjectConfigLocation] = { - "profiles.base.properties.secret": "anotherFakeValue" + "profiles.project_base.properties.secret": "anotherFakeValue" }; expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedJson); + expect(configJson.data).toEqual(expectedProjectConfig); // Should not contain human readable credentials - expect(fileContents.profiles.base.secure).toEqual(["secret"]); - expect(fileContents.profiles.base.properties).not.toEqual({secret: "anotherFakeValue"}); + expect(fileContents.profiles.project_base.secure).toEqual(["secret"]); + expect(fileContents.profiles.project_base.properties).not.toEqual({secret: "anotherFakeValue"}); // Check the securely stored JSON expect(securedValueJson).toEqual(expectedSecuredValueJson); }); - it("should secure the user config", async () => { + it("should secure the project user config", async () => { runCliScript(__dirname + "/../init/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--user-config"]); const response = runCliScript(__dirname + "/__scripts__/secure_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--user-config"]); - const fileContents = JSON.parse(fs.readFileSync(expectedUserConfigLocation).toString()); + const fileContents = JSON.parse(fs.readFileSync(expectedProjectUserConfigLocation).toString()); const config = runCliScript(__dirname + "/../list/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--rfj"]).stdout.toString(); const configJson = JSON.parse(config); - const securedValue = await keytar.getPassword(service, "secure_config_props"); + const securedValue = await keyring.getPassword(service, "secure_config_props"); const securedValueJson = securedValue == null ? null : JSON.parse(Buffer.from(securedValue, "base64").toString()); const expectedSecuredValueJson: any = null; expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedUserJson); + expect(configJson.data).toEqual(expectedProjectUserConfig); // Should not contain human readable credentials - expect(fileContents.profiles.base.secure).not.toEqual(["secret"]); - expect(fileContents.profiles.base.properties).not.toEqual({secret: "anotherFakeValue"}); + expect(fileContents.profiles.project_base.secure).not.toEqual(["secret"]); + expect(fileContents.profiles.project_base.properties).not.toEqual({secret: "anotherFakeValue"}); // Check the securely stored JSON expect(securedValueJson).toEqual(expectedSecuredValueJson); }); - it("should secure the global project config", async () => { + it("should secure the global config", async () => { runCliScript(__dirname + "/../init/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--global-config"]); const response = runCliScript(__dirname + "/__scripts__/secure_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--global-config"]); - const fileContents = JSON.parse(fs.readFileSync(expectedGlobalProjectConfigLocation).toString()); + const fileContents = JSON.parse(fs.readFileSync(expectedGlobalConfigLocation).toString()); const config = runCliScript(__dirname + "/../list/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--rfj"]).stdout.toString(); const configJson = JSON.parse(config); - const securedValue = await keytar.getPassword(service, "secure_config_props"); + const securedValue = await keyring.getPassword(service, "secure_config_props"); const securedValueJson = JSON.parse(Buffer.from(securedValue, "base64").toString()); const expectedSecuredValueJson: any = {}; - expectedSecuredValueJson[expectedGlobalProjectConfigLocation] = { - "profiles.base.properties.secret": "anotherFakeValue" + expectedSecuredValueJson[expectedGlobalConfigLocation] = { + "profiles.global_base.properties.secret": "anotherFakeValue" }; expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedJson); + expect(configJson.data).toEqual(expectedGlobalConfig); // Should not contain human readable credentials - expect(fileContents.profiles.base.secure).toEqual(["secret"]); - expect(fileContents.profiles.base.properties).not.toEqual({secret: "anotherFakeValue"}); + expect(fileContents.profiles.global_base.secure).toEqual(["secret"]); + expect(fileContents.profiles.global_base.properties).not.toEqual({secret: "anotherFakeValue"}); // Check the securely stored JSON expect(securedValueJson).toEqual(expectedSecuredValueJson); }); @@ -137,16 +150,16 @@ describe("imperative-test-cli config secure", () => { const fileContents = JSON.parse(fs.readFileSync(expectedGlobalUserConfigLocation).toString()); const config = runCliScript(__dirname + "/../list/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--rfj"]).stdout.toString(); const configJson = JSON.parse(config); - const securedValue = await keytar.getPassword(service, "secure_config_props"); + const securedValue = await keyring.getPassword(service, "secure_config_props"); const securedValueJson = securedValue == null ? null : JSON.parse(Buffer.from(securedValue, "base64").toString()); const expectedSecuredValueJson: any = null; expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedUserJson); + expect(configJson.data).toEqual(expectedGlobalUserConfig); // Should not contain human readable credentials - expect(fileContents.profiles.base.secure).not.toEqual(["secret"]); - expect(fileContents.profiles.base.properties).not.toEqual({secret: "anotherFakeValue"}); + expect(fileContents.profiles.global_base.secure).not.toEqual(["secret"]); + expect(fileContents.profiles.global_base.properties).not.toEqual({secret: "anotherFakeValue"}); // Check the securely stored JSON expect(securedValueJson).toEqual(expectedSecuredValueJson); }); @@ -166,27 +179,27 @@ describe("imperative-test-cli config secure", () => { ] }; runCliScript(__dirname + "/../set/__scripts__/set.sh", TEST_ENVIRONMENT.workingDir, - ["profiles", JSON.stringify({ base: baseProfile }), "--json"]); + ["profiles", JSON.stringify({ project_base: baseProfile }), "--json"]); const response = runCliScript(__dirname + "/__scripts__/secure_prompt.sh", TEST_ENVIRONMENT.workingDir); const fileContents = JSON.parse(fs.readFileSync(expectedProjectConfigLocation).toString()); const config = runCliScript(__dirname + "/../list/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--rfj"]).stdout.toString(); const configJson = JSON.parse(config); - const expectedJsonWithToken = lodash.cloneDeep(expectedJson); - expectedJsonWithToken.profiles = { base: baseProfile }; - expectedJsonWithToken.profiles.base.properties.tokenValue = "(secure value)"; - const securedValue = await keytar.getPassword(service, "secure_config_props"); + const expectedJsonWithToken = lodash.cloneDeep(expectedProjectConfig); + expectedJsonWithToken.profiles = { project_base: baseProfile }; + expectedJsonWithToken.profiles.project_base.properties.tokenValue = "(secure value)"; + const securedValue = await keyring.getPassword(service, "secure_config_props"); const securedValueJson = JSON.parse(Buffer.from(securedValue, "base64").toString()); const expectedSecuredValueJson: any = {}; expectedSecuredValueJson[expectedProjectConfigLocation] = { - "profiles.base.properties.tokenValue": "fakeUser:anotherFakeValue@fakeToken" + "profiles.project_base.properties.tokenValue": "fakeUser:anotherFakeValue@fakeToken" }; expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); expect(configJson.data).toEqual(expectedJsonWithToken); // Should not contain human readable credentials - expect(fileContents.profiles.base.secure).toEqual(["tokenValue"]); - expect(fileContents.profiles.base.properties.tokenValue).toBeUndefined(); + expect(fileContents.profiles.project_base.secure).toEqual(["tokenValue"]); + expect(fileContents.profiles.project_base.properties.tokenValue).toBeUndefined(); // Check the securely stored JSON expect(securedValueJson).toEqual(expectedSecuredValueJson); }); diff --git a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/set/cli.imperative-test-cli.config.set.integration.subtest.ts b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/set/cli.imperative-test-cli.config.set.integration.subtest.ts index e223fee09c..bfefe5051c 100644 --- a/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/set/cli.imperative-test-cli.config.set.integration.subtest.ts +++ b/packages/imperative/__tests__/__integration__/imperative/__tests__/__integration__/cli/config/set/cli.imperative-test-cli.config.set.integration.subtest.ts @@ -12,7 +12,10 @@ import { ITestEnvironment } from "../../../../../../../__src__/environment/doc/response/ITestEnvironment"; import { SetupTestEnvironment } from "../../../../../../../__src__/environment/SetupTestEnvironment"; import { runCliScript } from "../../../../../../../src/TestUtil"; -import { expectedConfigObject, expectedUserConfigObject } from "../__resources__/expectedObjects"; +import { + expectedGlobalConfigObject, expectedGlobalUserConfigObject, + expectedProjectConfigObject, expectedProjectUserConfigObject +} from "../__resources__/expectedObjects"; import * as fs from "fs"; import * as path from "path"; import { keyring as keytar } from "@zowe/secrets-for-zowe-sdk"; @@ -25,20 +28,34 @@ describe("imperative-test-cli config set", () => { const service = "imperative-test-cli"; let expectedProjectConfigLocation: string; let expectedUserConfigLocation: string; - let expectedGlobalProjectConfigLocation: string; + let expectedGlobalConfigLocation: string; let expectedGlobalUserConfigLocation: string; - const expectedJson = lodash.cloneDeep(expectedConfigObject); - delete expectedJson.$schema; - expectedJson.profiles.secured.properties.info = "(secure value)"; - expectedJson.profiles.secured.secure = ["info"]; - expectedJson.profiles.base.properties.secret = "(secure value)"; - expectedJson.profiles.base.secure = ["secret"]; + const expectedProjJson = lodash.cloneDeep(expectedProjectConfigObject); + delete expectedProjJson.$schema; + expectedProjJson.profiles.secured.properties.info = "(secure value)"; + expectedProjJson.profiles.secured.secure = ["info"]; + expectedProjJson.profiles.project_base.properties.secret = "(secure value)"; + expectedProjJson.profiles.project_base.secure = ["secret"]; - const expectedUserJson = lodash.cloneDeep(expectedUserConfigObject); - delete expectedUserJson.$schema; - expectedUserJson.profiles.secured.properties.info = "(secure value)"; - expectedUserJson.profiles.secured.secure = ["info"]; + const expectedGlobalJson = lodash.cloneDeep(expectedGlobalConfigObject); + delete expectedGlobalJson.$schema; + expectedGlobalJson.profiles.secured.properties.info = "(secure value)"; + expectedGlobalJson.profiles.secured.secure = ["info"]; + expectedGlobalJson.profiles.global_base.properties.secret = "(secure value)"; + expectedGlobalJson.profiles.global_base.secure = ["secret"]; + + const expectedProjUserJson = lodash.cloneDeep(expectedProjectUserConfigObject); + delete expectedProjUserJson.$schema; + expectedProjUserJson.profiles.secured.properties.info = "(secure value)"; + expectedProjUserJson.profiles.secured.secure = ["info"]; + expectedProjUserJson.profiles.project_base.secure = []; // config-init creates user base profile with an empty secure array + + const expectedGlobalUserJson = lodash.cloneDeep(expectedGlobalUserConfigObject); + delete expectedGlobalUserJson.$schema; + expectedGlobalUserJson.profiles.secured.properties.info = "(secure value)"; + expectedGlobalUserJson.profiles.secured.secure = ["info"]; + expectedGlobalUserJson.profiles.global_base.secure = []; // config-init creates user base profile with an empty secure array // Create the test environment beforeAll(async () => { @@ -47,7 +64,7 @@ describe("imperative-test-cli config set", () => { testName: "imperative_test_cli_test_config_set_command" }); expectedGlobalUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.user.json"); - expectedGlobalProjectConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); + expectedGlobalConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "imperative-test-cli.config.json"); expectedUserConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.user.json"); expectedProjectConfigLocation = path.join(TEST_ENVIRONMENT.workingDir, "test", "imperative-test-cli.config.json"); await keytar.setPassword("imperative-test-cli", "secure_config_props", Buffer.from("{}").toString("base64")); @@ -105,20 +122,20 @@ describe("imperative-test-cli config set", () => { const securedValueJson = JSON.parse(Buffer.from(securedValue, "base64").toString()); const expectedSecuredValueJson: any = {}; expectedSecuredValueJson[expectedProjectConfigLocation] = { - "profiles.base.properties.secret": "fakeValue", + "profiles.project_base.properties.secret": "fakeValue", "profiles.secured.properties.info": "some_fake_information" }; expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedJson); + expect(configJson.data).toEqual(expectedProjJson); // Should not contain human readable credentials expect(fileContents.profiles.secured.secure).toEqual(["info"]); expect(fileContents.profiles.secured.properties).not.toEqual({info: "some_fake_information"}); // Check the securely stored JSON expect(securedValueJson).toEqual(expectedSecuredValueJson); }); - it("should make the info property secure in the user config", async () => { + it("should make the info property secure in the project user config", async () => { runCliScript(__dirname + "/../init/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--user-config"]); const response = runCliScript(__dirname + "/__scripts__/set_secure.sh", TEST_ENVIRONMENT.workingDir, ["profiles.secured.properties.info", "some_fake_information", "--user-config"]); @@ -134,31 +151,31 @@ describe("imperative-test-cli config set", () => { expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedUserJson); + expect(configJson.data).toEqual(expectedProjUserJson); // Should not contain human readable credentials expect(fileContents.profiles.secured.secure).toEqual(["info"]); expect(fileContents.profiles.secured.properties).not.toEqual({info: "some_fake_information"}); // Check the securely stored JSON expect(securedValueJson).toEqual(expectedSecuredValueJson); }); - it("should make the info property secure in the global project config", async () => { + it("should make the info property secure in the global config", async () => { runCliScript(__dirname + "/../init/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, ["--global-config"]); const response = runCliScript(__dirname + "/__scripts__/set_secure.sh", TEST_ENVIRONMENT.workingDir, ["profiles.secured.properties.info", "some_fake_information", "--global-config"]); - const fileContents = JSON.parse(fs.readFileSync(expectedGlobalProjectConfigLocation).toString()); + const fileContents = JSON.parse(fs.readFileSync(expectedGlobalConfigLocation).toString()); const config = runCliScript(__dirname + "/../list/__scripts__/list_config.sh", TEST_ENVIRONMENT.workingDir, ["--rfj"]).stdout.toString(); const configJson = JSON.parse(config); const securedValue = await keytar.getPassword(service, "secure_config_props"); const securedValueJson = JSON.parse(Buffer.from(securedValue, "base64").toString()); const expectedSecuredValueJson: any = {}; - expectedSecuredValueJson[expectedGlobalProjectConfigLocation] = { - "profiles.base.properties.secret": "fakeValue", + expectedSecuredValueJson[expectedGlobalConfigLocation] = { + "profiles.global_base.properties.secret": "fakeValue", "profiles.secured.properties.info": "some_fake_information" }; expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedJson); + expect(configJson.data).toEqual(expectedGlobalJson); // Should not contain human readable credentials expect(fileContents.profiles.secured.secure).toEqual(["info"]); expect(fileContents.profiles.secured.properties).not.toEqual({info: "some_fake_information"}); @@ -181,7 +198,7 @@ describe("imperative-test-cli config set", () => { expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedUserJson); + expect(configJson.data).toEqual(expectedGlobalUserJson); // Should not contain human readable credentials expect(fileContents.profiles.secured.secure).toEqual(["info"]); expect(fileContents.profiles.secured.properties).not.toEqual({info: "some_fake_information"}); @@ -204,7 +221,7 @@ describe("imperative-test-cli config set", () => { expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); - expect(configJson.data).toEqual(expectedUserJson); + expect(configJson.data).toEqual(expectedGlobalUserJson); // Should not contain human readable credentials expect(fileContents.profiles.secured.secure).toEqual(["info"]); expect(fileContents.profiles.secured.properties).not.toEqual({info: {data: "fake"}}); @@ -222,20 +239,20 @@ describe("imperative-test-cli config set", () => { it("should store property securely without --secure flag if found in secure array", async () => { runCliScript(__dirname + "/../init/__scripts__/init_config_prompt.sh", TEST_ENVIRONMENT.workingDir, [""]); const response = runCliScript(__dirname + "/__scripts__/set.sh", TEST_ENVIRONMENT.workingDir, - ["profiles.base.properties.secret", "area51", ""]); + ["profiles.project_base.properties.secret", "area51", ""]); const fileContents = JSON.parse(fs.readFileSync(expectedProjectConfigLocation).toString()); const securedValue = await keytar.getPassword(service, "secure_config_props"); const securedValueJson = JSON.parse(Buffer.from(securedValue, "base64").toString()); const expectedSecuredValueJson: any = {}; expectedSecuredValueJson[expectedProjectConfigLocation] = { - "profiles.base.properties.secret": "area51" + "profiles.project_base.properties.secret": "area51" }; expect(response.stderr.toString()).toEqual(""); expect(response.status).toEqual(0); // Should not contain human readable credentials - expect(fileContents.profiles.base.secure).toEqual(["secret"]); - expect(fileContents.profiles.base.properties).toEqual({}); + expect(fileContents.profiles.project_base.secure).toEqual(["secret"]); + expect(fileContents.profiles.project_base.properties).toEqual({}); // Check the securely stored JSON expect(securedValueJson).toEqual(expectedSecuredValueJson); }); diff --git a/packages/imperative/package.json b/packages/imperative/package.json index e7f659e301..f0b6be5f6b 100644 --- a/packages/imperative/package.json +++ b/packages/imperative/package.json @@ -1,6 +1,6 @@ { "name": "@zowe/imperative", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "description": "framework for building configurable CLIs", "author": "Zowe", "license": "EPL-2.0", @@ -78,7 +78,6 @@ "strip-ansi": "^6.0.1", "which": "^4.0.0", "wrap-ansi": "^7.0.0", - "yamljs": "^0.3.0", "yargs": "^17.7.2" }, "devDependencies": { @@ -94,7 +93,7 @@ "@types/pacote": "^11.1.8", "@types/progress": "^2.0.7", "@types/stack-trace": "^0.0.33", - "@zowe/secrets-for-zowe-sdk": "8.0.0-next.202407181255", + "@zowe/secrets-for-zowe-sdk": "8.0.0-next.202407311544", "concurrently": "^8.0.0", "cowsay": "^1.6.0", "deep-diff": "^1.0.0", diff --git a/packages/imperative/src/cmd/__tests__/CommandProcessor.unit.test.ts b/packages/imperative/src/cmd/__tests__/CommandProcessor.unit.test.ts index aaaa738106..be3f7bfe2a 100644 --- a/packages/imperative/src/cmd/__tests__/CommandProcessor.unit.test.ts +++ b/packages/imperative/src/cmd/__tests__/CommandProcessor.unit.test.ts @@ -25,6 +25,8 @@ import { ImperativeConfig } from "../../utilities/src/ImperativeConfig"; import { setupConfigToLoad } from "../../../__tests__/src/TestUtil"; import { EnvFileUtils } from "../../utilities"; import { join } from "path"; +import { Config } from "../../config"; +import { LoggerUtils } from "../../logger/src/LoggerUtils"; jest.mock("../src/syntax/SyntaxValidator"); jest.mock("../src/utils/SharedOptions"); @@ -1479,7 +1481,7 @@ describe("Command Processor", () => { rootCommandName: SAMPLE_ROOT_COMMAND, commandLine: "", promptPhrase: "dummydummy", - config: ImperativeConfig.instance.config + config: ImperativeConfig.instance.config }); // Mock the profile loader @@ -1506,7 +1508,8 @@ describe("Command Processor", () => { expect(commandResponse.data.optionalProfiles[0]).toBe(`banana`); expect(commandResponse.data.requiredProfiles).toBeUndefined(); }); - it("should mask input value for a secure parm when --show-inputs-only flag is set", async () => { + + it("should mask input value for a default secure parm when --show-inputs-only flag is set", async () => { // values to test const secretParmKey = `brownSpots`; @@ -1589,7 +1592,7 @@ describe("Command Processor", () => { rootCommandName: SAMPLE_ROOT_COMMAND, commandLine: "", promptPhrase: "dummydummy", - config: ImperativeConfig.instance.config + config: ImperativeConfig.instance.config }); // Mock the profile loader @@ -1619,6 +1622,80 @@ describe("Command Processor", () => { expect(commandResponse.data.requiredProfiles).toBeUndefined(); }); + it.each(LoggerUtils.SECURE_PROMPT_OPTIONS)("should mask input value for secure parm %s when --show-inputs-only flag is set", async (propName) => { + + // values to test + const parm1Key = CliUtils.getOptionFormat(propName).kebabCase; + const parm1Value = `secret`; + const secure = `(secure value)`; + jest.spyOn(ImperativeConfig, "instance", "get").mockReturnValue({ + config: { + api: { + secure: { + securePropsForProfile: jest.fn(() => [propName]) + } + }, + layers: [{ exists: true, path: "zowe.config.json" }], + properties: Config.empty() + } + } as any); + + // Allocate the command processor + const processor: CommandProcessor = new CommandProcessor({ + envVariablePrefix: ENV_VAR_PREFIX, + fullDefinition: SAMPLE_COMPLEX_COMMAND, // `group action` + definition: { // `object` + name: "banana", + description: "The banana command", + type: "command", + handler: __dirname + "/__model__/TestCmdHandler", + options: [ + { + name: parm1Key, + type: "string", + description: "The first parameter", + } + ], + profile: { + optional: ["fruit"] + } + }, + helpGenerator: FAKE_HELP_GENERATOR, + rootCommandName: SAMPLE_ROOT_COMMAND, + commandLine: "", + promptPhrase: "dummydummy", + config: ImperativeConfig.instance.config + }); + + // Mock the profile loader + (CommandProfileLoader.loader as any) = jest.fn((args) => { + return { + loadProfiles: (profArgs: any) => { + return; + } + }; + }); + + // return the "fake" args object with values from profile + CliUtils.getOptValueFromProfiles = jest.fn((cmdProfiles, profileDef, allOpts) => { + return {}; + }); + + const parms: any = { + arguments: { + _: ["check", "for", "banana"], + $0: "", + [parm1Key]: parm1Value, + valid: true, + showInputsOnly: true, + }, + silent: true + }; + const commandResponse: ICommandResponse = await processor.invoke(parms); + expect(commandResponse.data.commandValues[parm1Key]).toBe(secure); + expect(commandResponse.stderr.toString()).toContain(`Some inputs are not displayed`); + }); + it("should not mask input value for a secure parm when --show-inputs-only flag is set with env setting", async () => { // values to test diff --git a/packages/imperative/src/config/__tests__/ConfigBuilder.unit.test.ts b/packages/imperative/src/config/__tests__/ConfigBuilder.unit.test.ts index ad4ef781da..b686927d39 100644 --- a/packages/imperative/src/config/__tests__/ConfigBuilder.unit.test.ts +++ b/packages/imperative/src/config/__tests__/ConfigBuilder.unit.test.ts @@ -14,6 +14,9 @@ import { Config, ConfigBuilder, IConfig } from "../"; import * as config from "../../../__tests__/__integration__/imperative/src/imperative"; import * as lodash from "lodash"; +const CHOOSE_GLOB_CONFIG = true; +const CHOOSE_PROJ_CONFIG = false; + const expectedConfigObject: IConfig = { autoStore: true, defaults: {}, @@ -67,14 +70,14 @@ describe("Config Builder tests", () => { describe("build", () => { it("should build a config without populating properties", async () => { - const builtConfig = await ConfigBuilder.build(testConfig); + const builtConfig = await ConfigBuilder.build(testConfig, CHOOSE_PROJ_CONFIG); expect(configEmptySpy).toHaveBeenCalledTimes(1); expect(getDefaultValueSpy).toHaveBeenCalledTimes(0); // Not populating any properties expect(builtConfig).toEqual(expectedConfig); }); it("should build a config and populate properties", async () => { - const builtConfig = await ConfigBuilder.build(testConfig, {populateProperties: true}); + const builtConfig = await ConfigBuilder.build(testConfig, CHOOSE_PROJ_CONFIG, {populateProperties: true}); expectedConfig.profiles.secured.properties.info = ""; expectedConfig.profiles.secured.secure.push("secret"); expectedConfig.defaults = { secured: "secured" }; @@ -85,7 +88,7 @@ describe("Config Builder tests", () => { it("should build a config and populate properties, even option with missing option definition", async () => { testConfig.profiles[0].schema.properties.fakestr = buildProfileProperty("fakestr", "string", true); - const builtConfig = await ConfigBuilder.build(testConfig, {populateProperties: true}); + const builtConfig = await ConfigBuilder.build(testConfig, CHOOSE_PROJ_CONFIG, {populateProperties: true}); expectedConfig.profiles.secured.properties.info = ""; expectedConfig.profiles.secured.properties.fakestr = ""; expectedConfig.profiles.secured.secure.push("secret"); @@ -103,7 +106,7 @@ describe("Config Builder tests", () => { testConfig.profiles[0].schema.properties.fakebool = buildProfileProperty("fakebool", "boolean"); testConfig.profiles[0].schema.properties.fakedflt = buildProfileProperty("fakedflt", "IShouldntExist"); - const builtConfig = await ConfigBuilder.build(testConfig, {populateProperties: true}); + const builtConfig = await ConfigBuilder.build(testConfig, CHOOSE_PROJ_CONFIG, {populateProperties: true}); expectedConfig.profiles.secured.properties.info = ""; expectedConfig.profiles.secured.properties.fakestr = ""; expectedConfig.profiles.secured.properties.fakenum = 0; @@ -122,7 +125,7 @@ describe("Config Builder tests", () => { it("should build a config and populate an empty property that can have multiple types", async () => { testConfig.profiles[0].schema.properties.fakestr = buildProfileProperty("fakestr", ["string", "number", "boolean"]); - const builtConfig = await ConfigBuilder.build(testConfig, {populateProperties: true}); + const builtConfig = await ConfigBuilder.build(testConfig, CHOOSE_PROJ_CONFIG, {populateProperties: true}); expectedConfig.profiles.secured.properties.info = ""; expectedConfig.profiles.secured.properties.fakestr = ""; expectedConfig.profiles.secured.secure.push("secret"); @@ -133,7 +136,7 @@ describe("Config Builder tests", () => { expect(builtConfig).toEqual(expectedConfig); }); - it("should build a config with a base profile", async () => { + it("should build a config with a project base profile", async () => { testConfig.baseProfile = { type: "base", schema: { @@ -144,7 +147,7 @@ describe("Config Builder tests", () => { } }; testConfig.profiles.push(testConfig.baseProfile); - const builtConfig = await ConfigBuilder.build(testConfig, {populateProperties: true}); + const builtConfig = await ConfigBuilder.build(testConfig, CHOOSE_PROJ_CONFIG, {populateProperties: true}); expectedConfig.profiles = { secured: { type: "secured", @@ -153,7 +156,7 @@ describe("Config Builder tests", () => { }, secure: ["secret"] }, - base: { + project_base: { type: "base", properties: { host: "" @@ -161,14 +164,14 @@ describe("Config Builder tests", () => { secure: [] } }; - expectedConfig.defaults = { base: "base", secured: "secured" }; + expectedConfig.defaults = { base: "project_base", secured: "secured" }; expect(configEmptySpy).toHaveBeenCalledTimes(1); expect(getDefaultValueSpy).toHaveBeenCalledTimes(2); // Populating default value for host and info expect(builtConfig).toEqual(expectedConfig); }); - it("should build a config with a base profile and prompt for missing property", async () => { + it("should build a config with a global base profile and prompt for missing property", async () => { testConfig.baseProfile = { type: "base", schema: { @@ -179,7 +182,7 @@ describe("Config Builder tests", () => { } }; testConfig.profiles.push(testConfig.baseProfile); - const builtConfig = await ConfigBuilder.build(testConfig, {populateProperties: true, getValueBack}); + const builtConfig = await ConfigBuilder.build(testConfig, CHOOSE_GLOB_CONFIG, {populateProperties: true, getValueBack}); expectedConfig.profiles = { secured: { type: "secured", @@ -188,7 +191,7 @@ describe("Config Builder tests", () => { }, secure: ["secret"] }, - base: { + global_base: { type: "base", properties: { host: "fake value", @@ -196,14 +199,14 @@ describe("Config Builder tests", () => { secure: [] } }; - expectedConfig.defaults = { base: "base", secured: "secured" }; + expectedConfig.defaults = { base: "global_base", secured: "secured" }; expect(configEmptySpy).toHaveBeenCalledTimes(1); expect(getDefaultValueSpy).toHaveBeenCalledTimes(2); // Populating default value for host and info expect(builtConfig).toEqual(expectedConfig); }); - it("should build a config with a base profile and prompt for missing secure property", async () => { + it("should build a config with a global base profile and prompt for missing secure property", async () => { testConfig.profiles.push(testConfig.baseProfile); expectedConfig.profiles = { secured: { @@ -213,7 +216,7 @@ describe("Config Builder tests", () => { }, secure: ["secret"] }, - base: { + global_base: { type: "base", properties: { secret: "fake value" @@ -221,11 +224,11 @@ describe("Config Builder tests", () => { secure: ["secret"] } }; - expectedConfig.defaults = { base: "base", secured: "secured" }; + expectedConfig.defaults = { base: "global_base", secured: "secured" }; let builtConfig; let caughtError; try { - builtConfig = await ConfigBuilder.build(testConfig, {populateProperties: true, getValueBack}); + builtConfig = await ConfigBuilder.build(testConfig, CHOOSE_GLOB_CONFIG, {populateProperties: true, getValueBack}); } catch (error) { caughtError = error; } diff --git a/packages/imperative/src/config/__tests__/ConfigUtils.unit.test.ts b/packages/imperative/src/config/__tests__/ConfigUtils.unit.test.ts index ae94f0e098..f064825c66 100644 --- a/packages/imperative/src/config/__tests__/ConfigUtils.unit.test.ts +++ b/packages/imperative/src/config/__tests__/ConfigUtils.unit.test.ts @@ -158,6 +158,107 @@ describe("Config Utils", () => { }); }); + describe("formGlobOrProjProfileNm", () => { + afterEach(() => { + /* zzz + jest.restoreAllMocks(); // restore spies + jest.clearAllMocks(); // set counts back to zero + */ + }); + + it("should return the type name if the type is not base", () => { + const baseProfileName = ConfigUtils.formGlobOrProjProfileNm("zosmf", false); + expect(baseProfileName).toEqual("zosmf"); + }); + + it("should return a project base profile name when asked", () => { + const baseProfileName = ConfigUtils.formGlobOrProjProfileNm("base", false); + expect(baseProfileName).toEqual("project_base"); + }); + + it("should return a global base profile name when asked", () => { + const baseProfileName = ConfigUtils.formGlobOrProjProfileNm("base", true); + expect(baseProfileName).toEqual("global_base"); + }); + + it("should return a global base profile name when no project layer exists", () => { + jest.spyOn(ImperativeConfig, "instance", "get").mockReturnValue({ + config: { + exists: true, + layers: [ + { + path: "fakePath", + exists: true, + properties: {}, + global: true, + user: false + } + ] + } + } as any); + + const baseProfileName = ConfigUtils.formGlobOrProjProfileNm("base"); + expect(baseProfileName).toEqual("global_base"); + }); + + it("should return a global base profile name when no base type in nested profiles", () => { + jest.spyOn(ImperativeConfig, "instance", "get").mockReturnValue({ + config: { + exists: true, + layers: [ + { + path: "fakePath", + exists: true, + properties: {}, + global: false, + user: false + } + ], + layerProfiles: jest.fn(() => { + return { + properties: {} + }; + }) + } + } as any); + + const baseProfileName = ConfigUtils.formGlobOrProjProfileNm("base"); + expect(baseProfileName).toEqual("global_base"); + }); + + it("should return a project base profile name when found in nested profiles", () => { + jest.spyOn(ImperativeConfig, "instance", "get").mockReturnValue({ + config: { + exists: true, + layers: [ + { + path: "fakePath", + exists: true, + properties: {}, + global: false, + user: false + } + ], + layerProfiles: jest.fn(() => { + return { + properties: { + profiles: { + profiles: { + properties: {}, + type: "base" + } + } + } + }; + }) + } + } as any); + + const baseProfileName = ConfigUtils.formGlobOrProjProfileNm("base"); + expect(baseProfileName).toEqual("project_base"); + }); + }); + describe("getZoweDir", () => { const expectedLoadedConfig = { name: "zowe", diff --git a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts index 5bd89d66bc..ab7a40eac2 100644 --- a/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts +++ b/packages/imperative/src/config/__tests__/ProfileInfo.TeamConfig.unit.test.ts @@ -1030,7 +1030,7 @@ describe("TeamConfig ProfileInfo tests", () => { }); describe("updateProperty", () => { - it("should throw and error if the desired profile is not found", async () => { + it("should throw an error if the desired profile is not found", async () => { const profInfo = createNewProfInfo(teamProjDir); await profInfo.readProfilesFromDisk(); jest.spyOn(profInfo as any, "getAllProfiles").mockReturnValue([]); @@ -1175,6 +1175,76 @@ describe("TeamConfig ProfileInfo tests", () => { profileType: "dummy" }); }); + + it("should succeed storing property in typeless profile if profile exists", async () => { + const profInfo = createNewProfInfo(teamProjDir); + await profInfo.readProfilesFromDisk(); + const storeSpy = jest.spyOn(ConfigAutoStore, "_storeSessCfgProps").mockImplementation(jest.fn()); + const profileOptions: IProfInfoUpdatePropOpts = { + profileName: "typeless", + profileType: null, + property: "areBirdsReal", + value: true + }; + let caughtError; + try { + await profInfo.updateProperty(profileOptions); + } catch (error) { + caughtError = error; + } + expect(caughtError).toBeUndefined(); + expect(storeSpy).toHaveBeenCalledWith({ + config: profInfo.getTeamConfig(), profileName: "typeless", profileType: null, + defaultBaseProfileName: "base_glob", + propsToStore: [ "areBirdsReal" ], sessCfg: { "areBirdsReal": true }, setSecure : undefined, + }); + }); + + it("should fail to store property in typeless profile if profile doesn't exist", async () => { + const profInfo = createNewProfInfo(teamProjDir); + await profInfo.readProfilesFromDisk(); + const profileOptions: IProfInfoUpdatePropOpts = { + profileName: "typeless_new", + profileType: null, + property: "areBirdsReal", + value: true + }; + let caughtError; + try { + await profInfo.updateProperty(profileOptions); + } catch (error) { + caughtError = error; + } + + expect(caughtError).toBeDefined(); + expect(caughtError.errorCode).toBe(ProfInfoErr.PROF_NOT_FOUND); + expect(caughtError.message).toContain("Failed to find profile"); + }); + + it("should succeed storing property in typeless profile if profile doesn't exist and forceUpdate is true", async () => { + const profInfo = createNewProfInfo(teamProjDir); + await profInfo.readProfilesFromDisk(); + const storeSpy = jest.spyOn(ConfigAutoStore, "_storeSessCfgProps").mockImplementation(jest.fn()); + const profileOptions: IProfInfoUpdatePropOpts = { + profileName: "typeless_new", + profileType: null, + property: "areBirdsReal", + value: true, + forceUpdate: true + }; + let caughtError; + try { + await profInfo.updateProperty(profileOptions); + } catch (error) { + caughtError = error; + } + expect(caughtError).toBeUndefined(); + expect(storeSpy).toHaveBeenCalledWith({ + config: profInfo.getTeamConfig(), profileName: "typeless_new", profileType: null, + defaultBaseProfileName: "base_glob", + propsToStore: [ "areBirdsReal" ], sessCfg: { "areBirdsReal": true }, setSecure : undefined, + }); + }); }); describe("updateKnownProperty", () => { diff --git a/packages/imperative/src/config/__tests__/__resources__/ProfInfoApp_team_config_proj/ProfInfoApp.config.json b/packages/imperative/src/config/__tests__/__resources__/ProfInfoApp_team_config_proj/ProfInfoApp.config.json index 9eea51ef10..0823095788 100644 --- a/packages/imperative/src/config/__tests__/__resources__/ProfInfoApp_team_config_proj/ProfInfoApp.config.json +++ b/packages/imperative/src/config/__tests__/__resources__/ProfInfoApp_team_config_proj/ProfInfoApp.config.json @@ -69,6 +69,9 @@ "responseFormatHeader": true, "fakeOffSchemaArg": "fakeArg" } + }, + "typeless": { + "properties": {} } }, "defaults": { diff --git a/packages/imperative/src/config/src/ConfigAutoStore.ts b/packages/imperative/src/config/src/ConfigAutoStore.ts index cd9b87d001..a399be85ee 100644 --- a/packages/imperative/src/config/src/ConfigAutoStore.ts +++ b/packages/imperative/src/config/src/ConfigAutoStore.ts @@ -132,6 +132,7 @@ export class ConfigAutoStore { const config = opts.config || ImperativeConfig.instance.config; // TODO Which autoStore value should take priority if it conflicts between layers if (opts.propsToStore.length == 0 || !config?.exists || !config.properties.autoStore) { + Logger.getAppLogger().info("Skipping update of profile properties. Check that config file exists and autoStore is true."); return; } diff --git a/packages/imperative/src/config/src/ConfigBuilder.ts b/packages/imperative/src/config/src/ConfigBuilder.ts index 02b2645cb8..f72bd34eab 100644 --- a/packages/imperative/src/config/src/ConfigBuilder.ts +++ b/packages/imperative/src/config/src/ConfigBuilder.ts @@ -15,35 +15,41 @@ import { Config } from "./Config"; import { IConfig } from "./doc/IConfig"; import { IConfigBuilderOpts } from "./doc/IConfigBuilderOpts"; import { ICommandProfileTypeConfiguration } from "../../cmd"; +import { ConfigUtils } from "./ConfigUtils"; export class ConfigBuilder { /** * Build a new Config object from an Imperative CLI app configuration. * @param impConfig The Imperative CLI app configuration. + * @param globalConfig Is the config to be a global config? * @param opts Options to control aspects of the builder. */ - public static async build(impConfig: IImperativeConfig, opts?: IConfigBuilderOpts): Promise { + public static async build(impConfig: IImperativeConfig, globalConfig: boolean, opts?: IConfigBuilderOpts): Promise { opts = opts || {}; const builtConfig: IConfig = Config.empty(); for (const profile of impConfig.profiles) { const defaultProfile = ConfigBuilder.buildDefaultProfile(profile, opts); + // Name our profile differently in a global config vs a project config + const profileName: string = ConfigUtils.formGlobOrProjProfileNm(profile.type, globalConfig); + // Add the profile to config and set it as default - lodash.set(builtConfig, `profiles.${profile.type}`, defaultProfile); + lodash.set(builtConfig, `profiles.${profileName}`, defaultProfile); if (opts.populateProperties) { - builtConfig.defaults[profile.type] = profile.type; + builtConfig.defaults[profile.type] = profileName; } } // Prompt for properties missing from base profile if (impConfig.baseProfile != null && opts.getValueBack != null) { + const baseProfileNm: string = ConfigUtils.formGlobOrProjProfileNm(impConfig.baseProfile.type, globalConfig); for (const [k, v] of Object.entries(impConfig.baseProfile.schema.properties)) { if (v.includeInTemplate && v.optionDefinition?.defaultValue == null) { const propValue = await opts.getValueBack(k, v); if (propValue != null) { - lodash.set(builtConfig, `profiles.${impConfig.baseProfile.type}.properties.${k}`, propValue); + lodash.set(builtConfig, `profiles.${baseProfileNm}.properties.${k}`, propValue); } } } diff --git a/packages/imperative/src/config/src/ConfigUtils.ts b/packages/imperative/src/config/src/ConfigUtils.ts index 2bcb7141df..79b8640537 100644 --- a/packages/imperative/src/config/src/ConfigUtils.ts +++ b/packages/imperative/src/config/src/ConfigUtils.ts @@ -22,6 +22,7 @@ import { LoggerManager } from "../../logger/src/LoggerManager"; import { LoggingConfigurer } from "../../imperative/src/LoggingConfigurer"; import { Logger } from "../../logger/src/Logger"; import { EnvironmentalVariableSettings } from "../../imperative/src/env/EnvironmentalVariableSettings"; +import { IConfigProfile } from "./doc/IConfigProfile"; import { IExtendersJsonOpts } from "./doc/IExtenderOpts"; export class ConfigUtils { @@ -175,7 +176,6 @@ export class ConfigUtils { }); } - // _______________________________________________________________________ /** * Perform a rudimentary initialization of some Imperative utilities. @@ -213,4 +213,86 @@ export class ConfigUtils { return Logger.getImperativeLogger(); } + // _______________________________________________________________________ + /** + * Form a profile name of a given profile type to be used as a default + * profile name. The name can vary based on whether the configuration to + * contain the profile is a global config or a project config. + * + * Currently, we only form a different global/project profile name for + * a base profile. The profile name for any other profile type is currently + * set to the profile type string. + * + * @param profileType + * The profile type for which we will form a name. + * + * @param globalConfig + * Indicator that the caller knows that the profile name will be + * for a globalConfig (true) or project config (false). + * If globalConfig is not supplied, we interrogate any existing + * Config object to determine whether to form a global or project + * profile name. + * + * @returns + * A string to be used as the profile name for the specified profile type. + */ + public static formGlobOrProjProfileNm(profileType: string, globalConfig: boolean = null): string { + if (profileType !== "base") { + // everything except base profiles use profile type as the profile name + return profileType; + } + + // were we told that this is for a global or project config? + if (globalConfig === true) { + return `global_${profileType}`; + + } else if (globalConfig === false) { + return `project_${profileType}`; + + } else { + // determine from existing config whether the profile is intended for a project config + const existingConfig = ImperativeConfig.instance.config; + for (const nextLayer of existingConfig.layers) { + // if desired profile type exists in the project layer, it wins + if (nextLayer.global === false) { + if (ConfigUtils.findProfTypeInNestedProfiles(profileType, existingConfig.layerProfiles(nextLayer))) { + return `project_${profileType}`; + } + } + } + } + // since we did not find the profile type at the project layers, return a global name + return `global_${profileType}`; + } + + // _______________________________________________________________________ + /** + * Find the specified profile type in the specified (or nested) profiles. + * + * @param profileType + * The profile type to search for. + * + * @param profilesObj + * The profile object in which we should search. + * + * @returns + * True if we find the profile type. False otherwise. + */ + private static findProfTypeInNestedProfiles( + profileType: string, + profilesObj: { [key: string]: IConfigProfile } + ): boolean { + for (const nextProfileObj of Object.values(profilesObj)) { + if (nextProfileObj?.type === profileType) { + return true; + } + // The specified type was not in nextProfileObj. Recursively look in its nested profiles. + if (nextProfileObj?.profiles) { + if (ConfigUtils.findProfTypeInNestedProfiles(profileType, nextProfileObj.profiles)) { + return true; + } + } + } + return false; + } } diff --git a/packages/imperative/src/config/src/ProfileInfo.ts b/packages/imperative/src/config/src/ProfileInfo.ts index 1230df3706..54dcc32713 100644 --- a/packages/imperative/src/config/src/ProfileInfo.ts +++ b/packages/imperative/src/config/src/ProfileInfo.ts @@ -185,25 +185,29 @@ export class ProfileInfo { */ public async updateProperty(options: IProfInfoUpdatePropOpts): Promise { this.ensureReadFromDisk(); - const desiredProfile = this.getAllProfiles(options.profileType).find(v => v.profName === options.profileName); - if (desiredProfile == null) { + const desiredProfile = options.profileType != null ? + this.getAllProfiles(options.profileType).find(v => v.profName === options.profileName) : null; + let updated = false; + if (desiredProfile != null) { + const mergedArgs = this.mergeArgsForProfile(desiredProfile, { getSecureVals: false }); + if (options.forceUpdate) { + const knownProperty = mergedArgs.knownArgs.find(v => v.argName === options.property); + if (knownProperty != null) { + const profPath = this.getTeamConfig().api.profiles.getProfilePathFromName(options.profileName); + if (!ConfigUtils.jsonPathMatches(knownProperty.argLoc.jsonLoc, profPath)) { + knownProperty.argLoc.jsonLoc = `${profPath}.properties.${options.property}`; + } + } + } + updated = await this.updateKnownProperty({ ...options, mergedArgs, osLocInfo: this.getOsLocInfo(desiredProfile)?.[0] }); + } else if (!(options.profileType == null && (options.forceUpdate || this.getTeamConfig().api.profiles.exists(options.profileName)))) { throw new ProfInfoErr({ errorCode: ProfInfoErr.PROF_NOT_FOUND, msg: `Failed to find profile ${options.profileName} of type ${options.profileType}` }); } - const mergedArgs = this.mergeArgsForProfile(desiredProfile, { getSecureVals: false }); - if (options.forceUpdate) { - const knownProperty = mergedArgs.knownArgs.find(v => v.argName === options.property); - if (knownProperty != null) { - const profPath = this.getTeamConfig().api.profiles.getProfilePathFromName(options.profileName); - if (!ConfigUtils.jsonPathMatches(knownProperty.argLoc.jsonLoc, profPath)) { - knownProperty.argLoc.jsonLoc = `${profPath}.properties.${options.property}`; - } - } - } - if (!await this.updateKnownProperty({ ...options, mergedArgs, osLocInfo: this.getOsLocInfo(desiredProfile)?.[0] })) { + if (!updated) { // Check to see if loadedConfig already contains the schema for the specified profile type if (ImperativeConfig.instance.loadedConfig?.profiles?.find(p => p.type === options.profileType)?.schema == null || ImperativeConfig.instance.loadedConfig?.baseProfile?.schema == null) { diff --git a/packages/imperative/src/config/src/doc/IProfInfoUpdatePropOpts.ts b/packages/imperative/src/config/src/doc/IProfInfoUpdatePropOpts.ts index 17f3f7f332..248843bc6d 100644 --- a/packages/imperative/src/config/src/doc/IProfInfoUpdatePropOpts.ts +++ b/packages/imperative/src/config/src/doc/IProfInfoUpdatePropOpts.ts @@ -18,9 +18,10 @@ import { IProfMergedArg } from "./IProfMergedArg"; */ export interface IProfInfoUpdatePropOpts extends IProfInfoUpdatePropCommonOpts { /** - * Type of the active profile + * Type of the active profile. + * Specify `null` to update a typeless profile. */ - profileType: string; + profileType: string | null; /** * Name of the active profile @@ -28,7 +29,7 @@ export interface IProfInfoUpdatePropOpts extends IProfInfoUpdatePropCommonOpts { profileName: string; /** - * Force the update to the profile specified even if the property comes from somehwere else + * Force the update to the profile specified even if the property comes from somewhere else * @example Token Value could be in the base profile (not in the service profile specified) * and the programmer has the intention of storing the token in the service profile * @default false When the property is not specified, the updateProperty method follows current diff --git a/packages/imperative/src/imperative/__tests__/config/cmd/import/import.handler.unit.test.ts b/packages/imperative/src/imperative/__tests__/config/cmd/import/import.handler.unit.test.ts index 671bab25e8..2d4af8c553 100644 --- a/packages/imperative/src/imperative/__tests__/config/cmd/import/import.handler.unit.test.ts +++ b/packages/imperative/src/imperative/__tests__/config/cmd/import/import.handler.unit.test.ts @@ -19,13 +19,13 @@ import { IHandlerParameters } from "../../../../../cmd"; import { Config, ConfigConstants, IConfig } from "../../../../../config"; import { ISession, RestClient } from "../../../../../rest"; import { ImperativeConfig } from "../../../../.."; -import { expectedConfigObject, expectedSchemaObject } from +import { expectedProjectConfigObject, expectedSchemaObject } from "../../../../../../__tests__/__integration__/imperative/__tests__/__integration__/cli/config/__resources__/expectedObjects"; jest.mock("fs"); -const expectedConfigText = JSONC.stringify(expectedConfigObject, null, ConfigConstants.INDENT); -const expectedConfigObjectWithoutSchema = lodash.omit(expectedConfigObject, "$schema"); +const expectedConfigText = JSONC.stringify(expectedProjectConfigObject, null, ConfigConstants.INDENT); +const expectedConfigObjectWithoutSchema = lodash.omit(expectedProjectConfigObject, "$schema"); const expectedConfigTextWithoutSchema = JSONC.stringify(expectedConfigObjectWithoutSchema, null, ConfigConstants.INDENT); const expectedSchemaText = JSONC.stringify(expectedSchemaObject, null, ConfigConstants.INDENT); @@ -134,7 +134,7 @@ describe("Configuration Import command handler", () => { it("should import config with schema from web address", async () => { jest.spyOn(fs, "existsSync").mockReturnValueOnce(false); writeFileSyncSpy.mockReturnValueOnce(); - fetchConfigSpy.mockResolvedValueOnce(expectedConfigObject); + fetchConfigSpy.mockResolvedValueOnce(expectedProjectConfigObject); const params: IHandlerParameters = getIHandlerParametersObject(); params.arguments.location = "http://example.com/downloads/fakeapp.config.json"; @@ -199,7 +199,7 @@ describe("Configuration Import command handler", () => { expect(config.profiles).toBeDefined(); expect(config.defaults).toBeDefined(); - expect(config).toMatchObject(expectedConfigObject); + expect(config).toMatchObject(expectedProjectConfigObject); }); it("should throw error when config file is not valid JSON", async () => { diff --git a/packages/imperative/src/imperative/__tests__/config/cmd/init/init.handler.unit.test.ts b/packages/imperative/src/imperative/__tests__/config/cmd/init/init.handler.unit.test.ts index 30cdada61e..5a337c38ec 100644 --- a/packages/imperative/src/imperative/__tests__/config/cmd/init/init.handler.unit.test.ts +++ b/packages/imperative/src/imperative/__tests__/config/cmd/init/init.handler.unit.test.ts @@ -55,7 +55,10 @@ const getIHandlerParametersObject = (): IHandlerParameters => { }; const fakeConfig = config as IImperativeConfig; -fakeConfig.profiles.push(fakeConfig.baseProfile); // Add base profile to profiles array to mimic Imperative init +if (fakeConfig.profiles && fakeConfig.baseProfile) { + // Add base profile to profiles array to mimic Imperative init + fakeConfig.profiles.push(fakeConfig.baseProfile); +} const fakeProjPath = path.join(__dirname, "fakeapp.config.json"); const fakeSchemaPath = path.join(__dirname, "fakeapp.schema.json"); const fakeProjUserPath = path.join(__dirname, "fakeapp.config.user.json"); @@ -118,6 +121,13 @@ describe("Configuration Initialization command handler", () => { // Run tests for all the config layers testLayers.forEach(({ name, user, global, configPath, schemaPath }) => describe(`${name} layer`, () => { + let baseProfName: string; + if (global) { + baseProfName = "global_base"; + } else { + baseProfName = "project_base"; + } + it("should attempt to initialize the configuration", async () => { const handler = new InitHandler(); const params = getIHandlerParametersObject(); @@ -140,12 +150,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null); + if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - if (!user) delete compObj.profiles.base.properties.secret; // Delete the secret + if (!user) delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(ensureCredMgrSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledTimes(1); @@ -162,7 +172,7 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(2, configPath, JSON.stringify(compObj, null, ConfigConstants.INDENT)); // Secure value supplied during prompting should be on properties - if (!user) expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual("fakeValue"); + if (!user) expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual("fakeValue"); }); it("should attempt to do a dry run of initializing the configuration", async () => { @@ -191,7 +201,7 @@ describe("Configuration Initialization command handler", () => { // initForDryRun const initForDryRunSpy = jest.spyOn(handler as any, "initForDryRun"); - if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null); + if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); expect(ensureCredMgrSpy).toHaveBeenCalledTimes(1); @@ -199,7 +209,9 @@ describe("Configuration Initialization command handler", () => { expect(readPromptSpy).toHaveBeenCalledTimes(0); // Dry run mode - no prompting should occur expect(initForDryRunSpy).toHaveBeenCalledTimes(1); - expect(initForDryRunSpy).toHaveBeenCalledWith(ImperativeConfig.instance.config, params.arguments.userConfig); + expect(initForDryRunSpy).toHaveBeenCalledWith(ImperativeConfig.instance.config, + params.arguments.userConfig, params.arguments.globalConfig + ); expect(writeFileSyncSpy).not.toHaveBeenCalled(); }); @@ -229,12 +241,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null); + if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - if (!user) delete compObj.profiles.base.properties.secret; // Delete the secret + if (!user) delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(ensureCredMgrSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledTimes(1); @@ -247,12 +259,12 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenCalledTimes(2); // Secure value supplied during prompting should be on properties - if (!user) expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual("fakeValue"); + if (!user) expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual("fakeValue"); // initWithSchema called with the correct parameters expect(initWithSchemaSpy).toHaveBeenCalledTimes(1); expect(initWithSchemaSpy).toHaveBeenCalledWith(ImperativeConfig.instance.config, params.arguments.userConfig, - params.arguments.overwrite && params.arguments.forSure); + params.arguments.globalConfig, params.arguments.overwrite && params.arguments.forSure); }); it("should attempt to initialize the configuration with prompting disabled", async () => { @@ -277,7 +289,7 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null); + if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first @@ -319,7 +331,7 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null); + if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first @@ -335,7 +347,7 @@ describe("Configuration Initialization command handler", () => { // initWithSchema called with the correct parameters expect(initWithSchemaSpy).toHaveBeenCalledTimes(1); expect(initWithSchemaSpy).toHaveBeenCalledWith(ImperativeConfig.instance.config, params.arguments.userConfig, - params.arguments.overwrite && params.arguments.forSure); + params.arguments.globalConfig, params.arguments.overwrite && params.arguments.forSure); }); it("should attempt to do a dry run of initializing the configuration and handle no changes", async () => { @@ -358,7 +370,7 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null); + if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first @@ -370,11 +382,13 @@ describe("Configuration Initialization command handler", () => { const jsonDataSpy = jest.spyOn(params.response.data, "setObj"); params.arguments.dryRun = true; - if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null); + if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); expect(initForDryRunSpy).toHaveBeenCalledTimes(1); - expect(initForDryRunSpy).toHaveBeenCalledWith(ImperativeConfig.instance.config, params.arguments.userConfig); + expect(initForDryRunSpy).toHaveBeenCalledWith(ImperativeConfig.instance.config, + params.arguments.userConfig, params.arguments.globalConfig + ); expect(jsonDataSpy).toHaveBeenCalledTimes(1); // console.log(jsonDataSpy.mock.calls[0][0]); @@ -404,7 +418,7 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null); + if (!global) jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); expect(ensureCredMgrSpy).toHaveBeenCalledTimes(1); @@ -413,7 +427,7 @@ describe("Configuration Initialization command handler", () => { expect(editFileSpy).toHaveBeenCalledWith(ImperativeConfig.instance.config.layerActive().path, undefined); expect(writeFileSyncSpy).toHaveBeenCalledTimes(2); - if (!user) expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual("fakeValue"); + if (!user) expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual("fakeValue"); }); })); @@ -422,6 +436,7 @@ describe("Configuration Initialization command handler", () => { const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = false; + const baseProfName = "project_base"; existsSyncSpy.mockReturnValue(false); // No files exist searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return @@ -438,12 +453,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(setSchemaSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledWith(expectedSchemaObject); @@ -458,7 +473,7 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(2, fakeProjPath, JSON.stringify(compObj, null, ConfigConstants.INDENT)); // Secure value supplied during prompting should be on properties - expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual(true); + expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual(true); }); it("should attempt to initialize the project configuration and use boolean false for the prompt", async () => { @@ -466,6 +481,7 @@ describe("Configuration Initialization command handler", () => { const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = false; + const baseProfName = "project_base"; existsSyncSpy.mockReturnValue(false); // No files exist searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return @@ -482,12 +498,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(setSchemaSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledWith(expectedSchemaObject); @@ -502,7 +518,7 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(2, fakeProjPath, JSON.stringify(compObj, null, ConfigConstants.INDENT)); // Secure value supplied during prompting should be on properties - expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual(false); + expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual(false); }); it("should attempt to initialize the project configuration and use a number for the prompt", async () => { @@ -510,6 +526,7 @@ describe("Configuration Initialization command handler", () => { const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = false; + const baseProfName = "project_base"; existsSyncSpy.mockReturnValue(false); // No files exist searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return @@ -528,12 +545,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(setSchemaSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledWith(expectedSchemaObject); @@ -548,7 +565,7 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(2, fakeProjPath, JSON.stringify(compObj, null, ConfigConstants.INDENT)); // Secure value supplied during prompting should be on properties - expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual(randomValueNumber); + expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual(randomValueNumber); }); it("should attempt to initialize the project configuration and handle getting nothing from the prompt", async () => { @@ -556,6 +573,7 @@ describe("Configuration Initialization command handler", () => { const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = false; + const baseProfName = "project_base"; existsSyncSpy.mockReturnValue(false); // No files exist searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return @@ -572,12 +590,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(setSchemaSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledWith(expectedSchemaObject); @@ -592,7 +610,7 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(2, fakeProjPath, JSON.stringify(compObj, null, ConfigConstants.INDENT)); // Secure value supplied during prompting should be on properties - expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual(undefined); + expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual(undefined); }); it("should attempt to initialize the project configuration and overwrite empty value with prompt", async () => { @@ -600,12 +618,13 @@ describe("Configuration Initialization command handler", () => { const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = false; + const baseProfName = "project_base"; existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); // Project config exists searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return jest.spyOn(fs, "readFileSync").mockReturnValueOnce(JSON.stringify({ profiles: { - base: { + project_base: { properties: { secret: "" } @@ -626,12 +645,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(setSchemaSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledWith(expectedSchemaObject); @@ -646,7 +665,7 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(2, fakeProjPath, JSON.stringify(compObj, null, ConfigConstants.INDENT)); // Secure value supplied during prompting should be on properties - expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual("area51"); + expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual("area51"); }); it("should attempt to initialize the project configuration and overwrite non-empty value with prompt", async () => { @@ -654,12 +673,13 @@ describe("Configuration Initialization command handler", () => { const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = false; + const baseProfName = "project_base"; existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); // Project config exists searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return jest.spyOn(fs, "readFileSync").mockReturnValueOnce(JSON.stringify({ profiles: { - base: { + project_base: { properties: { secret: "expired" } @@ -680,12 +700,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(setSchemaSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledWith(expectedSchemaObject); @@ -700,7 +720,7 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(2, fakeProjPath, JSON.stringify(compObj, null, ConfigConstants.INDENT)); // Secure value supplied during prompting should be on properties - expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual("area51"); + expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual("area51"); }); it("should attempt to initialize the project configuration and not overwrite value when prompt is skipped", async () => { @@ -708,12 +728,13 @@ describe("Configuration Initialization command handler", () => { const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = false; + const baseProfName = "project_base"; existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); // Project config exists searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return jest.spyOn(fs, "readFileSync").mockReturnValueOnce(JSON.stringify({ profiles: { - base: { + project_base: { properties: { info: "fakeValue", secret: "area51" @@ -736,12 +757,12 @@ describe("Configuration Initialization command handler", () => { (params.response.console as any).prompt = readPromptSpy; writeFileSyncSpy.mockImplementation(); // Don't actually write files - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(setSchemaSpy).toHaveBeenCalledTimes(1); expect(setSchemaSpy).toHaveBeenCalledWith(expectedSchemaObject); @@ -756,8 +777,8 @@ describe("Configuration Initialization command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(2, fakeProjPath, JSON.stringify(compObj, null, ConfigConstants.INDENT)); // Secure value supplied during prompting should be on properties - expect(ImperativeConfig.instance.config.properties.profiles.base.properties.info).toEqual("fakeValue"); - expect(ImperativeConfig.instance.config.properties.profiles.base.properties.secret).toEqual("area51"); + expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.info).toEqual("fakeValue"); + expect(ImperativeConfig.instance.config.properties.profiles[baseProfName].properties.secret).toEqual("area51"); }); it("should display warning if unable to securely save credentials", async () => { @@ -765,6 +786,7 @@ describe("Configuration Initialization command handler", () => { const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = false; + const baseProfName = "project_base"; existsSyncSpy.mockReturnValue(false); // No files exist searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return @@ -783,12 +805,12 @@ describe("Configuration Initialization command handler", () => { jest.spyOn(CredentialManagerFactory, "initialized", "get").mockReturnValue(false); jest.spyOn(CredentialManagerFactory, "manager", "get").mockReturnValue({ secureErrorDetails: jest.fn() } as any); - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); await handler.process(params as IHandlerParameters); const compObj: any = { $schema: "./fakeapp.schema.json" }; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles[baseProfName].properties.secret; // Delete the secret expect(readPromptSpy).toHaveBeenCalledTimes(0); expect(writeFileSyncSpy).toHaveBeenCalledTimes(2); @@ -819,7 +841,7 @@ describe("Configuration Initialization command handler", () => { jest.spyOn(CredentialManagerFactory, "initialized", "get").mockReturnValue(false); jest.spyOn(CredentialManagerFactory, "manager", "get").mockReturnValue({ secureErrorDetails: jest.fn() } as any); - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); // Mocking for logical branch intended to evaluate const secureSaveErrorSpy = jest.spyOn(ConfigUtils, "secureSaveError"); @@ -861,7 +883,7 @@ describe("Configuration Initialization command handler", () => { jest.spyOn(CredentialManagerFactory, "initialized", "get").mockReturnValue(false); jest.spyOn(CredentialManagerFactory, "manager", "get").mockReturnValue({ secureErrorDetails: jest.fn() } as any); - jest.spyOn(process, "cwd").mockReturnValueOnce(null); + jest.spyOn(process, "cwd").mockReturnValueOnce(null as unknown as string); // Mocking for logical branch intended to evaluate const secureSaveErrorSpy = jest.spyOn(ConfigUtils, "secureSaveError"); 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 a2926aec91..c3194cc9c6 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 @@ -16,7 +16,7 @@ import { ImperativeConfig } from "../../../../../utilities"; import { IImperativeConfig } from "../../../../src/doc/IImperativeConfig"; import { ICredentialManagerInit } from "../../../../../security/src/doc/ICredentialManagerInit"; import { CredentialManagerFactory } from "../../../../../security"; -import { expectedConfigObject } from +import { expectedGlobalConfigObject, expectedGlobalUserConfigObject, expectedProjectConfigObject, expectedProjectUserConfigObject } from "../../../../../../__tests__/__integration__/imperative/__tests__/__integration__/cli/config/__resources__/expectedObjects"; import SecureHandler from "../../../../src/config/cmd/secure/secure.handler"; import * as config from "../../../../../../__tests__/__integration__/imperative/src/imperative"; @@ -74,9 +74,9 @@ const fakeGblProjUserPath = path.join(__dirname, ".fakeapp", "fakeapp.config.use const fakeUnrelatedPath = path.join(__dirname, "fakeapp.unrelated.config.json"); const fakeSecureDataJson: any = {}; -fakeSecureDataJson[fakeProjPath] = {"profiles.base.properties.secure": "fakeSecureValue"}; -fakeSecureDataJson[fakeGblProjPath] = {"profiles.base.properties.secure": "fakeSecureValue"}; -fakeSecureDataJson[fakeUnrelatedPath] = {"profiles.base.properties.secure": "anotherFakeSecureValue"}; +fakeSecureDataJson[fakeProjPath] = {"profiles.project_base.properties.secure": "fakeSecureValue"}; +fakeSecureDataJson[fakeGblProjPath] = {"profiles.global_base.properties.secure": "fakeSecureValue"}; +fakeSecureDataJson[fakeUnrelatedPath] = {"profiles.project_base.properties.secure": "anotherFakeSecureValue"}; const fakeSecureData = Buffer.from(JSON.stringify(fakeSecureDataJson)).toString("base64"); @@ -149,7 +149,7 @@ describe("Configuration Secure command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -173,7 +173,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.secret": "fakePromptingData" + "profiles.project_base.properties.secret": "fakePromptingData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -181,7 +181,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.project_base.properties.secret; // Delete the secret if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(4); @@ -195,7 +195,7 @@ describe("Configuration Secure command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeProjPath, JSON.stringify(compObj, null, 4)); // Config }); - it("should attempt to secure the user configuration", async () => { + it("should attempt to secure the project user configuration", async () => { const handler = new SecureHandler(); const params = getIHandlerParametersObject(); @@ -211,7 +211,7 @@ describe("Configuration Secure command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectUserConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -233,7 +233,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); fakeSecureDataExpectedJson[fakeProjUserPath] = { - "profiles.base.properties.secret": "fakePromptingData" + "profiles.project_base.properties.secret": "fakePromptingData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -241,7 +241,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.project_base.properties.secret; // Delete the secret if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(4); @@ -255,7 +255,7 @@ describe("Configuration Secure command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeProjUserPath, JSON.stringify(compObj, null, 4)); // Config }); - it("should attempt to secure the global project configuration", async () => { + it("should attempt to secure the global configuration", async () => { const handler = new SecureHandler(); const params = getIHandlerParametersObject(); @@ -271,14 +271,14 @@ describe("Configuration Secure command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedGlobalConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(false).mockReturnValueOnce(false) - .mockReturnValueOnce(true).mockReturnValue(false); // Only the global project config exists + .mockReturnValueOnce(true).mockReturnValue(false); // Only the global config exists writeFileSyncSpy.mockImplementation(); - searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return + searchSpy.mockReturnValueOnce(fakeGblProjUserPath).mockReturnValueOnce(fakeGblProjPath); // Give search something to return await setupConfigToLoad(undefined, configOpts); // Setup the config @@ -295,7 +295,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeGblProjPath]; fakeSecureDataExpectedJson[fakeGblProjPath] = { - "profiles.base.properties.secret": "fakePromptingData" + "profiles.global_base.properties.secret": "fakePromptingData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -303,7 +303,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.global_base.properties.secret; // Delete the secret if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(4); @@ -333,7 +333,7 @@ describe("Configuration Secure command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedGlobalUserConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -357,7 +357,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeGblProjUserPath]; fakeSecureDataExpectedJson[fakeGblProjUserPath] = { - "profiles.base.properties.secret": "fakePromptingData" + "profiles.global_base.properties.secret": "fakePromptingData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -365,7 +365,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.global_base.properties.secret; // Delete the secret if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(4); @@ -395,7 +395,7 @@ describe("Configuration Secure command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -440,7 +440,7 @@ describe("Configuration Secure command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -462,7 +462,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = { [fakeProjPath]: { - "profiles.base.properties.secret": "fakePromptingData" + "profiles.project_base.properties.secret": "fakePromptingData" } }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -471,7 +471,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.project_base.properties.secret; // Delete the secret if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(8); @@ -498,13 +498,13 @@ describe("Configuration Secure command handler", () => { ] }; - const expectedConfigObjectWithToken: IConfig = { + const expectedProjConfigObjectWithToken: IConfig = { $schema: "./fakeapp.schema.json", profiles: { - base: baseProfile, + project_base: baseProfile, }, defaults: { - base: "base" + base: "project_base" } }; @@ -555,7 +555,7 @@ describe("Configuration Secure command handler", () => { }); it("should invoke auth handler to obtain token and store it securely", async () => { - const eco = lodash.cloneDeep(expectedConfigObjectWithToken); + const eco = lodash.cloneDeep(expectedProjConfigObjectWithToken); // Create another base profile and mock the loggers to test multiple login operations in a single config-secure eco.profiles["base2"] = baseProfile; @@ -583,7 +583,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.tokenValue": "fakeLoginData", + "profiles.project_base.properties.tokenValue": "fakeLoginData", "profiles.base2.properties.tokenValue": "fakeLoginData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -592,7 +592,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.tokenValue; // Delete the secret + delete compObj.profiles.project_base.properties.tokenValue; // Delete the secret delete compObj.profiles.base2.properties.tokenValue; // Delete the secret expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(process.platform === "win32" ? 4 : 3); @@ -606,8 +606,8 @@ describe("Configuration Secure command handler", () => { }); it("should not invoke auth handler if profile type is undefined", async () => { - const eco = lodash.cloneDeep(expectedConfigObjectWithToken); - delete eco.profiles.base.type; + const eco = lodash.cloneDeep(expectedProjConfigObjectWithToken); + delete eco.profiles.project_base.type; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); // Only the project config exists @@ -620,7 +620,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.tokenValue": "fakePromptingData" + "profiles.project_base.properties.tokenValue": "fakePromptingData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -628,7 +628,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.tokenValue; // Delete the secret + delete compObj.profiles.project_base.properties.tokenValue; // Delete the secret expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(process.platform === "win32" ? 4 : 3); expect(keytarGetPasswordSpy).toHaveBeenCalledTimes(1); @@ -640,8 +640,8 @@ describe("Configuration Secure command handler", () => { }); it("should not invoke auth handler if profile token type is undefined", async () => { - const eco = lodash.cloneDeep(expectedConfigObjectWithToken); - delete eco.profiles.base.properties.tokenType; + const eco = lodash.cloneDeep(expectedProjConfigObjectWithToken); + delete eco.profiles.project_base.properties.tokenType; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); // Only the project config exists @@ -654,7 +654,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.tokenValue": "fakePromptingData" + "profiles.project_base.properties.tokenValue": "fakePromptingData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -662,7 +662,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.tokenValue; // Delete the secret + delete compObj.profiles.project_base.properties.tokenValue; // Delete the secret expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(process.platform === "win32" ? 4 : 3); expect(keytarGetPasswordSpy).toHaveBeenCalledTimes(1); @@ -675,7 +675,7 @@ describe("Configuration Secure command handler", () => { }); it("should not invoke auth handler if no matching auth config is found", async () => { - const eco = lodash.cloneDeep(expectedConfigObjectWithToken); + const eco = lodash.cloneDeep(expectedProjConfigObjectWithToken); readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); // Only the project config exists @@ -696,7 +696,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.tokenValue": "fakePromptingData" + "profiles.project_base.properties.tokenValue": "fakePromptingData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -704,7 +704,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.tokenValue; // Delete the secret + delete compObj.profiles.project_base.properties.tokenValue; // Delete the secret expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(process.platform === "win32" ? 4 : 3); expect(keytarGetPasswordSpy).toHaveBeenCalledTimes(1); @@ -717,7 +717,7 @@ describe("Configuration Secure command handler", () => { }); it("should not invoke auth handler if auth handler is for different token type", async () => { - const eco = lodash.cloneDeep(expectedConfigObjectWithToken); + const eco = lodash.cloneDeep(expectedProjConfigObjectWithToken); readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); // Only the project config exists @@ -740,7 +740,7 @@ describe("Configuration Secure command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.tokenValue": "fakePromptingData" + "profiles.project_base.properties.tokenValue": "fakePromptingData" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -748,7 +748,7 @@ describe("Configuration Secure command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.tokenValue; // Delete the secret + delete compObj.profiles.project_base.properties.tokenValue; // Delete the secret expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(process.platform === "win32" ? 4 : 3); expect(keytarGetPasswordSpy).toHaveBeenCalledTimes(1); @@ -761,7 +761,7 @@ describe("Configuration Secure command handler", () => { }); it("should fail to invoke auth handler if it throws an error", async () => { - const eco = lodash.cloneDeep(expectedConfigObjectWithToken); + const eco = lodash.cloneDeep(expectedProjConfigObjectWithToken); readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(true).mockReturnValue(false); // Only the project config exists diff --git a/packages/imperative/src/imperative/__tests__/config/cmd/set/set.handler.unit.test.ts b/packages/imperative/src/imperative/__tests__/config/cmd/set/set.handler.unit.test.ts index 272c140435..3185c489a6 100644 --- a/packages/imperative/src/imperative/__tests__/config/cmd/set/set.handler.unit.test.ts +++ b/packages/imperative/src/imperative/__tests__/config/cmd/set/set.handler.unit.test.ts @@ -16,7 +16,7 @@ import { ImperativeConfig } from "../../../../../utilities"; import { IImperativeConfig } from "../../../../src/doc/IImperativeConfig"; import { ICredentialManagerInit } from "../../../../../security/src/doc/ICredentialManagerInit"; import { CredentialManagerFactory } from "../../../../../security"; -import { expectedConfigObject, expectedUserConfigObject } from +import { expectedGlobalConfigObject, expectedGlobalUserConfigObject, expectedProjectConfigObject, expectedProjectUserConfigObject } from "../../../../../../__tests__/__integration__/imperative/__tests__/__integration__/cli/config/__resources__/expectedObjects"; import SetHandler from "../../../../src/config/cmd/set/set.handler"; import * as config from "../../../../../../__tests__/__integration__/imperative/src/imperative"; @@ -61,16 +61,14 @@ const credentialManager: ICredentialManagerInit = { const fakeConfig = config as IImperativeConfig; const fakeProjPath = path.join(__dirname, "fakeapp.config.json"); -const fakeSchemaPath = path.join(__dirname, "fakeapp.schema.json"); const fakeProjUserPath = path.join(__dirname, "fakeapp.config.user.json"); -const fakeGblProjPath = path.join(__dirname, ".fakeapp", "fakeapp.config.json"); -const fakeGblSchemaPath = path.join(__dirname, ".fakeapp", "fakeapp.schema.json"); -const fakeGblProjUserPath = path.join(__dirname, ".fakeapp", "fakeapp.config.user.json"); +const fakeGlobalPath = path.join(__dirname, ".fakeapp", "fakeapp.config.json"); +const fakeGlobalUserPath = path.join(__dirname, ".fakeapp", "fakeapp.config.user.json"); const fakeUnrelatedPath = path.join(__dirname, "anotherapp.config.json"); const fakeSecureDataJson: any = {}; -fakeSecureDataJson[fakeProjPath] = {"profiles.base.properties.secret": "fakeSecureValue"}; -fakeSecureDataJson[fakeGblProjPath] = {"profiles.base.properties.secret": "fakeSecureValue"}; +fakeSecureDataJson[fakeProjPath] = {"profiles.project_base.properties.secret": "fakeSecureValue"}; +fakeSecureDataJson[fakeGlobalPath] = {"profiles.global_base.properties.secret": "fakeSecureValue"}; const fakeSecureData = Buffer.from(JSON.stringify(fakeSecureDataJson)).toString("base64"); @@ -144,7 +142,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -168,7 +166,7 @@ describe("Configuration Set command handler", () => { delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { "profiles.secured.properties.testProperty": "aSecuredTestProperty", - "profiles.base.properties.secret": "fakeSecureValue" + "profiles.project_base.properties.secret": "fakeSecureValue" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -176,7 +174,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.project_base.properties.secret; // Delete the secret delete compObj.profiles.secured.properties.testProperty; // Delete the new secret if (process.platform === "win32") { @@ -191,7 +189,7 @@ describe("Configuration Set command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeProjPath, JSON.stringify(compObj, null, 4)); // Config }); - it("should secure a property and add it to the user configuration", async () => { + it("should secure a property and add it to the project user configuration", async () => { const handler = new SetHandler(); const params = getIHandlerParametersObject(); @@ -210,7 +208,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedUserConfigObject); + const eco = lodash.cloneDeep(expectedProjectUserConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -240,7 +238,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.project_base.properties.secret; // Delete the secret delete compObj.profiles.secured.properties.testProperty; // Delete the new secret if (process.platform === "win32") { @@ -255,7 +253,7 @@ describe("Configuration Set command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeProjUserPath, JSON.stringify(compObj, null, 4)); // Config }); - it("should secure a property and add it to the global project configuration", async () => { + it("should secure a property and add it to the global configuration", async () => { const handler = new SetHandler(); const params = getIHandlerParametersObject(); @@ -274,14 +272,14 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedGlobalConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(false).mockReturnValueOnce(false) .mockReturnValueOnce(true).mockReturnValue(false); // Only the global project config exists writeFileSyncSpy.mockImplementation(); - searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return + searchSpy.mockReturnValueOnce(fakeGlobalUserPath).mockReturnValueOnce(fakeGlobalPath); // Give search something to return await setupConfigToLoad(undefined, configOpts); // Setup the config @@ -296,10 +294,10 @@ describe("Configuration Set command handler", () => { await handler.process(params); const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); - delete fakeSecureDataExpectedJson[fakeGblProjPath]; - fakeSecureDataExpectedJson[fakeGblProjPath] = { + delete fakeSecureDataExpectedJson[fakeGlobalPath]; + fakeSecureDataExpectedJson[fakeGlobalPath] = { "profiles.secured.properties.testProperty": "aSecuredTestProperty", - "profiles.base.properties.secret": "fakeSecureValue" + "profiles.global_base.properties.secret": "fakeSecureValue" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -307,7 +305,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.global_base.properties.secret; // Delete the secret delete compObj.profiles.secured.properties.testProperty; // Delete the new secret if (process.platform === "win32") { @@ -319,7 +317,7 @@ describe("Configuration Set command handler", () => { expect(keytarSetPasswordSpy).toHaveBeenCalledTimes(1); expect(keytarSetPasswordSpy).toHaveBeenCalledWith("Zowe", "secure_config_props", fakeSecureDataExpected); expect(writeFileSyncSpy).toHaveBeenCalledTimes(1); - expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGblProjPath, JSON.stringify(compObj, null, 4)); // Config + expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGlobalPath, JSON.stringify(compObj, null, 4)); // Config }); it("should secure a property and add it to the global user configuration", async () => { @@ -341,14 +339,14 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedUserConfigObject); + const eco = lodash.cloneDeep(expectedGlobalUserConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(false) .mockReturnValueOnce(true).mockReturnValue(false); // Only the global user project config exists writeFileSyncSpy.mockImplementation(); - searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return + searchSpy.mockReturnValueOnce(fakeGlobalUserPath).mockReturnValueOnce(fakeGlobalPath); // Give search something to return await setupConfigToLoad(undefined, configOpts); // Setup the config @@ -363,7 +361,7 @@ describe("Configuration Set command handler", () => { await handler.process(params); const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); - fakeSecureDataExpectedJson[fakeGblProjUserPath] = { + fakeSecureDataExpectedJson[fakeGlobalUserPath] = { "profiles.secured.properties.testProperty": "aSecuredTestProperty" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -372,7 +370,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; // Delete the secret + delete compObj.profiles.global_base.properties.secret; // Delete the secret delete compObj.profiles.secured.properties.testProperty; // Delete the new secret if (process.platform === "win32") { @@ -384,7 +382,7 @@ describe("Configuration Set command handler", () => { expect(keytarSetPasswordSpy).toHaveBeenCalledTimes(1); expect(keytarSetPasswordSpy).toHaveBeenCalledWith("Zowe", "secure_config_props", fakeSecureDataExpected); expect(writeFileSyncSpy).toHaveBeenCalledTimes(1); - expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGblProjUserPath, JSON.stringify(compObj, null, 4)); // Config + expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGlobalUserPath, JSON.stringify(compObj, null, 4)); // Config }); it("should allow you to define an insecure property and add it to the project configuration", async () => { @@ -394,7 +392,7 @@ describe("Configuration Set command handler", () => { params.arguments.userConfig = false; params.arguments.globalConfig = false; params.arguments.secure = false; - params.arguments.property = "profiles.base.properties.secret"; + params.arguments.property = "profiles.project_base.properties.secret"; params.arguments.value = "anUnsecuredTestProperty"; // Start doing fs mocks @@ -406,7 +404,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -447,14 +445,14 @@ describe("Configuration Set command handler", () => { expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeProjPath, JSON.stringify(compObj, null, 4)); // Config }); - it("should allow you to define an insecure property and add it to the user configuration", async () => { + it("should allow you to define an insecure property and add it to the project user configuration", async () => { const handler = new SetHandler(); const params = getIHandlerParametersObject(); params.arguments.userConfig = true; params.arguments.globalConfig = false; params.arguments.secure = false; - params.arguments.property = "profiles.base.properties.secret"; + params.arguments.property = "profiles.project_base.properties.secret"; params.arguments.value = "anUnsecuredTestProperty"; // Start doing fs mocks @@ -466,13 +464,13 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedUserConfigObject); + const eco = lodash.cloneDeep(expectedProjectUserConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(true).mockReturnValue(false); // Only the global user project config exists writeFileSyncSpy.mockImplementation(); - searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return + searchSpy.mockReturnValueOnce(fakeGlobalUserPath).mockReturnValueOnce(fakeGlobalPath); // Give search something to return await setupConfigToLoad(undefined, configOpts); // Setup the config @@ -503,17 +501,17 @@ describe("Configuration Set command handler", () => { expect(keytarSetPasswordSpy).toHaveBeenCalledTimes(1); expect(keytarSetPasswordSpy).toHaveBeenCalledWith("Zowe", "secure_config_props", fakeSecureDataExpected); expect(writeFileSyncSpy).toHaveBeenCalledTimes(1); - expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeProjUserPath, JSON.stringify(compObj, null, 4)); // Config + expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGlobalUserPath, JSON.stringify(compObj, null, 4)); // Config }); - it("should allow you to define an insecure property and add it to the global project configuration", async () => { + it("should allow you to define an insecure property and add it to the global configuration", async () => { const handler = new SetHandler(); const params = getIHandlerParametersObject(); params.arguments.userConfig = false; params.arguments.globalConfig = true; params.arguments.secure = false; - params.arguments.property = "profiles.base.properties.secret"; + params.arguments.property = "profiles.global_base.properties.secret"; params.arguments.value = "anUnsecuredTestProperty"; // Start doing fs mocks @@ -525,14 +523,14 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedGlobalConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(false).mockReturnValueOnce(false) .mockReturnValueOnce(true).mockReturnValue(false); // Only the global project config exists writeFileSyncSpy.mockImplementation(); - searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return + searchSpy.mockReturnValueOnce(fakeGlobalUserPath).mockReturnValueOnce(fakeGlobalPath); // Give search something to return await setupConfigToLoad(undefined, configOpts); // Setup the config @@ -547,7 +545,7 @@ describe("Configuration Set command handler", () => { await handler.process(params); const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); - delete fakeSecureDataExpectedJson[fakeGblProjPath]; + delete fakeSecureDataExpectedJson[fakeGlobalPath]; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); const compObj: any = {}; @@ -564,7 +562,7 @@ describe("Configuration Set command handler", () => { expect(keytarSetPasswordSpy).toHaveBeenCalledTimes(1); expect(keytarSetPasswordSpy).toHaveBeenCalledWith("Zowe", "secure_config_props", fakeSecureDataExpected); expect(writeFileSyncSpy).toHaveBeenCalledTimes(1); - expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGblProjPath, JSON.stringify(compObj, null, 4)); // Config + expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGlobalPath, JSON.stringify(compObj, null, 4)); // Config }); it("should allow you to define an insecure property and add it to the global user configuration", async () => { @@ -574,7 +572,7 @@ describe("Configuration Set command handler", () => { params.arguments.userConfig = true; params.arguments.globalConfig = true; params.arguments.secure = false; - params.arguments.property = "profiles.base.properties.secret"; + params.arguments.property = "profiles.global_base.properties.secret"; params.arguments.value = "anUnsecuredTestProperty"; // Start doing fs mocks @@ -586,14 +584,14 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedUserConfigObject); + const eco = lodash.cloneDeep(expectedGlobalUserConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); existsSyncSpy.mockReturnValueOnce(false).mockReturnValueOnce(false) .mockReturnValueOnce(true).mockReturnValue(false); // Only the global user project config exists writeFileSyncSpy.mockImplementation(); - searchSpy.mockReturnValueOnce(fakeProjUserPath).mockReturnValueOnce(fakeProjPath); // Give search something to return + searchSpy.mockReturnValueOnce(fakeGlobalUserPath).mockReturnValueOnce(fakeGlobalPath); // Give search something to return await setupConfigToLoad(undefined, configOpts); // Setup the config @@ -623,8 +621,8 @@ describe("Configuration Set command handler", () => { expect(keytarGetPasswordSpy).toHaveBeenCalledTimes(1); // No pre-existing secure values, only the combine expect(keytarSetPasswordSpy).toHaveBeenCalledTimes(1); expect(keytarSetPasswordSpy).toHaveBeenCalledWith("Zowe", "secure_config_props", fakeSecureDataExpected); - expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGblProjUserPath, JSON.stringify(compObj, null, 4)); // Config expect(writeFileSyncSpy).toHaveBeenCalledTimes(1); + expect(writeFileSyncSpy).toHaveBeenNthCalledWith(1, fakeGlobalUserPath, JSON.stringify(compObj, null, 4)); // Config }); it("should allow you to define an insecure property and add it to the project configuration while keeping other secure props", async () => { @@ -634,11 +632,11 @@ describe("Configuration Set command handler", () => { params.arguments.userConfig = false; params.arguments.globalConfig = false; params.arguments.secure = false; - params.arguments.property = "profiles.base.properties.secret"; + params.arguments.property = "profiles.project_base.properties.secret"; params.arguments.value = "anUnsecuredTestProperty"; const testKeystoreJson = lodash.cloneDeep(fakeSecureDataJson); - testKeystoreJson[fakeUnrelatedPath] = {"profiles.base.properties.secret": "anotherFakeSecureValue"}; + testKeystoreJson[fakeUnrelatedPath] = {"profiles.project_base.properties.secret": "anotherFakeSecureValue"}; const testKeystore = Buffer.from(JSON.stringify(testKeystoreJson)).toString("base64"); @@ -651,7 +649,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -673,7 +671,7 @@ describe("Configuration Set command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; - fakeSecureDataExpectedJson[fakeUnrelatedPath] = {"profiles.base.properties.secret": "anotherFakeSecureValue"}; + fakeSecureDataExpectedJson[fakeUnrelatedPath] = {"profiles.project_base.properties.secret": "anotherFakeSecureValue"}; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); const compObj: any = {}; @@ -715,7 +713,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -739,7 +737,7 @@ describe("Configuration Set command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.secret": "fakeSecureValue" + "profiles.project_base.properties.secret": "fakeSecureValue" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -747,7 +745,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; + delete compObj.profiles.project_base.properties.secret; if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(4); @@ -770,7 +768,7 @@ describe("Configuration Set command handler", () => { params.arguments.userConfig = false; params.arguments.globalConfig = false; params.arguments.secure = null; - params.arguments.property = "profiles.base.properties.secret"; + params.arguments.property = "profiles.project_base.properties.secret"; params.arguments.value = "aSecuredTestProperty"; // Start doing fs mocks @@ -782,7 +780,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -805,7 +803,7 @@ describe("Configuration Set command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.secret": "aSecuredTestProperty" + "profiles.project_base.properties.secret": "aSecuredTestProperty" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -813,7 +811,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; + delete compObj.profiles.project_base.properties.secret; if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(4); @@ -846,7 +844,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -870,7 +868,7 @@ describe("Configuration Set command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.secret": "fakeSecureValue" + "profiles.project_base.properties.secret": "fakeSecureValue" }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -878,7 +876,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; + delete compObj.profiles.project_base.properties.secret; if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(4); @@ -900,7 +898,7 @@ describe("Configuration Set command handler", () => { params.arguments.globalConfig = false; params.arguments.secure = true; params.arguments.json = true; - params.arguments.property = "profiles.base.properties.secret"; + params.arguments.property = "profiles.project_base.properties.secret"; params.arguments.value = '{"fakeProp":"fakeVal"}'; // Start doing fs mocks @@ -912,7 +910,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -935,7 +933,7 @@ describe("Configuration Set command handler", () => { const fakeSecureDataExpectedJson: { [key: string]: any} = lodash.cloneDeep(fakeSecureDataJson); delete fakeSecureDataExpectedJson[fakeProjPath]; fakeSecureDataExpectedJson[fakeProjPath] = { - "profiles.base.properties.secret": {"fakeProp": "fakeVal"} + "profiles.project_base.properties.secret": {"fakeProp": "fakeVal"} }; const fakeSecureDataExpected = Buffer.from(JSON.stringify(fakeSecureDataExpectedJson)).toString("base64"); @@ -944,7 +942,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; + delete compObj.profiles.project_base.properties.secret; if (process.platform === "win32") { expect(keytarDeletePasswordSpy).toHaveBeenCalledTimes(4); @@ -966,7 +964,7 @@ describe("Configuration Set command handler", () => { params.arguments.globalConfig = false; params.arguments.secure = true; params.arguments.json = true; - params.arguments.property = "profiles.base.properties.secret"; + params.arguments.property = "profiles.project_base.properties.secret"; params.arguments.value = '{"fakeProp"::"fakeVal"}'; // Start doing fs mocks @@ -978,7 +976,7 @@ describe("Configuration Set command handler", () => { writeFileSyncSpy = jest.spyOn(fs, "writeFileSync"); existsSyncSpy = jest.spyOn(fs, "existsSync"); - const eco = lodash.cloneDeep(expectedConfigObject); + const eco = lodash.cloneDeep(expectedProjectConfigObject); eco.$schema = "./fakeapp.schema.json"; readFileSyncSpy.mockReturnValueOnce(JSON.stringify(eco)); @@ -1007,7 +1005,7 @@ describe("Configuration Set command handler", () => { // Make changes to satisfy what would be stored on the JSON compObj.$schema = "./fakeapp.schema.json"; // Fill in the name of the schema file, and make it first lodash.merge(compObj, ImperativeConfig.instance.config.properties); // Add the properties from the config - delete compObj.profiles.base.properties.secret; + delete compObj.profiles.project_base.properties.secret; expect(error).toBeDefined(); expect(error.message).toContain("could not parse JSON value"); diff --git a/packages/imperative/src/imperative/src/config/cmd/init/init.handler.ts b/packages/imperative/src/imperative/src/config/cmd/init/init.handler.ts index f31a379de0..6370a4a3b0 100644 --- a/packages/imperative/src/imperative/src/config/cmd/init/init.handler.ts +++ b/packages/imperative/src/imperative/src/config/cmd/init/init.handler.ts @@ -47,13 +47,14 @@ export default class InitHandler implements ICommandHandler { // Load the config and set the active layer according to user options await OverridesLoader.ensureCredentialManagerLoaded(); const config = ImperativeConfig.instance.config; - const configDir = params.arguments.globalConfig ? null : process.cwd(); - config.api.layers.activate(params.arguments.userConfig, params.arguments.globalConfig, configDir); + const globalConfig: boolean = params.arguments?.globalConfig ? true : false; + const configDir = globalConfig ? null : process.cwd(); + config.api.layers.activate(params.arguments.userConfig, globalConfig, configDir); const layer = config.api.layers.get(); // Do a dry run if dryRun flag is present. Otherwise, initialize or overwrite the config if (params.arguments.dryRun && params.arguments.dryRun === true) { - let dryRun = await this.initForDryRun(config, params.arguments.userConfig); + let dryRun = await this.initForDryRun(config, params.arguments.userConfig, globalConfig); // Merge and display, do not save // Handle if the file doesn't actually exist @@ -100,14 +101,16 @@ export default class InitHandler implements ICommandHandler { params.response.console.log(jsonDiff); params.response.data.setObj(jsonDiff); } else { - await this.initWithSchema(config, params.arguments.userConfig, params.arguments.overwrite && params.arguments.forSure); + await this.initWithSchema(config, params.arguments.userConfig, globalConfig, params.arguments.overwrite && params.arguments.forSure); if (params.arguments.prompt !== false && config.api.secure.loadFailed && config.api.secure.secureFields().length > 0) { const warning = ConfigUtils.secureSaveError(); let message = "Warning:\n" + warning.message + " Skipped prompting for credentials."; + if (warning.additionalDetails) { message += `\n\n${warning.additionalDetails}\n`; } + params.response.console.log(TextUtils.chalk.yellow(message)); } @@ -126,8 +129,10 @@ export default class InitHandler implements ICommandHandler { * folder alongside the config. * @param config Config object to be populated * @param user If true, properties will be left empty for user config + * @param globalConfig Is the config to be a global config? + * @param overwrite Shall we overwrite an existing config? */ - private async initWithSchema(config: Config, user: boolean, overwrite: boolean): Promise { + private async initWithSchema(config: Config, user: boolean, globalConfig: boolean, overwrite: boolean): Promise { const opts: IConfigBuilderOpts = {}; if (!user) { opts.populateProperties = true; @@ -135,17 +140,18 @@ export default class InitHandler implements ICommandHandler { } // Build new config and merge with existing layer or overwrite it if overwrite & forSure options are present - const newConfig: IConfig = await ConfigBuilder.build(ImperativeConfig.instance.loadedConfig, opts); + const newConfig: IConfig = await ConfigBuilder.build(ImperativeConfig.instance.loadedConfig, globalConfig, opts); if (overwrite) { config.api.layers.set(newConfig); } else { const oldConfig = config.layerActive().properties; - if (oldConfig.profiles.base?.properties != null) { + const baseProfileNm: string = ConfigUtils.formGlobOrProjProfileNm("base", globalConfig); + if (oldConfig.profiles[baseProfileNm]?.properties != null) { // Remove values that should be overwritten from old base profile - for (const propName of Object.keys(oldConfig.profiles.base.properties)) { - const newPropValue = newConfig.profiles.base.properties[propName]; + for (const propName of Object.keys(oldConfig.profiles[baseProfileNm].properties)) { + const newPropValue = newConfig.profiles[baseProfileNm].properties[propName]; if (this.promptProps.includes(propName) && newPropValue != null && newPropValue !== "") { - delete oldConfig.profiles.base.properties[propName]; + delete oldConfig.profiles[baseProfileNm].properties[propName]; } } } @@ -161,15 +167,16 @@ export default class InitHandler implements ICommandHandler { * Also create a schema file in the same folder alongside the config. * @param config Config object to be populated * @param user If true, properties will be left empty for user config + * @param globalConfig Is the config to be a global config? */ - private async initForDryRun(config: Config, user: boolean): Promise { + private async initForDryRun(config: Config, user: boolean, globalConfig: boolean): Promise { const opts: IConfigBuilderOpts = {}; if (!user) { opts.populateProperties = true; } // Build new config and merge with existing layer - const newConfig: IConfig = await ConfigBuilder.build(ImperativeConfig.instance.loadedConfig, opts); + const newConfig: IConfig = await ConfigBuilder.build(ImperativeConfig.instance.loadedConfig, globalConfig, opts); return config.api.layers.merge(newConfig, true); } diff --git a/packages/imperative/src/imperative/src/plugins/PluginManagementFacility.ts b/packages/imperative/src/imperative/src/plugins/PluginManagementFacility.ts index 1936837395..58094e46a5 100644 --- a/packages/imperative/src/imperative/src/plugins/PluginManagementFacility.ts +++ b/packages/imperative/src/imperative/src/plugins/PluginManagementFacility.ts @@ -280,7 +280,7 @@ export class PluginManagementFacility { * We must find the plugin name from within our known credMgr overrides. */ const credMgrInfo = CredentialManagerOverride.getCredMgrInfoByDisplayName(overrideDispNm); - if ( credMgrInfo === null) { + if (credMgrInfo === null) { credMgrIsUnknown = true; } else { // record the known plugin name that we found for this display name @@ -624,9 +624,8 @@ export class PluginManagementFacility { */ if (pluginGroupNm.toLowerCase() === cmdTreeDef.name.toLowerCase()) { const conflictMessage = this.impLogger.error("The plugin named '%s' attempted to add a group of commands" + - " with the name '%s'" + - ". Your base application already contains a group with the name '%s'.", pluginGroupNm, pluginGroupDefinition.name, - cmdTreeDef.name); + " with the name '%s'. Your base application already contains a group with the name '%s'.", + pluginGroupNm, pluginGroupDefinition.name, cmdTreeDef.name); return { hasConflict: true, message: conflictMessage }; } @@ -634,9 +633,8 @@ export class PluginManagementFacility { for (const pluginAlias of pluginGroupDefinition.aliases) { if (pluginAlias.toLowerCase() === cmdTreeDef.name.toLowerCase()) { const conflictMessage = this.impLogger.error("The plugin named '%s' attempted to add a group of commands" + - " with the alias '%s' " + - ". Your base application already contains a group with the name '%s'.", pluginGroupNm, pluginAlias, - cmdTreeDef.name); + " with the alias '%s' . Your base application already contains a group with the name '%s'.", + pluginGroupNm, pluginAlias, cmdTreeDef.name); return { hasConflict: true, message: conflictMessage }; } } @@ -649,8 +647,8 @@ export class PluginManagementFacility { // if the plugin name matches an alias of the definition tree if (pluginGroupNm.toLowerCase() === nextAliasToTest.toLowerCase()) { const conflictMessage = this.impLogger.error("The plugin attempted to add a group of commands with the name '%s' " + - ". Your base application already contains a group with an alias '%s'.", pluginGroupNm, nextAliasToTest, - cmdTreeDef.name); + ". Your base application already contains a group with an alias '%s'.", + pluginGroupNm, nextAliasToTest, cmdTreeDef.name); return { hasConflict: true, message: conflictMessage }; } if (pluginGroupDefinition.aliases != null) { @@ -658,9 +656,8 @@ export class PluginManagementFacility { // if an alias of the plugin matches an alias of hte definition tree if (pluginAlias.toLowerCase() === nextAliasToTest.toLowerCase()) { const conflictMessage = this.impLogger.error("The plugin named '%s' attempted to add a " + - "group of command with the alias '%s', which conflicts with " + - "another alias of the same name for group '%s'.", pluginGroupDefinition.name, pluginAlias, - cmdTreeDef.name); + "group of command with the alias '%s', which conflicts with another alias of the same name for group '%s'.", + pluginGroupDefinition.name, pluginAlias, cmdTreeDef.name); return { hasConflict: true, message: conflictMessage }; } } @@ -882,19 +879,17 @@ export class PluginManagementFacility { */ const knownCredMgrs: ICredentialManagerNameMap[] = CredentialManagerOverride.getKnownCredMgrs(); overrideErrMsg += `"${settingNm}": "${CredentialManagerOverride.DEFAULT_CRED_MGR_NAME}" (default)`; - for ( let credMgrInx = 1; credMgrInx < knownCredMgrs.length; credMgrInx++) { + for (let credMgrInx = 1; credMgrInx < knownCredMgrs.length; credMgrInx++) { overrideErrMsg += `\n"${settingNm}": "${knownCredMgrs[credMgrInx].credMgrDisplayName}" `; - if ( typeof knownCredMgrs[credMgrInx].credMgrPluginName !== "undefined") { + if (typeof knownCredMgrs[credMgrInx].credMgrPluginName !== "undefined") { overrideErrMsg += `(supplied in CLI plugin ${knownCredMgrs[credMgrInx].credMgrPluginName}`; } - if ( typeof knownCredMgrs[credMgrInx].credMgrZEName !== "undefined") { + if (typeof knownCredMgrs[credMgrInx].credMgrZEName !== "undefined") { const punctuation = 8; overrideErrMsg += "\n"; - for (let indent: number = 0; indent < - settingNm.length + knownCredMgrs[credMgrInx].credMgrDisplayName.length + punctuation; - indent++ ) - { + const indentLength = settingNm.length + `${knownCredMgrs[credMgrInx].credMgrDisplayName}`.length + punctuation; + for (let indent: number = 0; indent < indentLength; indent++) { overrideErrMsg += " "; } overrideErrMsg += `and in ZE extension ${knownCredMgrs[credMgrInx].credMgrZEName}`; diff --git a/packages/imperative/src/imperative/src/plugins/cmd/list/list.handler.ts b/packages/imperative/src/imperative/src/plugins/cmd/list/list.handler.ts index 9fc6a09fe3..176d72db34 100644 --- a/packages/imperative/src/imperative/src/plugins/cmd/list/list.handler.ts +++ b/packages/imperative/src/imperative/src/plugins/cmd/list/list.handler.ts @@ -98,8 +98,7 @@ export default class ListHandler implements ICommandHandler { if(containsLegacyPlugin) { - listOutput = listOutput + `${chalk.yellow.bold("Plug-ins marked with (?) may be invalid or out of date, and might need to be reinstalled.")}`; - listOutput = listOutput + "\n"; + listOutput += chalk.yellow.bold("Plug-ins marked with (?) may be invalid or out of date, and might need to be reinstalled.") + "\n"; } if (listOutput === "") { diff --git a/packages/imperative/src/profiles/src/utils/__tests__/V1ProfileRead.unit.test.ts b/packages/imperative/src/profiles/src/utils/__tests__/V1ProfileRead.unit.test.ts index 6321523f1d..2e96d07811 100644 --- a/packages/imperative/src/profiles/src/utils/__tests__/V1ProfileRead.unit.test.ts +++ b/packages/imperative/src/profiles/src/utils/__tests__/V1ProfileRead.unit.test.ts @@ -12,7 +12,6 @@ jest.mock("fs"); jest.mock("../../../../io/src/IO"); jest.mock("js-yaml"); -jest.mock("yamljs"); jest.mock("../../../../utilities/src/ImperativeConfig"); import * as fs from "fs"; @@ -29,13 +28,11 @@ import { IProfile } from "../../../../index"; import { ImperativeConfig } from "../../../../utilities"; const readYaml = require("js-yaml"); -const writeYaml = require("yamljs"); const mocks = { createDirsSync: jest.spyOn(IO, "createDirsSync"), safeLoad: jest.spyOn(readYaml, "load"), writeFileSync: jest.spyOn(fs, "writeFileSync"), - yamlStringify: jest.spyOn(writeYaml, "stringify"), unlinkSync: jest.spyOn(fs, "unlinkSync"), existsSync: jest.spyOn(fs, "existsSync"), readdirSync: jest.spyOn(fs, "readdirSync"), diff --git a/packages/imperative/src/rest/index.ts b/packages/imperative/src/rest/index.ts index 27c81369cf..2b7c2ae485 100644 --- a/packages/imperative/src/rest/index.ts +++ b/packages/imperative/src/rest/index.ts @@ -17,6 +17,7 @@ export * from "./src/client/doc/IRestClientError"; export * from "./src/client/doc/IRestClientResponse"; export * from "./src/client/doc/IRestOptions"; export * from "./src/client/Headers"; +export * from "./src/client/Proxy"; export * from "./src/client/AbstractRestClient"; // export * from "./src/client/CompressionUtils"; export * from "./src/client/RestClient"; diff --git a/packages/imperative/src/security/__tests__/CredentialManagerOverride.unit.test.ts b/packages/imperative/src/security/__tests__/CredentialManagerOverride.unit.test.ts index 13d1824810..dabac1d8f4 100644 --- a/packages/imperative/src/security/__tests__/CredentialManagerOverride.unit.test.ts +++ b/packages/imperative/src/security/__tests__/CredentialManagerOverride.unit.test.ts @@ -57,13 +57,37 @@ describe("CredentialManagerOverride", () => { "credMgrDisplayName": "Secrets for Kubernetes", "credMgrPluginName": "@zowe/secrets-for-kubernetes-for-zowe-cli", "credMgrZEName": "Zowe.secrets-for-kubernetes" - } + }, + { + "credMgrDisplayName": false + }, ]; const receivedCredMgrs = CredentialManagerOverride.getKnownCredMgrs(); expect(receivedCredMgrs).toEqual(expectedCredMgrs); }); }); + describe("getCurrentCredMgr", () => { + it("should return the current credential manager", () => { + const getSettingsFileJsonSpy = jest.spyOn(CredentialManagerOverride as any, "getSettingsFileJson"); + getSettingsFileJsonSpy.mockReturnValue({json: {overrides: { CredentialManager: "test"}}}); + const current = CredentialManagerOverride.getCurrentCredMgr(); + expect(current).toEqual("test"); + }); + it("should return false if the credential management is disabled", () => { + const getSettingsFileJsonSpy = jest.spyOn(CredentialManagerOverride as any, "getSettingsFileJson"); + getSettingsFileJsonSpy.mockReturnValue({json: {overrides: { CredentialManager: false}}}); + const current = CredentialManagerOverride.getCurrentCredMgr(); + expect(current).toBe(false); + }); + it("should return the default credential manager if settings file does not exist", () => { + const getSettingsFileJsonSpy = jest.spyOn(CredentialManagerOverride as any, "getSettingsFileJson"); + getSettingsFileJsonSpy.mockImplementation(() => {throw "test";}); + const current = CredentialManagerOverride.getCurrentCredMgr(); + expect(current).toEqual(CredentialManagerOverride.DEFAULT_CRED_MGR_NAME); + }); + }); + describe("getCredMgrInfoByDisplayName", () => { it("should return null when name is not found", () => { const credMgrInfo = CredentialManagerOverride.getCredMgrInfoByDisplayName("NotACredMgrName"); diff --git a/packages/imperative/src/security/__tests__/doc/ICredentialManagerNameMap.unit.test.ts b/packages/imperative/src/security/__tests__/doc/ICredentialManagerNameMap.unit.test.ts index a900552d89..617cb8c888 100644 --- a/packages/imperative/src/security/__tests__/doc/ICredentialManagerNameMap.unit.test.ts +++ b/packages/imperative/src/security/__tests__/doc/ICredentialManagerNameMap.unit.test.ts @@ -18,7 +18,7 @@ describe("ICredentialManagerNameMap", () => { join(__dirname, "../../src/doc/ICredentialManagerNameMap.ts"), "utf8" ); - expect(nameMap).toContain("credMgrDisplayName: string;"); + expect(nameMap).toContain("credMgrDisplayName: string | false;"); expect(nameMap).toContain("credMgrPluginName?: string;"); expect(nameMap).toContain("credMgrZEName?: string;"); }); diff --git a/packages/imperative/src/security/src/CredentialManagerFactory.ts b/packages/imperative/src/security/src/CredentialManagerFactory.ts index a382a2ecd2..90134a1f46 100644 --- a/packages/imperative/src/security/src/CredentialManagerFactory.ts +++ b/packages/imperative/src/security/src/CredentialManagerFactory.ts @@ -82,13 +82,13 @@ export class CredentialManagerFactory { } // If the display name is not passed, use the cli name - const displayName = params.displayName == null ? params.service : params.displayName; + const displayName = params.displayName ?? params.service; // If a manager override was not passed, use the default keytar manager - const Manager = params.Manager == null ? DefaultCredentialManager : params.Manager; + const Manager = params.Manager ?? DefaultCredentialManager; // Default invalid on failure to false if not specified - params.invalidOnFailure = params.invalidOnFailure == null ? false : params.invalidOnFailure; + params.invalidOnFailure ??= false; if (this.mManager != null) { // Something tried to change the already existing credential manager, we should stop this. diff --git a/packages/imperative/src/security/src/CredentialManagerOverride.ts b/packages/imperative/src/security/src/CredentialManagerOverride.ts index 0a943d1415..261c063fbc 100644 --- a/packages/imperative/src/security/src/CredentialManagerOverride.ts +++ b/packages/imperative/src/security/src/CredentialManagerOverride.ts @@ -40,7 +40,10 @@ export class CredentialManagerOverride { "credMgrDisplayName": "Secrets for Kubernetes", "credMgrPluginName": "@zowe/secrets-for-kubernetes-for-zowe-cli", "credMgrZEName": "Zowe.secrets-for-kubernetes" - } + }, + { + "credMgrDisplayName": false + }, ]; //________________________________________________________________________ @@ -53,7 +56,7 @@ export class CredentialManagerOverride { * @returns An ICredentialManagerNameMap or * null if the specified plugin is not a known credential manager. */ - public static getCredMgrInfoByDisplayName(credMgrDisplayName: string) : ICredentialManagerNameMap | null { + public static getCredMgrInfoByDisplayName(credMgrDisplayName: string | false) : ICredentialManagerNameMap | null { return this.KNOWN_CRED_MGRS.find((credMgr) => credMgr.credMgrDisplayName === credMgrDisplayName) ?? null; } @@ -93,6 +96,21 @@ export class CredentialManagerOverride { return this.KNOWN_CRED_MGRS; } + //________________________________________________________________________ + /** + * Get the active credential manager. + * + * @returns Information about the current redential managers or false if none is set. + */ + public static getCurrentCredMgr() : string | false { + try { + const settings = this.getSettingsFileJson(); + return settings.json.overrides.CredentialManager; + } catch (err) { + return this.DEFAULT_CRED_MGR_NAME; + } + } + /** * Record the specified credential manager in the configuration of overrides. * A plugin or ZE extension that provides a credential manager would record @@ -103,7 +121,7 @@ export class CredentialManagerOverride { * * @throws An ImperativeError upon error. */ - public static recordCredMgrInConfig(newCredMgrName: string) : void { + public static recordCredMgrInConfig(newCredMgrName: string | false) : void { const credMgrInfo: ICredentialManagerNameMap = CredentialManagerOverride.getCredMgrInfoByDisplayName(newCredMgrName); if (credMgrInfo === null) { @@ -160,7 +178,7 @@ export class CredentialManagerOverride { * * @throws An ImperativeError upon error. */ - public static recordDefaultCredMgrInConfig(credMgrToReplace: string) : void { + public static recordDefaultCredMgrInConfig(credMgrToReplace: string | false) : void { // read in the existing settings file let settings: any; try { diff --git a/packages/imperative/src/security/src/doc/ICredentialManagerNameMap.ts b/packages/imperative/src/security/src/doc/ICredentialManagerNameMap.ts index 57b38e8e27..2a7e88a456 100644 --- a/packages/imperative/src/security/src/doc/ICredentialManagerNameMap.ts +++ b/packages/imperative/src/security/src/doc/ICredentialManagerNameMap.ts @@ -24,8 +24,11 @@ export interface ICredentialManagerNameMap { /** * Name of the credential manager. This is the name that will be stored in * $ZOWE_CLI_HOME/settings/imperative.json. + * + * A boolean value of false will be accepted. + * This indicates that credentials may be stored in plain text. */ - credMgrDisplayName: string; + credMgrDisplayName: string | false; /** * Name of the plugin that supplies the credential manager override software. diff --git a/packages/provisioning/package.json b/packages/provisioning/package.json index 3b544e502c..c259e00fb5 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/secrets/package.json b/packages/secrets/package.json index bb7b31824c..3655e662c1 100644 --- a/packages/secrets/package.json +++ b/packages/secrets/package.json @@ -3,7 +3,7 @@ "description": "Credential management facilities for Imperative, Zowe CLI, and extenders.", "repository": "https://github.com/zowe/zowe-cli.git", "author": "Zowe", - "version": "8.0.0-next.202407181255", + "version": "8.0.0-next.202407311544", "homepage": "https://github.com/zowe/zowe-cli/tree/master/packages/secrets#readme", "bugs": { "url": "https://github.com/zowe/zowe-cli/issues" diff --git a/packages/workflows/package.json b/packages/workflows/package.json index 9aa67cd195..ea0ab4bbe7 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255" + "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407311544" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosconsole/package.json b/packages/zosconsole/package.json index ee30d77ff7..17bc86d02a 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosfiles/package.json b/packages/zosfiles/package.json index dd68e4250c..4df09eb712 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255", - "@zowe/zos-uss-for-zowe-sdk": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544", + "@zowe/zos-uss-for-zowe-sdk": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosjobs/package.json b/packages/zosjobs/package.json index 0a91b98963..0105f547ec 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255" + "@zowe/zos-files-for-zowe-sdk": "8.0.0-next.202407311544" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zoslogs/package.json b/packages/zoslogs/package.json index 71028d9207..675efc3bb6 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosmf/package.json b/packages/zosmf/package.json index f521c2aa53..3f1fd7ec62 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zostso/package.json b/packages/zostso/package.json index c458a72fc3..0b97565aea 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255" + "@zowe/zosmf-for-zowe-sdk": "8.0.0-next.202407311544" }, "devDependencies": { - "@zowe/cli-test-utils": "8.0.0-next.202407181255", - "@zowe/core-for-zowe-sdk": "8.0.0-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/core-for-zowe-sdk": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/core-for-zowe-sdk": "^8.0.0-next", diff --git a/packages/zosuss/package.json b/packages/zosuss/package.json index 5304ae94cd..12fbe8ebc6 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-next.202407181255", + "version": "8.0.0-next.202407311544", "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-next.202407181255", - "@zowe/imperative": "8.0.0-next.202407181255" + "@zowe/cli-test-utils": "8.0.0-next.202407311544", + "@zowe/imperative": "8.0.0-next.202407311544" }, "peerDependencies": { "@zowe/imperative": "^8.0.0-next"