From 0a5a3167656a7d53b5f8930e164f0f847d41b8b2 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Fri, 31 Mar 2023 15:55:53 -0400 Subject: [PATCH 1/6] Allow secure credentials to be enabled in Theia Signed-off-by: Timothy Johnson --- packages/zowe-explorer/CHANGELOG.md | 1 + packages/zowe-explorer/src/globals.ts | 9 ++------- packages/zowe-explorer/src/shared/init.ts | 4 ++-- packages/zowe-explorer/src/utils/ProfilesUtils.ts | 2 +- packages/zowe-explorer/src/utils/SettingsConfig.ts | 4 ++-- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 3b03c28684..0b7757018e 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added back fix that was accidentally removed between updates: Resolved an issue where VSCode did not provide all context menu options for a profile node after a multi-select operation. [#2108](https://github.com/zowe/vscode-extension-for-zowe/pull/2108) - Fixed issue where "Paste" option is shown for a multi-select operation in the "Data Sets" pane. - Fixed z/OSMF profiles issue with Data Sets and Jobs with special characters in the names. [#2175](https://github.com/zowe/vscode-extension-for-zowe/issues/2175) +- Fixed secure credential storage not possible to enable in Theia. ## `2.7.0` diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 1b9fca826b..ab51d39f10 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -327,13 +327,8 @@ export function setSavedProfileContents(value: Uint8Array): void { SAVED_PROFILE_CONTENTS = value; } -export async function setGlobalSecurityValue(): Promise { - if (ISTHEIA) { - PROFILE_SECURITY = false; - await SettingsConfig.setDirectValue(this.SETTINGS_SECURE_CREDENTIALS_ENABLED, false, vscode.ConfigurationTarget.Global); - return; - } - const settingEnabled: boolean = SettingsConfig.getDirectValue(this.SETTINGS_SECURE_CREDENTIALS_ENABLED); +export function setGlobalSecurityValue(): void { + const settingEnabled: boolean = SettingsConfig.getDirectValue(this.SETTINGS_SECURE_CREDENTIALS_ENABLED, !ISTHEIA); if (!settingEnabled) { PROFILE_SECURITY = false; } else { diff --git a/packages/zowe-explorer/src/shared/init.ts b/packages/zowe-explorer/src/shared/init.ts index fcdc9fae95..9e4995d19c 100644 --- a/packages/zowe-explorer/src/shared/init.ts +++ b/packages/zowe-explorer/src/shared/init.ts @@ -67,8 +67,8 @@ export function registerRefreshCommand( export function registerCommonCommands(context: vscode.ExtensionContext, providers: IZoweProviders): void { // Update imperative.json to false only when VS Code setting is set to false context.subscriptions.push( - vscode.commands.registerCommand("zowe.updateSecureCredentials", async () => { - await globals.setGlobalSecurityValue(); + vscode.commands.registerCommand("zowe.updateSecureCredentials", () => { + globals.setGlobalSecurityValue(); writeOverridesFile(); }) ); diff --git a/packages/zowe-explorer/src/utils/ProfilesUtils.ts b/packages/zowe-explorer/src/utils/ProfilesUtils.ts index db979fa976..a4b439c2c3 100644 --- a/packages/zowe-explorer/src/utils/ProfilesUtils.ts +++ b/packages/zowe-explorer/src/utils/ProfilesUtils.ts @@ -309,7 +309,7 @@ export async function promptCredentials(node: IZoweTreeNode): Promise { export async function initializeZoweFolder(): Promise { // ensure the Secure Credentials Enabled value is read // set globals.PROFILE_SECURITY value accordingly - await globals.setGlobalSecurityValue(); + globals.setGlobalSecurityValue(); // Ensure that ~/.zowe folder exists // Ensure that the ~/.zowe/settings/imperative.json exists // TODO: update code below once this imperative issue is resolved. diff --git a/packages/zowe-explorer/src/utils/SettingsConfig.ts b/packages/zowe-explorer/src/utils/SettingsConfig.ts index 192eefb8f9..0f96ac2537 100644 --- a/packages/zowe-explorer/src/utils/SettingsConfig.ts +++ b/packages/zowe-explorer/src/utils/SettingsConfig.ts @@ -28,9 +28,9 @@ export class SettingsConfig { * } * @param {string} key - The config property that needs retrieving */ - public static getDirectValue(key: string): T { + public static getDirectValue(key: string, defaultValue?: T): T { const [first, ...rest] = key.split("."); - return vscode.workspace.getConfiguration(first).get(rest.join(".")); + return vscode.workspace.getConfiguration(first).get(rest.join("."), defaultValue); } /** From d024947d2596e3851adc97eb118a4aa38701431c Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 4 Apr 2023 16:56:49 -0400 Subject: [PATCH 2/6] Improve error handling for Zowe folder and don't overwrite setting in Theia Signed-off-by: Timothy Johnson --- .../zowe-explorer/src/ZoweExplorerExtender.ts | 5 ----- packages/zowe-explorer/src/globals.ts | 9 ++++++-- packages/zowe-explorer/src/shared/init.ts | 4 ++-- .../zowe-explorer/src/utils/ProfilesUtils.ts | 20 +++++++++++++++--- .../zowe-explorer/src/utils/SettingsConfig.ts | 21 +++++++++++++++++-- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/packages/zowe-explorer/src/ZoweExplorerExtender.ts b/packages/zowe-explorer/src/ZoweExplorerExtender.ts index 1a983c390a..ce476fd715 100644 --- a/packages/zowe-explorer/src/ZoweExplorerExtender.ts +++ b/packages/zowe-explorer/src/ZoweExplorerExtender.ts @@ -52,11 +52,6 @@ export class ZoweExplorerExtender implements ZoweExplorerApi.IApiExplorerExtende * @param errorDetails Details of the error (to be parsed for config name and path) */ public static showZoweConfigError(errorDetails: string): void { - if (errorDetails.includes("imperative.json")) { - // Handle errors parsing Imperative overrides file separately from errors in config - errorHandling(errorDetails); - return; - } Gui.errorMessage( localize("initialize.profiles.error", 'Error encountered when loading your Zowe config. Click "Show Config" for more details.'), { items: ["Show Config"] } diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index ab51d39f10..4d99c80070 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -327,8 +327,13 @@ export function setSavedProfileContents(value: Uint8Array): void { SAVED_PROFILE_CONTENTS = value; } -export function setGlobalSecurityValue(): void { - const settingEnabled: boolean = SettingsConfig.getDirectValue(this.SETTINGS_SECURE_CREDENTIALS_ENABLED, !ISTHEIA); +export async function setGlobalSecurityValue(): Promise { + if (ISTHEIA && !SettingsConfig.isConfigSettingSetByUser(SETTINGS_SECURE_CREDENTIALS_ENABLED)) { + PROFILE_SECURITY = false; + await SettingsConfig.setDirectValue(SETTINGS_SECURE_CREDENTIALS_ENABLED, false, vscode.ConfigurationTarget.Global); + return; + } + const settingEnabled: boolean = SettingsConfig.getDirectValue(SETTINGS_SECURE_CREDENTIALS_ENABLED); if (!settingEnabled) { PROFILE_SECURITY = false; } else { diff --git a/packages/zowe-explorer/src/shared/init.ts b/packages/zowe-explorer/src/shared/init.ts index 9e4995d19c..fcdc9fae95 100644 --- a/packages/zowe-explorer/src/shared/init.ts +++ b/packages/zowe-explorer/src/shared/init.ts @@ -67,8 +67,8 @@ export function registerRefreshCommand( export function registerCommonCommands(context: vscode.ExtensionContext, providers: IZoweProviders): void { // Update imperative.json to false only when VS Code setting is set to false context.subscriptions.push( - vscode.commands.registerCommand("zowe.updateSecureCredentials", () => { - globals.setGlobalSecurityValue(); + vscode.commands.registerCommand("zowe.updateSecureCredentials", async () => { + await globals.setGlobalSecurityValue(); writeOverridesFile(); }) ); diff --git a/packages/zowe-explorer/src/utils/ProfilesUtils.ts b/packages/zowe-explorer/src/utils/ProfilesUtils.ts index ffecaf231f..b3f1684a8a 100644 --- a/packages/zowe-explorer/src/utils/ProfilesUtils.ts +++ b/packages/zowe-explorer/src/utils/ProfilesUtils.ts @@ -303,7 +303,7 @@ export async function promptCredentials(node: IZoweTreeNode): Promise { export async function initializeZoweFolder(): Promise { // ensure the Secure Credentials Enabled value is read // set globals.PROFILE_SECURITY value accordingly - globals.setGlobalSecurityValue(); + await globals.setGlobalSecurityValue(); // Ensure that ~/.zowe folder exists // Ensure that the ~/.zowe/settings/imperative.json exists // TODO: update code below once this imperative issue is resolved. @@ -372,10 +372,24 @@ export function writeOverridesFile(): void { export async function initializeZoweProfiles(): Promise { try { await initializeZoweFolder(); - await readConfigFromDisk(); } catch (err) { globals.LOG.error(err); - ZoweExplorerExtender.showZoweConfigError(err.message); + Gui.errorMessage(localize( + "initializeZoweFolder.error", + "Failed to initialize Zowe folder: {0}", + err.message + )); + } + + try { + await readConfigFromDisk(); + } catch (err) { + if (err instanceof imperative.ImperativeError) { + errorHandling(err, undefined, err.details.causeErrors); + } else { + globals.LOG.error(err); + ZoweExplorerExtender.showZoweConfigError(err.message); + } } if (!fs.existsSync(globals.ZOWETEMPFOLDER)) { diff --git a/packages/zowe-explorer/src/utils/SettingsConfig.ts b/packages/zowe-explorer/src/utils/SettingsConfig.ts index 0f96ac2537..64aff3849c 100644 --- a/packages/zowe-explorer/src/utils/SettingsConfig.ts +++ b/packages/zowe-explorer/src/utils/SettingsConfig.ts @@ -28,9 +28,9 @@ export class SettingsConfig { * } * @param {string} key - The config property that needs retrieving */ - public static getDirectValue(key: string, defaultValue?: T): T { + public static getDirectValue(key: string): T { const [first, ...rest] = key.split("."); - return vscode.workspace.getConfiguration(first).get(rest.join("."), defaultValue); + return vscode.workspace.getConfiguration(first).get(rest.join(".")); } /** @@ -47,6 +47,23 @@ export class SettingsConfig { return vscode.workspace.getConfiguration(first).update(rest.join("."), value, target); } + public static isConfigSettingSetByUser(key: string): boolean { + const [first, ...rest] = key.split("."); + const inspect = vscode.workspace.getConfiguration(first).inspect(rest.join(".")); + if (inspect === undefined) { + return false; + } + + return ( + inspect.globalValue !== undefined || + inspect.workspaceValue !== undefined || + inspect.workspaceFolderValue !== undefined || + inspect.globalLanguageValue !== undefined || + inspect.workspaceLanguageValue !== undefined || + inspect.workspaceFolderLanguageValue !== undefined + ); + } + public static async standardizeSettings(): Promise { const globalIsNotMigrated = SettingsConfig.configurations.inspect(globals.SETTINGS_VERSION).globalValue !== SettingsConfig.currentVersionNumber; From 63d111a1a1a9f49398673ed6e9f2973a71e67584 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Wed, 5 Apr 2023 18:10:38 -0400 Subject: [PATCH 3/6] Update unit tests for error handling Signed-off-by: Timothy Johnson --- .../ZoweExplorerExtender.unit.test.ts | 9 ---- .../__unit__/utils/ProfilesUtils.unit.test.ts | 52 +++++++++++++++++-- .../sample/src/utils/ProfilesUtils.i18n.json | 1 + .../zowe-explorer/src/utils/ProfilesUtils.ts | 12 ++--- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts index 6c412c1fb0..07e1ecc868 100644 --- a/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts @@ -192,15 +192,6 @@ describe("ZoweExplorerExtender unit tests", () => { } }); - it("properly handles error parsing Imperative overrides json", async () => { - const blockMocks = await createBlockMocks(); - ZoweExplorerExtender.createInstance(); - const errMsg = "Failed to parse JSON file imperative.json"; - - await ZoweExplorerExtender.showZoweConfigError(errMsg); - expect(blockMocks.mockErrorMessage).toHaveBeenCalledWith("Error: " + errMsg, undefined); - }); - it("should initialize zowe", async () => { const blockMocks = await createBlockMocks(); Object.defineProperty(vscode.workspace, "workspaceFolders", { diff --git a/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts index e7d5442735..e68a56ab54 100644 --- a/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts @@ -18,6 +18,7 @@ import * as vscode from "vscode"; import * as zowe from "@zowe/cli"; import { Profiles } from "../../../src/Profiles"; import { ZoweLogger } from "../../../src/utils/LoggerUtils"; +import { ZoweExplorerExtender } from "../../../src/ZoweExplorerExtender"; jest.mock("fs"); jest.mock("vscode"); @@ -130,7 +131,7 @@ describe("ProfilesUtils unit tests", () => { configurable: true, }); const mockReadProfilesFromDisk = jest.fn(); - jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ + const profInfoSpy = jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ readProfilesFromDisk: mockReadProfilesFromDisk, usingTeamConfig: true, getTeamConfig: () => ({ @@ -156,6 +157,7 @@ describe("ProfilesUtils unit tests", () => { }); await expect(profileUtils.readConfigFromDisk()).resolves.not.toThrow(); expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1); + profInfoSpy.mockRestore(); }); it("should readConfigFromDisk and find with defaults", async () => { @@ -170,7 +172,7 @@ describe("ProfilesUtils unit tests", () => { configurable: true, }); const mockReadProfilesFromDisk = jest.fn(); - jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ + const profInfoSpy = jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ readProfilesFromDisk: mockReadProfilesFromDisk, usingTeamConfig: true, getTeamConfig: () => [], @@ -181,6 +183,7 @@ describe("ProfilesUtils unit tests", () => { }); await expect(profileUtils.readConfigFromDisk()).resolves.not.toThrow(); expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1); + profInfoSpy.mockRestore(); }); it("should keep Imperative error details if readConfigFromDisk fails", async () => { @@ -196,13 +199,14 @@ describe("ProfilesUtils unit tests", () => { }); const impErr = new zowe.imperative.ImperativeError({ msg: "Unexpected Imperative error" }); const mockReadProfilesFromDisk = jest.fn().mockRejectedValue(impErr); - jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ + const profInfoSpy = jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ readProfilesFromDisk: mockReadProfilesFromDisk, usingTeamConfig: true, getTeamConfig: () => [], } as never); await expect(profileUtils.readConfigFromDisk()).rejects.toBe(impErr); expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1); + profInfoSpy.mockRestore(); }); }); @@ -369,6 +373,48 @@ describe("ProfilesUtils unit tests", () => { }); }); + describe("initializeZoweProfiles", () => { + it("should successfully initialize Zowe folder and read config from disk", async () => { + const initZoweFolderSpy = jest.spyOn(profileUtils, "initializeZoweFolder"); + const readConfigFromDiskSpy = jest.spyOn(profileUtils, "readConfigFromDisk").mockResolvedValueOnce(); + await profileUtils.initializeZoweProfiles(); + expect(initZoweFolderSpy).toHaveBeenCalledTimes(1); + expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1); + expect(ZoweLogger.error).not.toHaveBeenCalled(); + }); + + it("should handle error thrown on initialize Zowe folder", async () => { + const testError = new Error("initializeZoweFolder failed"); + const initZoweFolderSpy = jest.spyOn(profileUtils, "initializeZoweFolder").mockRejectedValueOnce(testError); + const readConfigFromDiskSpy = jest.spyOn(profileUtils, "readConfigFromDisk").mockResolvedValueOnce(); + await profileUtils.initializeZoweProfiles(); + expect(initZoweFolderSpy).toHaveBeenCalledTimes(1); + expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1); + expect(Gui.errorMessage).toHaveBeenCalledWith(expect.stringContaining(testError.message)); + }); + + it("should handle Imperative error thrown on read config from disk", async () => { + const testError = new zowe.imperative.ImperativeError({ msg: "readConfigFromDisk failed" }); + const initZoweFolderSpy = jest.spyOn(profileUtils, "initializeZoweFolder").mockResolvedValueOnce(); + const readConfigFromDiskSpy = jest.spyOn(profileUtils, "readConfigFromDisk").mockRejectedValueOnce(testError); + await profileUtils.initializeZoweProfiles(); + expect(initZoweFolderSpy).toHaveBeenCalledTimes(1); + expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1); + expect(Gui.errorMessage).toHaveBeenCalledWith(expect.stringContaining(testError.message)); + }); + + it("should handle JSON parse error thrown on read config from disk", async () => { + const testError = new Error("readConfigFromDisk failed"); + const initZoweFolderSpy = jest.spyOn(profileUtils, "initializeZoweFolder").mockResolvedValueOnce(); + const readConfigFromDiskSpy = jest.spyOn(profileUtils, "readConfigFromDisk").mockRejectedValueOnce(testError); + const showZoweConfigErrorSpy = jest.spyOn(ZoweExplorerExtender, "showZoweConfigError").mockReturnValueOnce(); + await profileUtils.initializeZoweProfiles(); + expect(initZoweFolderSpy).toHaveBeenCalledTimes(1); + expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1); + expect(showZoweConfigErrorSpy).toHaveBeenCalledWith(testError.message); + }); + }); + it("filterItem should get label if the filterItem icon exists", () => { const testFilterItem = new profileUtils.FilterItem({ icon: "test", diff --git a/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json b/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json index f111d288b8..3956eb5866 100644 --- a/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json @@ -13,6 +13,7 @@ "promptCredentials.updatedCredentials": "Credentials for {0} were successfully updated", "initializeZoweFolder.location": "Zowe home directory is located at {0}", "writeOverridesFile.jsonParseError": "Failed to parse JSON file {0}:", + "initializeZoweFolder.error": "Failed to initialize Zowe folder: {0}", "initializeZoweProfiles.success": "Zowe Profiles initialized successfully.", "initializeZoweTempFolder.success": "Zowe Temp folder initialized successfully." } diff --git a/packages/zowe-explorer/src/utils/ProfilesUtils.ts b/packages/zowe-explorer/src/utils/ProfilesUtils.ts index 0642281f79..97ceca6dda 100644 --- a/packages/zowe-explorer/src/utils/ProfilesUtils.ts +++ b/packages/zowe-explorer/src/utils/ProfilesUtils.ts @@ -386,22 +386,18 @@ export function writeOverridesFile(): void { export async function initializeZoweProfiles(): Promise { ZoweLogger.trace("ProfilesUtils.initializeZoweProfiles called."); try { - await initializeZoweFolder(); + await this.initializeZoweFolder(); } catch (err) { ZoweLogger.error(err); - Gui.errorMessage(localize( - "initializeZoweFolder.error", - "Failed to initialize Zowe folder: {0}", - err.message - )); + Gui.errorMessage(localize("initializeZoweFolder.error", "Failed to initialize Zowe folder: {0}", err.message)); } try { - await readConfigFromDisk(); + await this.readConfigFromDisk(); ZoweLogger.info(localize("initializeZoweProfiles.success", "Zowe Profiles initialized successfully.")); } catch (err) { if (err instanceof imperative.ImperativeError) { - errorHandling(err, undefined, err.details.causeErrors); + errorHandling(err, undefined, err.mDetails.causeErrors); } else { ZoweLogger.error(err); ZoweExplorerExtender.showZoweConfigError(err.message); From 8e5e962aa4292ccf2b63ae9c1f7d21dcb922d024 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Wed, 5 Apr 2023 20:52:43 -0400 Subject: [PATCH 4/6] Move ProfileUtils methods into class to fix tests Signed-off-by: Timothy Johnson --- .../ZoweExplorerExtender.unit.test.ts | 4 +- .../__unit__/shared/init.unit.test.ts | 6 +- .../__unit__/utils/ProfilesUtils.unit.test.ts | 68 ++-- .../sample/src/utils/ProfilesUtils.i18n.json | 4 +- packages/zowe-explorer/src/Profiles.ts | 4 +- .../zowe-explorer/src/ZoweExplorerExtender.ts | 4 +- packages/zowe-explorer/src/extension.ts | 6 +- packages/zowe-explorer/src/shared/init.ts | 6 +- .../zowe-explorer/src/utils/ProfilesUtils.ts | 368 +++++++++--------- 9 files changed, 236 insertions(+), 234 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts index 07e1ecc868..f0b31380c4 100644 --- a/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/ZoweExplorerExtender.unit.test.ts @@ -22,7 +22,7 @@ import { Profiles } from "../../src/Profiles"; import * as path from "path"; import * as fs from "fs"; import { getZoweDir, Gui } from "@zowe/zowe-explorer-api"; -import * as profilesUtils from "../../src/utils/ProfilesUtils"; +import * as profUtils from "../../src/utils/ProfilesUtils"; import { ZoweLogger } from "../../src/utils/LoggerUtils"; jest.mock("fs"); @@ -211,7 +211,7 @@ describe("ZoweExplorerExtender unit tests", () => { const readProfilesFromDiskSpy = jest.fn(); const refreshProfilesQueueAddSpy = jest.spyOn((ZoweExplorerExtender as any).refreshProfilesQueue, "add"); - jest.spyOn(profilesUtils, "getProfileInfo").mockReturnValue({ + jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({ readProfilesFromDisk: readProfilesFromDiskSpy, } as any); await expect(blockMocks.instTest.initForZowe("USS", ["" as any])).resolves.not.toThrow(); diff --git a/packages/zowe-explorer/__tests__/__unit__/shared/init.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/shared/init.unit.test.ts index ce549b4159..8115b6a9ff 100644 --- a/packages/zowe-explorer/__tests__/__unit__/shared/init.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/shared/init.unit.test.ts @@ -15,7 +15,7 @@ import * as refreshActions from "../../../src/shared/refresh"; import * as sharedActions from "../../../src/shared/actions"; import * as sharedExtension from "../../../src/shared/init"; import { Profiles } from "../../../src/Profiles"; -import * as profileUtils from "../../../src/utils/ProfilesUtils"; +import * as profUtils from "../../../src/utils/ProfilesUtils"; import * as tempFolder from "../../../src/utils/TempFolder"; import * as zowe from "@zowe/cli"; import { IJestIt, ITestContext, processSubscriptions, spyOnSubscriptions } from "../../__common__/testUtils"; @@ -44,12 +44,12 @@ describe("Test src/shared/extension", () => { name: "zowe.updateSecureCredentials", mock: [ { spy: jest.spyOn(globals, "setGlobalSecurityValue"), arg: [] }, - { spy: jest.spyOn(profileUtils, "writeOverridesFile"), arg: [] }, + { spy: jest.spyOn(profUtils.ProfilesUtils, "writeOverridesFile"), arg: [] }, ], }, { name: "zowe.promptCredentials", - mock: [{ spy: jest.spyOn(profileUtils, "promptCredentials"), arg: [test.value] }], + mock: [{ spy: jest.spyOn(profUtils.ProfilesUtils, "promptCredentials"), arg: [test.value] }], }, { name: "onDidChangeConfiguration:1", diff --git a/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts index e68a56ab54..994dc84d06 100644 --- a/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts @@ -13,7 +13,7 @@ import * as fs from "fs"; import * as path from "path"; import { Gui, ProfilesCache, ZoweVsCodeExtension } from "@zowe/zowe-explorer-api"; import * as globals from "../../../src/globals"; -import * as profileUtils from "../../../src/utils/ProfilesUtils"; +import * as profUtils from "../../../src/utils/ProfilesUtils"; import * as vscode from "vscode"; import * as zowe from "@zowe/cli"; import { Profiles } from "../../../src/Profiles"; @@ -63,7 +63,7 @@ describe("ProfilesUtils unit tests", () => { const errorDetails = new Error("i haz error"); const label = "test"; const moreInfo = "Task failed successfully"; - await profileUtils.errorHandling(errorDetails, label, moreInfo); + await profUtils.errorHandling(errorDetails, label, moreInfo); expect(Gui.errorMessage).toBeCalledWith(`${moreInfo} ` + errorDetails); expect(ZoweLogger.error).toBeCalledWith(`Error: ${errorDetails.message}\n` + JSON.stringify({ errorDetails, label, moreInfo })); }); @@ -92,7 +92,7 @@ describe("ProfilesUtils unit tests", () => { openConfigFile: spyOpenConfigFile, }), }); - await profileUtils.errorHandling(errorDetails, label, moreInfo); + await profUtils.errorHandling(errorDetails, label, moreInfo); expect(spyOpenConfigFile).toBeCalledTimes(1); }); @@ -104,7 +104,7 @@ describe("ProfilesUtils unit tests", () => { }); const label = "test"; const moreInfo = "Task failed successfully"; - jest.spyOn(profileUtils, "isTheia").mockReturnValue(false); + jest.spyOn(profUtils, "isTheia").mockReturnValue(false); const showMessageSpy = jest.spyOn(Gui, "showMessage").mockResolvedValue("selection"); const ssoLoginSpy = jest.fn(); Object.defineProperty(Profiles, "getInstance", { @@ -112,7 +112,7 @@ describe("ProfilesUtils unit tests", () => { ssoLogin: ssoLoginSpy, }), }); - await profileUtils.errorHandling(errorDetails, label, moreInfo); + await profUtils.errorHandling(errorDetails, label, moreInfo); expect(showMessageSpy).toBeCalledTimes(1); expect(ssoLoginSpy).toBeCalledTimes(1); }); @@ -131,7 +131,7 @@ describe("ProfilesUtils unit tests", () => { configurable: true, }); const mockReadProfilesFromDisk = jest.fn(); - const profInfoSpy = jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ + const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({ readProfilesFromDisk: mockReadProfilesFromDisk, usingTeamConfig: true, getTeamConfig: () => ({ @@ -155,7 +155,7 @@ describe("ProfilesUtils unit tests", () => { value: jest.fn(), configurable: true, }); - await expect(profileUtils.readConfigFromDisk()).resolves.not.toThrow(); + await expect(profUtils.ProfilesUtils.readConfigFromDisk()).resolves.not.toThrow(); expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1); profInfoSpy.mockRestore(); }); @@ -172,7 +172,7 @@ describe("ProfilesUtils unit tests", () => { configurable: true, }); const mockReadProfilesFromDisk = jest.fn(); - const profInfoSpy = jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ + const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({ readProfilesFromDisk: mockReadProfilesFromDisk, usingTeamConfig: true, getTeamConfig: () => [], @@ -181,7 +181,7 @@ describe("ProfilesUtils unit tests", () => { value: jest.fn(), configurable: true, }); - await expect(profileUtils.readConfigFromDisk()).resolves.not.toThrow(); + await expect(profUtils.ProfilesUtils.readConfigFromDisk()).resolves.not.toThrow(); expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1); profInfoSpy.mockRestore(); }); @@ -199,12 +199,12 @@ describe("ProfilesUtils unit tests", () => { }); const impErr = new zowe.imperative.ImperativeError({ msg: "Unexpected Imperative error" }); const mockReadProfilesFromDisk = jest.fn().mockRejectedValue(impErr); - const profInfoSpy = jest.spyOn(zowe.imperative, "ProfileInfo").mockResolvedValue({ + const profInfoSpy = jest.spyOn(profUtils.ProfilesUtils, "getProfileInfo").mockReturnValue({ readProfilesFromDisk: mockReadProfilesFromDisk, usingTeamConfig: true, getTeamConfig: () => [], } as never); - await expect(profileUtils.readConfigFromDisk()).rejects.toBe(impErr); + await expect(profUtils.ProfilesUtils.readConfigFromDisk()).rejects.toBe(impErr); expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1); profInfoSpy.mockRestore(); }); @@ -229,7 +229,7 @@ describe("ProfilesUtils unit tests", () => { configurable: true, }); jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue([]); - await profileUtils.promptCredentials(null); + await profUtils.ProfilesUtils.promptCredentials(null); expect(getProfileInfoSpy).toHaveBeenCalled(); }); @@ -250,7 +250,7 @@ describe("ProfilesUtils unit tests", () => { configurable: true, }); jest.spyOn(ZoweVsCodeExtension as any, "promptUserPass").mockResolvedValue([]); - await profileUtils.promptCredentials(null); + await profUtils.ProfilesUtils.promptCredentials(null); expect(Gui.showMessage).toHaveBeenCalledWith("Operation Cancelled"); }); @@ -275,7 +275,7 @@ describe("ProfilesUtils unit tests", () => { configurable: true, }); jest.spyOn(Profiles.prototype, "promptCredentials").mockResolvedValue(["some_user", "some_pass", "c29tZV9iYXNlNjRfc3RyaW5n"]); - await profileUtils.promptCredentials(null); + await profUtils.ProfilesUtils.promptCredentials(null); expect(Gui.showMessage).toHaveBeenCalledWith("Credentials for testConfig were successfully updated"); }); @@ -303,7 +303,7 @@ describe("ProfilesUtils unit tests", () => { value: jest.fn(), configurable: true, }); - await profileUtils.promptCredentials(null); + await profUtils.ProfilesUtils.promptCredentials(null); expect(mockProfileInstance.getProfileInfo).toHaveBeenCalled(); expect(Gui.showMessage).toHaveBeenCalledWith('"Update Credentials" operation not supported when "autoStore" is false'); }); @@ -313,7 +313,7 @@ describe("ProfilesUtils unit tests", () => { it("should create directories and files that do not exist", async () => { const blockMocks = createBlockMocks(); blockMocks.mockExistsSync.mockReturnValue(false); - await profileUtils.initializeZoweFolder(); + await profUtils.ProfilesUtils.initializeZoweFolder(); expect(blockMocks.mockMkdirSync).toHaveBeenCalledTimes(2); expect(blockMocks.mockWriteFileSync).toHaveBeenCalledTimes(1); }); @@ -321,7 +321,7 @@ describe("ProfilesUtils unit tests", () => { it("should skip creating directories and files that already exist", async () => { const blockMocks = createBlockMocks(); blockMocks.mockExistsSync.mockReturnValue(true); - await profileUtils.initializeZoweFolder(); + await profUtils.ProfilesUtils.initializeZoweFolder(); expect(blockMocks.mockMkdirSync).toHaveBeenCalledTimes(0); expect(blockMocks.mockWriteFileSync).toHaveBeenCalledTimes(0); }); @@ -333,7 +333,7 @@ describe("ProfilesUtils unit tests", () => { const fileJson = { overrides: { CredentialManager: "@zowe/cli", testValue: true } }; const content = JSON.stringify(fileJson, null, 2); blockMocks.mockReadFileSync.mockReturnValueOnce(JSON.stringify({ overrides: { CredentialManager: false, testValue: true } }, null, 2)); - profileUtils.writeOverridesFile(); + profUtils.ProfilesUtils.writeOverridesFile(); expect(blockMocks.mockOpenSync).toBeCalledWith(blockMocks.zoweDir, "r+"); expect(blockMocks.mockWriteFileSync).toBeCalledWith(blockMocks.fileHandle, content, "utf-8"); }); @@ -344,7 +344,7 @@ describe("ProfilesUtils unit tests", () => { test: null, }; blockMocks.mockReadFileSync.mockReturnValueOnce(JSON.stringify(fileJson, null, 2)); - profileUtils.writeOverridesFile(); + profUtils.ProfilesUtils.writeOverridesFile(); expect(blockMocks.mockWriteFileSync).toBeCalledTimes(0); }); @@ -357,7 +357,7 @@ describe("ProfilesUtils unit tests", () => { return blockMocks.fileHandle; }); const content = JSON.stringify(blockMocks.mockFileRead, null, 2); - profileUtils.writeOverridesFile(); + profUtils.ProfilesUtils.writeOverridesFile(); expect(blockMocks.mockWriteFileSync).toBeCalledWith(blockMocks.fileHandle, content, "utf-8"); expect(blockMocks.mockOpenSync).toBeCalledTimes(2); expect(blockMocks.mockReadFileSync).toBeCalledTimes(0); @@ -369,15 +369,15 @@ describe("ProfilesUtils unit tests", () => { test: null, }; blockMocks.mockReadFileSync.mockReturnValueOnce(JSON.stringify(fileJson, null, 2).slice(1)); - expect(profileUtils.writeOverridesFile).toThrow(); + expect(profUtils.ProfilesUtils.writeOverridesFile).toThrow(); }); }); describe("initializeZoweProfiles", () => { it("should successfully initialize Zowe folder and read config from disk", async () => { - const initZoweFolderSpy = jest.spyOn(profileUtils, "initializeZoweFolder"); - const readConfigFromDiskSpy = jest.spyOn(profileUtils, "readConfigFromDisk").mockResolvedValueOnce(); - await profileUtils.initializeZoweProfiles(); + const initZoweFolderSpy = jest.spyOn(profUtils.ProfilesUtils, "initializeZoweFolder"); + const readConfigFromDiskSpy = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockResolvedValueOnce(); + await profUtils.ProfilesUtils.initializeZoweProfiles(); expect(initZoweFolderSpy).toHaveBeenCalledTimes(1); expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1); expect(ZoweLogger.error).not.toHaveBeenCalled(); @@ -385,9 +385,9 @@ describe("ProfilesUtils unit tests", () => { it("should handle error thrown on initialize Zowe folder", async () => { const testError = new Error("initializeZoweFolder failed"); - const initZoweFolderSpy = jest.spyOn(profileUtils, "initializeZoweFolder").mockRejectedValueOnce(testError); - const readConfigFromDiskSpy = jest.spyOn(profileUtils, "readConfigFromDisk").mockResolvedValueOnce(); - await profileUtils.initializeZoweProfiles(); + const initZoweFolderSpy = jest.spyOn(profUtils.ProfilesUtils, "initializeZoweFolder").mockRejectedValueOnce(testError); + const readConfigFromDiskSpy = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockResolvedValueOnce(); + await profUtils.ProfilesUtils.initializeZoweProfiles(); expect(initZoweFolderSpy).toHaveBeenCalledTimes(1); expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1); expect(Gui.errorMessage).toHaveBeenCalledWith(expect.stringContaining(testError.message)); @@ -395,9 +395,9 @@ describe("ProfilesUtils unit tests", () => { it("should handle Imperative error thrown on read config from disk", async () => { const testError = new zowe.imperative.ImperativeError({ msg: "readConfigFromDisk failed" }); - const initZoweFolderSpy = jest.spyOn(profileUtils, "initializeZoweFolder").mockResolvedValueOnce(); - const readConfigFromDiskSpy = jest.spyOn(profileUtils, "readConfigFromDisk").mockRejectedValueOnce(testError); - await profileUtils.initializeZoweProfiles(); + const initZoweFolderSpy = jest.spyOn(profUtils.ProfilesUtils, "initializeZoweFolder").mockResolvedValueOnce(); + const readConfigFromDiskSpy = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockRejectedValueOnce(testError); + await profUtils.ProfilesUtils.initializeZoweProfiles(); expect(initZoweFolderSpy).toHaveBeenCalledTimes(1); expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1); expect(Gui.errorMessage).toHaveBeenCalledWith(expect.stringContaining(testError.message)); @@ -405,10 +405,10 @@ describe("ProfilesUtils unit tests", () => { it("should handle JSON parse error thrown on read config from disk", async () => { const testError = new Error("readConfigFromDisk failed"); - const initZoweFolderSpy = jest.spyOn(profileUtils, "initializeZoweFolder").mockResolvedValueOnce(); - const readConfigFromDiskSpy = jest.spyOn(profileUtils, "readConfigFromDisk").mockRejectedValueOnce(testError); + const initZoweFolderSpy = jest.spyOn(profUtils.ProfilesUtils, "initializeZoweFolder").mockResolvedValueOnce(); + const readConfigFromDiskSpy = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockRejectedValueOnce(testError); const showZoweConfigErrorSpy = jest.spyOn(ZoweExplorerExtender, "showZoweConfigError").mockReturnValueOnce(); - await profileUtils.initializeZoweProfiles(); + await profUtils.ProfilesUtils.initializeZoweProfiles(); expect(initZoweFolderSpy).toHaveBeenCalledTimes(1); expect(readConfigFromDiskSpy).toHaveBeenCalledTimes(1); expect(showZoweConfigErrorSpy).toHaveBeenCalledWith(testError.message); @@ -416,7 +416,7 @@ describe("ProfilesUtils unit tests", () => { }); it("filterItem should get label if the filterItem icon exists", () => { - const testFilterItem = new profileUtils.FilterItem({ + const testFilterItem = new profUtils.FilterItem({ icon: "test", } as any); expect(testFilterItem.label).toEqual("test undefined"); diff --git a/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json b/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json index 3956eb5866..3d23826597 100644 --- a/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/utils/ProfilesUtils.i18n.json @@ -5,7 +5,6 @@ "errorHandling.authentication.login": "Log in to Authentication Service", "errorHandling.checkCredentials.button": "Check Credentials", "errorHandling.checkCredentials.cancelled": "Operation Cancelled", - "getProfile.notTreeItem": "Tree Item is not a Zowe Explorer item.", "zowe.promptCredentials.notSupported": "\"Update Credentials\" operation not supported when \"autoStore\" is false", "createNewConnection.option.prompt.profileName.placeholder": "Connection Name", "createNewConnection.option.prompt.profileName": "Enter a name for the connection.", @@ -15,5 +14,6 @@ "writeOverridesFile.jsonParseError": "Failed to parse JSON file {0}:", "initializeZoweFolder.error": "Failed to initialize Zowe folder: {0}", "initializeZoweProfiles.success": "Zowe Profiles initialized successfully.", - "initializeZoweTempFolder.success": "Zowe Temp folder initialized successfully." + "initializeZoweTempFolder.success": "Zowe Temp folder initialized successfully.", + "getProfile.notTreeItem": "Tree Item is not a Zowe Explorer item." } diff --git a/packages/zowe-explorer/src/Profiles.ts b/packages/zowe-explorer/src/Profiles.ts index 9fc9d2f7d4..c010f6eb91 100644 --- a/packages/zowe-explorer/src/Profiles.ts +++ b/packages/zowe-explorer/src/Profiles.ts @@ -30,7 +30,7 @@ import { getFullPath, getZoweDir, } from "@zowe/zowe-explorer-api"; -import { errorHandling, FilterDescriptor, FilterItem, readConfigFromDisk } from "./utils/ProfilesUtils"; +import { errorHandling, FilterDescriptor, FilterItem, ProfilesUtils } from "./utils/ProfilesUtils"; import { ZoweExplorerApiRegister } from "./ZoweExplorerApiRegister"; import { ZoweExplorerExtender } from "./ZoweExplorerExtender"; import * as globals from "./globals"; @@ -852,7 +852,7 @@ export class Profiles extends ProfilesCache { Gui.showMessage(localize("createNewConnection.success", "Profile {0} was created.", newProfileName)); // Trigger a ProfilesCache.createConfigInstance with a fresh Config.load // This shall capture any profiles created (v1 or v2) - await readConfigFromDisk(); + await ProfilesUtils.readConfigFromDisk(); return newProfileName; } catch (error) { await errorHandling(error, profileName); diff --git a/packages/zowe-explorer/src/ZoweExplorerExtender.ts b/packages/zowe-explorer/src/ZoweExplorerExtender.ts index 53ca32833a..12ad86c84c 100644 --- a/packages/zowe-explorer/src/ZoweExplorerExtender.ts +++ b/packages/zowe-explorer/src/ZoweExplorerExtender.ts @@ -30,7 +30,7 @@ import { } from "@zowe/zowe-explorer-api"; import { Profiles } from "./Profiles"; import { ZoweExplorerApiRegister } from "./ZoweExplorerApiRegister"; -import { getProfileInfo, getProfile, errorHandling } from "./utils/ProfilesUtils"; +import { getProfile, ProfilesUtils } from "./utils/ProfilesUtils"; import { ZoweLogger } from "./utils/LoggerUtils"; // Set up localization @@ -165,7 +165,7 @@ export class ZoweExplorerExtender implements ZoweExplorerApi.IApiExplorerExtende */ let usingTeamConfig: boolean; try { - const mProfileInfo = getProfileInfo(globals.ISTHEIA); + const mProfileInfo = ProfilesUtils.getProfileInfo(globals.ISTHEIA); if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0]) { const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath; await mProfileInfo.readProfilesFromDisk({ homeDir: zoweDir, projectDir: getFullPath(rootPath) }); diff --git a/packages/zowe-explorer/src/extension.ts b/packages/zowe-explorer/src/extension.ts index 9a30eb2166..275303264a 100644 --- a/packages/zowe-explorer/src/extension.ts +++ b/packages/zowe-explorer/src/extension.ts @@ -15,7 +15,7 @@ import { getZoweDir } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "./ZoweExplorerApiRegister"; import { ZoweExplorerExtender } from "./ZoweExplorerExtender"; import { Profiles } from "./Profiles"; -import { initializeZoweProfiles, initializeZoweTempFolder } from "./utils/ProfilesUtils"; +import { ProfilesUtils } from "./utils/ProfilesUtils"; import { initializeSpoolProvider } from "./SpoolProvider"; import { cleanTempDir, hideTempFolder } from "./utils/TempFolder"; import { SettingsConfig } from "./utils/SettingsConfig"; @@ -41,8 +41,8 @@ export async function activate(context: vscode.ExtensionContext): Promise { await globals.setGlobalSecurityValue(); - writeOverridesFile(); + ProfilesUtils.writeOverridesFile(); }) ); context.subscriptions.push( vscode.commands.registerCommand("zowe.promptCredentials", async (node: IZoweTreeNode) => { - await promptCredentials(node); + await ProfilesUtils.promptCredentials(node); }) ); diff --git a/packages/zowe-explorer/src/utils/ProfilesUtils.ts b/packages/zowe-explorer/src/utils/ProfilesUtils.ts index 97ceca6dda..f77a443da9 100644 --- a/packages/zowe-explorer/src/utils/ProfilesUtils.ts +++ b/packages/zowe-explorer/src/utils/ProfilesUtils.ts @@ -207,216 +207,218 @@ export class FilterDescriptor implements vscode.QuickPickItem { } } -/** - * Function to update the node profile information - */ -export function setProfile(node: IZoweTreeNode, profile: imperative.IProfile): void { - ZoweLogger.trace("ProfilesUtils.setProfile called."); - node.getProfile().profile = profile; -} +export class ProfilesUtils { + public static getProfileInfo(envTheia: boolean): imperative.ProfileInfo { + ZoweLogger.trace("ProfilesUtils.getProfileInfo called."); + const mProfileInfo = new imperative.ProfileInfo("zowe", { + requireKeytar: () => getSecurityModules("keytar", envTheia), + }); + return mProfileInfo; + } -/** - * Function to update the node session information - */ -export function setSession(node: IZoweTreeNode, combinedSessionProfile: imperative.IProfile): void { - ZoweLogger.trace("ProfilesUtils.setSession called."); - const sessionNode = node.getSession(); - for (const prop of Object.keys(combinedSessionProfile)) { - if (prop === "host") { - sessionNode.ISession.hostname = combinedSessionProfile[prop]; + public static async readConfigFromDisk(): Promise { + ZoweLogger.trace("ProfilesUtils.readConfigFromDisk called."); + let rootPath: string; + const mProfileInfo = ProfilesUtils.getProfileInfo(globals.ISTHEIA); + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0]) { + rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath; + await mProfileInfo.readProfilesFromDisk({ homeDir: getZoweDir(), projectDir: getFullPath(rootPath) }); } else { - sessionNode.ISession[prop] = combinedSessionProfile[prop]; + await mProfileInfo.readProfilesFromDisk({ homeDir: getZoweDir(), projectDir: undefined }); + } + if (mProfileInfo.usingTeamConfig) { + globals.setConfigPath(rootPath); + ZoweLogger.info(`Zowe Explorer is using the team configuration file "${mProfileInfo.getTeamConfig().configName}"`); + const layers = mProfileInfo.getTeamConfig().layers || []; + const layerSummary = layers.map( + (config: imperative.IConfigLayer) => + `Path: ${config.path}: ${ + config.exists + ? "Found, with the following defaults:" + JSON.stringify(config.properties?.defaults || "Undefined default") + : "Not available" + } ` + ); + ZoweLogger.debug(`Summary of team configuration files considered for Zowe Explorer: ${JSON.stringify(layerSummary)}`); } } -} - -export function getProfileInfo(envTheia: boolean): imperative.ProfileInfo { - ZoweLogger.trace("ProfilesUtils.getProfileInfo called."); - const mProfileInfo = new imperative.ProfileInfo("zowe", { - requireKeytar: () => getSecurityModules("keytar", envTheia), - }); - return mProfileInfo; -} - -export function getProfile(node: vscode.TreeItem | ZoweTreeNode): imperative.IProfileLoaded { - ZoweLogger.trace("ProfilesUtils.getProfile called."); - if (node instanceof ZoweTreeNode) { - return node.getProfile(); - } - throw new Error(localize("getProfile.notTreeItem", "Tree Item is not a Zowe Explorer item.")); -} - -export async function readConfigFromDisk(): Promise { - ZoweLogger.trace("ProfilesUtils.readConfigFromDisk called."); - let rootPath: string; - const mProfileInfo = await getProfileInfo(globals.ISTHEIA); - if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0]) { - rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath; - await mProfileInfo.readProfilesFromDisk({ homeDir: getZoweDir(), projectDir: getFullPath(rootPath) }); - } else { - await mProfileInfo.readProfilesFromDisk({ homeDir: getZoweDir(), projectDir: undefined }); - } - if (mProfileInfo.usingTeamConfig) { - globals.setConfigPath(rootPath); - ZoweLogger.info(`Zowe Explorer is using the team configuration file "${mProfileInfo.getTeamConfig().configName}"`); - const layers = mProfileInfo.getTeamConfig().layers || []; - const layerSummary = layers.map( - (config: imperative.IConfigLayer) => - `Path: ${config.path}: ${ - config.exists - ? "Found, with the following defaults:" + JSON.stringify(config.properties?.defaults || "Undefined default") - : "Not available" - } ` - ); - ZoweLogger.debug(`Summary of team configuration files considered for Zowe Explorer: ${JSON.stringify(layerSummary)}`); - } -} - -export async function promptCredentials(node: IZoweTreeNode): Promise { - ZoweLogger.trace("ProfilesUtils.promptCredentials called."); - const mProfileInfo = await Profiles.getInstance().getProfileInfo(); - if (mProfileInfo.usingTeamConfig && !mProfileInfo.getTeamConfig().properties.autoStore) { - const msg = localize("zowe.promptCredentials.notSupported", '"Update Credentials" operation not supported when "autoStore" is false'); - ZoweLogger.warn(msg); - Gui.showMessage(msg); - return; - } - let profile: string | imperative.IProfileLoaded = node?.getProfile(); - if (profile == null) { - // prompt for profile - profile = ( - await Gui.showInputBox({ - placeHolder: localize("createNewConnection.option.prompt.profileName.placeholder", "Connection Name"), - prompt: localize("createNewConnection.option.prompt.profileName", "Enter a name for the connection."), - ignoreFocusOut: true, - }) - ).trim(); - if (!profile) { - Gui.showMessage(localize("createNewConnection.undefined.passWord", "Operation Cancelled")); + public static async promptCredentials(node: IZoweTreeNode): Promise { + ZoweLogger.trace("ProfilesUtils.promptCredentials called."); + const mProfileInfo = await Profiles.getInstance().getProfileInfo(); + if (mProfileInfo.usingTeamConfig && !mProfileInfo.getTeamConfig().properties.autoStore) { + const msg = localize("zowe.promptCredentials.notSupported", '"Update Credentials" operation not supported when "autoStore" is false'); + ZoweLogger.warn(msg); + Gui.showMessage(msg); return; } - } + let profile: string | imperative.IProfileLoaded = node?.getProfile(); + if (profile == null) { + // prompt for profile + profile = ( + await Gui.showInputBox({ + placeHolder: localize("createNewConnection.option.prompt.profileName.placeholder", "Connection Name"), + prompt: localize("createNewConnection.option.prompt.profileName", "Enter a name for the connection."), + ignoreFocusOut: true, + }) + ).trim(); - const creds = await Profiles.getInstance().promptCredentials(profile, true); + if (!profile) { + Gui.showMessage(localize("createNewConnection.undefined.passWord", "Operation Cancelled")); + return; + } + } - if (creds != null) { - const successMsg = localize( - "promptCredentials.updatedCredentials", - "Credentials for {0} were successfully updated", - typeof profile === "string" ? profile : profile.name - ); - ZoweLogger.info(successMsg); - Gui.showMessage(successMsg); - } -} + const creds = await Profiles.getInstance().promptCredentials(profile, true); -export async function initializeZoweFolder(): Promise { - ZoweLogger.trace("ProfilesUtils.initializeZoweFolder called."); - // ensure the Secure Credentials Enabled value is read - // set globals.PROFILE_SECURITY value accordingly - await globals.setGlobalSecurityValue(); - // Ensure that ~/.zowe folder exists - // Ensure that the ~/.zowe/settings/imperative.json exists - // TODO: update code below once this imperative issue is resolved. - // https://github.com/zowe/imperative/issues/840 - const zoweDir = getZoweDir(); - if (!fs.existsSync(zoweDir)) { - fs.mkdirSync(zoweDir); - } - const settingsPath = path.join(zoweDir, "settings"); - if (!fs.existsSync(settingsPath)) { - fs.mkdirSync(settingsPath); - } - if (!fs.existsSync(path.join(settingsPath, "imperative.json"))) { - writeOverridesFile(); - } - // If not using team config, ensure that the ~/.zowe/profiles directory - // exists with appropriate types within - if (!imperative.ImperativeConfig.instance.config?.exists) { - await imperative.CliProfileManager.initialize({ - configuration: getImperativeConfig().profiles, - profileRootDirectory: path.join(zoweDir, "profiles"), - }); + if (creds != null) { + const successMsg = localize( + "promptCredentials.updatedCredentials", + "Credentials for {0} were successfully updated", + typeof profile === "string" ? profile : profile.name + ); + ZoweLogger.info(successMsg); + Gui.showMessage(successMsg); + } } - ZoweLogger.info(localize("initializeZoweFolder.location", "Zowe home directory is located at {0}", zoweDir)); -} -export function writeOverridesFile(): void { - ZoweLogger.trace("ProfilesUtils.writeOverridesFile called."); - let fd: number; - let fileContent: string; - const settingsFile = path.join(getZoweDir(), "settings", "imperative.json"); - try { - fd = fs.openSync(settingsFile, "r+"); - fileContent = fs.readFileSync(fd, "utf-8"); - } catch { - // If reading the file failed because it does not exist, then create it - // This should never fail, unless file system is read-only or the file - // was created by another process after first openSync call - fd = fs.openSync(settingsFile, "wx"); + public static async initializeZoweFolder(): Promise { + ZoweLogger.trace("ProfilesUtils.initializeZoweFolder called."); + // ensure the Secure Credentials Enabled value is read + // set globals.PROFILE_SECURITY value accordingly + await globals.setGlobalSecurityValue(); + // Ensure that ~/.zowe folder exists + // Ensure that the ~/.zowe/settings/imperative.json exists + // TODO: update code below once this imperative issue is resolved. + // https://github.com/zowe/imperative/issues/840 + const zoweDir = getZoweDir(); + if (!fs.existsSync(zoweDir)) { + fs.mkdirSync(zoweDir); + } + const settingsPath = path.join(zoweDir, "settings"); + if (!fs.existsSync(settingsPath)) { + fs.mkdirSync(settingsPath); + } + if (!fs.existsSync(path.join(settingsPath, "imperative.json"))) { + ProfilesUtils.writeOverridesFile(); + } + // If not using team config, ensure that the ~/.zowe/profiles directory + // exists with appropriate types within + if (!imperative.ImperativeConfig.instance.config?.exists) { + await imperative.CliProfileManager.initialize({ + configuration: getImperativeConfig().profiles, + profileRootDirectory: path.join(zoweDir, "profiles"), + }); + } + ZoweLogger.info(localize("initializeZoweFolder.location", "Zowe home directory is located at {0}", zoweDir)); } - try { - let settings: any; - if (fileContent) { - try { - settings = JSON.parse(fileContent); - } catch (err) { - if (err instanceof Error) { - throw new Error( - localize("writeOverridesFile.jsonParseError", "Failed to parse JSON file {0}:", settingsFile) + " " + err.message - ); + + public static writeOverridesFile(): void { + ZoweLogger.trace("ProfilesUtils.writeOverridesFile called."); + let fd: number; + let fileContent: string; + const settingsFile = path.join(getZoweDir(), "settings", "imperative.json"); + try { + fd = fs.openSync(settingsFile, "r+"); + fileContent = fs.readFileSync(fd, "utf-8"); + } catch { + // If reading the file failed because it does not exist, then create it + // This should never fail, unless file system is read-only or the file + // was created by another process after first openSync call + fd = fs.openSync(settingsFile, "wx"); + } + try { + let settings: any; + if (fileContent) { + try { + settings = JSON.parse(fileContent); + } catch (err) { + if (err instanceof Error) { + throw new Error( + localize("writeOverridesFile.jsonParseError", "Failed to parse JSON file {0}:", settingsFile) + " " + err.message + ); + } + } + if (settings && settings?.overrides && settings?.overrides?.CredentialManager !== globals.PROFILE_SECURITY) { + settings.overrides.CredentialManager = globals.PROFILE_SECURITY; + } else { + return; } - } - if (settings && settings?.overrides && settings?.overrides?.CredentialManager !== globals.PROFILE_SECURITY) { - settings.overrides.CredentialManager = globals.PROFILE_SECURITY; } else { - return; + settings = { overrides: { CredentialManager: globals.PROFILE_SECURITY } }; } - } else { - settings = { overrides: { CredentialManager: globals.PROFILE_SECURITY } }; + fileContent = JSON.stringify(settings, null, 2); + fs.writeFileSync(fd, fileContent, "utf-8"); + } finally { + fs.closeSync(fd); } - fileContent = JSON.stringify(settings, null, 2); - fs.writeFileSync(fd, fileContent, "utf-8"); - } finally { - fs.closeSync(fd); } -} -export async function initializeZoweProfiles(): Promise { - ZoweLogger.trace("ProfilesUtils.initializeZoweProfiles called."); - try { - await this.initializeZoweFolder(); - } catch (err) { - ZoweLogger.error(err); - Gui.errorMessage(localize("initializeZoweFolder.error", "Failed to initialize Zowe folder: {0}", err.message)); + public static async initializeZoweProfiles(): Promise { + ZoweLogger.trace("ProfilesUtils.initializeZoweProfiles called."); + try { + await ProfilesUtils.initializeZoweFolder(); + } catch (err) { + ZoweLogger.error(err); + Gui.errorMessage(localize("initializeZoweFolder.error", "Failed to initialize Zowe folder: {0}", err.message)); + } + + try { + await ProfilesUtils.readConfigFromDisk(); + ZoweLogger.info(localize("initializeZoweProfiles.success", "Zowe Profiles initialized successfully.")); + } catch (err) { + if (err instanceof imperative.ImperativeError) { + errorHandling(err, undefined, err.mDetails.causeErrors); + } else { + ZoweLogger.error(err); + ZoweExplorerExtender.showZoweConfigError(err.message); + } + } } - try { - await this.readConfigFromDisk(); - ZoweLogger.info(localize("initializeZoweProfiles.success", "Zowe Profiles initialized successfully.")); - } catch (err) { - if (err instanceof imperative.ImperativeError) { - errorHandling(err, undefined, err.mDetails.causeErrors); - } else { + public static initializeZoweTempFolder(): void { + ZoweLogger.trace("ProfilesUtils.initializeZoweTempFolder called."); + try { + if (!fs.existsSync(globals.ZOWETEMPFOLDER)) { + fs.mkdirSync(globals.ZOWETEMPFOLDER); + fs.mkdirSync(globals.ZOWE_TMP_FOLDER); + fs.mkdirSync(globals.USS_DIR); + fs.mkdirSync(globals.DS_DIR); + ZoweLogger.info(localize("initializeZoweTempFolder.success", "Zowe Temp folder initialized successfully.")); + } + } catch (err) { ZoweLogger.error(err); ZoweExplorerExtender.showZoweConfigError(err.message); } } } -export function initializeZoweTempFolder(): void { - ZoweLogger.trace("ProfilesUtils.initializeZoweTempFolder called."); - try { - if (!fs.existsSync(globals.ZOWETEMPFOLDER)) { - fs.mkdirSync(globals.ZOWETEMPFOLDER); - fs.mkdirSync(globals.ZOWE_TMP_FOLDER); - fs.mkdirSync(globals.USS_DIR); - fs.mkdirSync(globals.DS_DIR); - ZoweLogger.info(localize("initializeZoweTempFolder.success", "Zowe Temp folder initialized successfully.")); +/** + * Function to update the node profile information + */ +export function setProfile(node: IZoweTreeNode, profile: imperative.IProfile): void { + ZoweLogger.trace("ProfilesUtils.setProfile called."); + node.getProfile().profile = profile; +} + +/** + * Function to update the node session information + */ +export function setSession(node: IZoweTreeNode, combinedSessionProfile: imperative.IProfile): void { + ZoweLogger.trace("ProfilesUtils.setSession called."); + const sessionNode = node.getSession(); + for (const prop of Object.keys(combinedSessionProfile)) { + if (prop === "host") { + sessionNode.ISession.hostname = combinedSessionProfile[prop]; + } else { + sessionNode.ISession[prop] = combinedSessionProfile[prop]; } - } catch (err) { - ZoweLogger.error(err); - ZoweExplorerExtender.showZoweConfigError(err.message); } } + +export function getProfile(node: vscode.TreeItem | ZoweTreeNode): imperative.IProfileLoaded { + ZoweLogger.trace("ProfilesUtils.getProfile called."); + if (node instanceof ZoweTreeNode) { + return node.getProfile(); + } + throw new Error(localize("getProfile.notTreeItem", "Tree Item is not a Zowe Explorer item.")); +} From 421bdaf4351550545b8ae54c64a933aec681976f Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 11 Apr 2023 13:12:14 -0400 Subject: [PATCH 5/6] Remove unnecessary usage of this keyword Signed-off-by: Timothy Johnson --- packages/zowe-explorer/src/globals.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 9075ebb70a..38bd8824ce 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -33,7 +33,7 @@ export let ZOWE_TMP_FOLDER: string; export let USS_DIR: string; export let DS_DIR: string; export let CONFIG_PATH; // set during activate -export let ISTHEIA: boolean = false; // set during activate +export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; export const COMMAND_COUNT = 102; export const MAX_SEARCH_HISTORY = 5; @@ -286,7 +286,7 @@ export function defineGlobals(tempPath: string | undefined): void { ((appName && appName.toLowerCase().includes("theia")) || (uriScheme && uriScheme.toLowerCase().includes("theia"))) && vscode.env.uiKind === vscode.UIKind.Web ) { - this.ISTHEIA = true; + ISTHEIA = true; ZoweLogger.info(localize("globals.defineGlobals.isTheia", "Zowe Explorer is running in Theia environment.")); } @@ -319,7 +319,7 @@ export function initLogger(context: vscode.ExtensionContext): string { ); } imperative.Logger.initLogger(loggerConfig); - this.LOG = imperative.Logger.getAppLogger(); + LOG = imperative.Logger.getAppLogger(); return loggerConfig.log4jsConfig.appenders.app.filename; } From e615b823629f02fc6e5029e8869b4d7d014dc5e9 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Mon, 17 Apr 2023 14:19:14 -0400 Subject: [PATCH 6/6] Add unit tests for isConfigSettingSetByUser method Signed-off-by: Timothy Johnson --- .../utils/SettingsConfig.unit.test.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/zowe-explorer/__tests__/__unit__/utils/SettingsConfig.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/utils/SettingsConfig.unit.test.ts index 8dc84aa518..d6d0c3506c 100644 --- a/packages/zowe-explorer/__tests__/__unit__/utils/SettingsConfig.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/utils/SettingsConfig.unit.test.ts @@ -23,6 +23,35 @@ afterEach(() => { jest.resetAllMocks(); }); +describe("SettingsConfig Unit Tests - function isConfigSettingSetByUser", () => { + it("should return false if setting is undefined or empty", () => { + const falseCases = [undefined, {}]; + for (const retVal of falseCases) { + jest.spyOn(vscode.workspace, "getConfiguration").mockReturnValueOnce({ + inspect: jest.fn().mockReturnValue(retVal), + } as any); + expect(SettingsConfig.isConfigSettingSetByUser("zowe.setting")).toBe(false); + } + }); + + it("should return true if setting is defined in any scope", () => { + const trueCases = [ + { globalValue: "a" }, + { workspaceValue: "b" }, + { workspaceFolderValue: "c" }, + { globalLanguageValue: "d" }, + { workspaceLanguageValue: "e" }, + { workspaceFolderLanguageValue: "f" }, + ]; + for (const retVal of trueCases) { + jest.spyOn(vscode.workspace, "getConfiguration").mockReturnValueOnce({ + inspect: jest.fn().mockReturnValue(retVal), + } as any); + expect(SettingsConfig.isConfigSettingSetByUser("zowe.setting")).toBe(true); + } + }); +}); + describe("SettingsConfig Unit Tests - function promptReload", () => { it("should trigger a reload when prompted", async () => { const privateSettingsConfig = SettingsConfig as any;