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/npm-shrinkwrap.json b/npm-shrinkwrap.json index cc6384aa94..735f1b893e 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -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" @@ -14840,6 +14849,7 @@ }, "node_modules/sprintf-js": { "version": "1.0.3", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/ssh2": { @@ -16136,6 +16146,7 @@ }, "node_modules/wrappy": { "version": "1.0.2", + "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -16249,25 +16260,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", @@ -16481,7 +16473,6 @@ "strip-ansi": "^6.0.1", "which": "^4.0.0", "wrap-ansi": "^7.0.0", - "yamljs": "^0.3.0", "yargs": "^17.7.2" }, "devDependencies": { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 19cf6048a6..92c34782f9 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. +## Recent Changes + +- 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..999b624f85 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 @@ -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,11 @@ 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 - user & password with existing base profile", async () => { @@ -301,7 +306,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 +354,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 +398,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/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/imperative/CHANGELOG.md b/packages/imperative/CHANGELOG.md index 38f957a5a4..485571c86e 100644 --- a/packages/imperative/CHANGELOG.md +++ b/packages/imperative/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to the Imperative package will be documented in this file. +## Recent Changes + +- 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) 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..f4a9f6f19b 100644 --- a/packages/imperative/package.json +++ b/packages/imperative/package.json @@ -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": { 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/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/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/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"),