From d9e970332a14c9c50ae4af037784d2b39619e63a Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Thu, 26 Sep 2024 08:24:59 -0400 Subject: [PATCH 01/25] Temporarily disable ON_VAULT_CHANGED event to prevent double refresh Signed-off-by: Timothy Johnson --- .../src/fs/types/abstract.ts | 1 - .../src/trees/shared/SharedInit.ts | 22 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/zowe-explorer-api/src/fs/types/abstract.ts b/packages/zowe-explorer-api/src/fs/types/abstract.ts index f29305bc6b..b26c64ba59 100644 --- a/packages/zowe-explorer-api/src/fs/types/abstract.ts +++ b/packages/zowe-explorer-api/src/fs/types/abstract.ts @@ -68,7 +68,6 @@ export type ConflictData = { export interface IFileSystemEntry extends vscode.FileStat { name: string; metadata: EntryMetadata; - type: vscode.FileType; wasAccessed: boolean; data?: Uint8Array; } diff --git a/packages/zowe-explorer/src/trees/shared/SharedInit.ts b/packages/zowe-explorer/src/trees/shared/SharedInit.ts index b45dd82a61..237f588ea2 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedInit.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedInit.ts @@ -321,17 +321,17 @@ export class SharedInit { }); }); - try { - const zoweWatcher = imperative.EventOperator.getWatcher().subscribeUser(imperative.ZoweUserEvents.ON_VAULT_CHANGED, async () => { - ZoweLogger.info(vscode.l10n.t("Changes in the credential vault detected, refreshing Zowe Explorer.")); - await ProfilesUtils.readConfigFromDisk(); - await SharedActions.refreshAll(); - ZoweExplorerApiRegister.getInstance().onVaultUpdateEmitter.fire(Validation.EventType.UPDATE); - }); - context.subscriptions.push(new vscode.Disposable(zoweWatcher.close.bind(zoweWatcher))); - } catch (err) { - Gui.errorMessage("Unable to watch for vault changes. " + JSON.stringify(err)); - } + // try { + // const zoweWatcher = imperative.EventOperator.getWatcher().subscribeUser(imperative.ZoweUserEvents.ON_VAULT_CHANGED, async () => { + // ZoweLogger.info(vscode.l10n.t("Changes in the credential vault detected, refreshing Zowe Explorer.")); + // await ProfilesUtils.readConfigFromDisk(); + // await SharedActions.refreshAll(); + // ZoweExplorerApiRegister.getInstance().onVaultUpdateEmitter.fire(Validation.EventType.UPDATE); + // }); + // context.subscriptions.push(new vscode.Disposable(zoweWatcher.close.bind(zoweWatcher))); + // } catch (err) { + // Gui.errorMessage("Unable to watch for vault changes. " + JSON.stringify(err)); + // } try { const zoweWatcher = imperative.EventOperator.getWatcher().subscribeShared( From b70c0ab36287a6d44f0e2c86e8286cdc02d6c739 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Thu, 26 Sep 2024 10:59:38 -0400 Subject: [PATCH 02/25] Propagate profile updates to child nodes Signed-off-by: Timothy Johnson --- packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts index 7c03c4c2c2..18f3fffc2b 100644 --- a/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts @@ -111,6 +111,9 @@ export class ZoweTreeNode extends vscode.TreeItem { if (fsEntry != null) { fsEntry.metadata.profile = aProfile; } + for (const child of this.children) { + (child as unknown as ZoweTreeNode).setProfileToChoice(aProfile, fsProvider); + } } /** * Sets the session for this node to the one chosen in parameters. From bbdb79fe471631b3ffabcf8ed07d02f85f41618b Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Fri, 27 Sep 2024 08:12:24 -0400 Subject: [PATCH 03/25] Skip vault change events triggered by ZE Signed-off-by: Timothy Johnson --- .../src/configuration/Constants.ts | 1 + .../src/trees/shared/SharedInit.ts | 35 +++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/zowe-explorer/src/configuration/Constants.ts b/packages/zowe-explorer/src/configuration/Constants.ts index f2643a3504..493ba07376 100644 --- a/packages/zowe-explorer/src/configuration/Constants.ts +++ b/packages/zowe-explorer/src/configuration/Constants.ts @@ -83,6 +83,7 @@ export class Constants { public static MEMBER_NAME_REGEX_CHECK = /^[a-zA-Z#@$][a-zA-Z0-9#@$]{0,7}$/; public static ACTIVATED = false; public static SAVED_PROFILE_CONTENTS = new Uint8Array(); + public static IGNORE_VAULT_CHANGE = false; public static readonly JOBS_MAX_PREFIX = 8; public static PROFILES_CACHE: Profiles; public static readonly WORKSPACE_UTIL_TAB_SWITCH_DELAY = 200; diff --git a/packages/zowe-explorer/src/trees/shared/SharedInit.ts b/packages/zowe-explorer/src/trees/shared/SharedInit.ts index 237f588ea2..cc1f9fec43 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedInit.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedInit.ts @@ -321,17 +321,30 @@ export class SharedInit { }); }); - // try { - // const zoweWatcher = imperative.EventOperator.getWatcher().subscribeUser(imperative.ZoweUserEvents.ON_VAULT_CHANGED, async () => { - // ZoweLogger.info(vscode.l10n.t("Changes in the credential vault detected, refreshing Zowe Explorer.")); - // await ProfilesUtils.readConfigFromDisk(); - // await SharedActions.refreshAll(); - // ZoweExplorerApiRegister.getInstance().onVaultUpdateEmitter.fire(Validation.EventType.UPDATE); - // }); - // context.subscriptions.push(new vscode.Disposable(zoweWatcher.close.bind(zoweWatcher))); - // } catch (err) { - // Gui.errorMessage("Unable to watch for vault changes. " + JSON.stringify(err)); - // } + try { + // TODO Workaround to skip ON_VAULT_CHANGED events triggered by ZE and not by external app + // Remove this hack once https://github.com/zowe/zowe-cli/issues/2279 is implemented + const oldEmitZoweEvent = (imperative.EventProcessor.prototype as any).emitZoweEvent; + (imperative.EventProcessor.prototype as any).emitZoweEvent = function (eventName: string): void { + if (eventName === imperative.ZoweUserEvents.ON_VAULT_CHANGED) { + Constants.IGNORE_VAULT_CHANGE = true; + } + oldEmitZoweEvent.call(this, eventName); + }; + const zoweWatcher = imperative.EventOperator.getWatcher().subscribeUser(imperative.ZoweUserEvents.ON_VAULT_CHANGED, async () => { + if (Constants.IGNORE_VAULT_CHANGE) { + Constants.IGNORE_VAULT_CHANGE = false; + return; + } + ZoweLogger.info(vscode.l10n.t("Changes in the credential vault detected, refreshing Zowe Explorer.")); + await ProfilesUtils.readConfigFromDisk(); + await SharedActions.refreshAll(); + ZoweExplorerApiRegister.getInstance().onVaultUpdateEmitter.fire(Validation.EventType.UPDATE); + }); + context.subscriptions.push(new vscode.Disposable(zoweWatcher.close.bind(zoweWatcher))); + } catch (err) { + Gui.errorMessage("Unable to watch for vault changes. " + JSON.stringify(err)); + } try { const zoweWatcher = imperative.EventOperator.getWatcher().subscribeShared( From abd2b188896ac58fd494255e9efbc7595d53a942 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Fri, 27 Sep 2024 08:22:54 -0400 Subject: [PATCH 04/25] Update changelog Signed-off-by: Timothy Johnson --- packages/zowe-explorer/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 4843d9baa7..9b656dfc1c 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen ### Bug fixes +- Fixed issue where 401 error could occur when opening PDS members after updating credentials. [#3150](https://github.com/zowe/zowe-explorer-vscode/issues/3150) + ## `3.0.0-next.202409251932` ### New features and enhancements From cdd7d7c39175c5f773a0ea0d996fbf7dbd3805bf Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Fri, 27 Sep 2024 10:12:49 -0400 Subject: [PATCH 05/25] fix(tests): Add EventProcessor stub to imperative module mock Signed-off-by: Trae Yelovich --- packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts b/packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts index d321cfa569..a99ec0a37f 100644 --- a/packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts +++ b/packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts @@ -17,6 +17,8 @@ import * as os from "os"; import * as path from "path"; const log4js = require("log4js"); +export class EventProcessor {} + /** * Constants used for REST client, etc. * @export From b492163e1aba23ad8db8809c1bceba8dd7f851e9 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Fri, 27 Sep 2024 10:56:48 -0400 Subject: [PATCH 06/25] refactor: Move logic into separate functions; tests: add patch coverage Signed-off-by: Trae Yelovich --- .../__unit__/tree/ZoweTreeNode.unit.test.ts | 19 ++++++ .../__tests__/__mocks__/@zowe/imperative.ts | 4 +- .../trees/shared/SharedInit.unit.test.ts | 67 ++++++++++++++++++- .../src/trees/shared/SharedInit.ts | 48 +++++++------ 4 files changed, 116 insertions(+), 22 deletions(-) diff --git a/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts b/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts index fe3ca62227..c5a97b17b6 100644 --- a/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts +++ b/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts @@ -108,4 +108,23 @@ describe("ZoweTreeNode", () => { expect(node.getProfileName()).toBe("newProfile"); expect(fsEntry.metadata.profile.name).toBe("newProfile"); }); + + it("setProfileToChoice should update child nodes with the new profile", () => { + const node = makeNode("test", vscode.TreeItemCollapsibleState.Expanded, undefined); + const nodeChild = makeNode("child", vscode.TreeItemCollapsibleState.None, undefined); + node.children = [nodeChild as any]; + const setProfileToChoiceChildMock = jest.spyOn(nodeChild, "setProfileToChoice").mockImplementation(); + const fsEntry = { + metadata: { + profile: { name: "oldProfile" }, + }, + }; + const mockNewProfile = { name: "newProfile" } as unknown as imperative.IProfileLoaded; + const mockProvider = { + lookup: jest.fn().mockReturnValue(fsEntry), + } as unknown as BaseProvider; + node.setProfileToChoice(mockNewProfile, mockProvider); + expect(node.getProfileName()).toBe("newProfile"); + expect(setProfileToChoiceChildMock).toHaveBeenCalledWith(mockNewProfile, mockProvider); + }); }); diff --git a/packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts b/packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts index a99ec0a37f..23c8570108 100644 --- a/packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts +++ b/packages/zowe-explorer/__tests__/__mocks__/@zowe/imperative.ts @@ -17,7 +17,9 @@ import * as os from "os"; import * as path from "path"; const log4js = require("log4js"); -export class EventProcessor {} +export class EventProcessor { + public emitZoweEvent(eventName: string): void {} +} /** * Constants used for REST client, etc. diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts index 7a54f6aaa8..6eab47aac6 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts @@ -30,6 +30,7 @@ import { Gui, imperative, ZoweScheme } from "@zowe/zowe-explorer-api"; import { MockedProperty } from "../../../__mocks__/mockUtils"; import { DatasetFSProvider } from "../../../../src/trees/dataset/DatasetFSProvider"; import { UssFSProvider } from "../../../../src/trees/uss/UssFSProvider"; +import { ZoweLogger } from "../../../../src/tools/ZoweLogger"; jest.mock("../../../../src/utils/LoggerUtils"); jest.mock("../../../../src/tools/ZoweLogger"); @@ -408,7 +409,7 @@ describe("Test src/shared/extension", () => { const spyRefreshAll = jest.spyOn(SharedActions, "refreshAll").mockImplementation(jest.fn()); // Setup watchers - await SharedInit.watchConfigProfile(context); + SharedInit.watchConfigProfile(context); expect(spyWatcher).toHaveBeenCalled(); expect(spyGuiError).not.toHaveBeenCalled(); @@ -435,7 +436,7 @@ describe("Test src/shared/extension", () => { }); const spyGuiError = jest.spyOn(Gui, "errorMessage"); - await SharedInit.watchConfigProfile(context); + SharedInit.watchConfigProfile(context); expect(spyWatcher).toHaveBeenCalled(); expect(spyGuiError.mock.calls[0][0]).toContain("vault changes"); @@ -443,6 +444,39 @@ describe("Test src/shared/extension", () => { expect(spyGuiError.mock.calls[1][0]).toContain("credential manager changes"); expect(spyGuiError.mock.calls[1][0]).toContain(testError); }); + + it("should replace the function signature for EventProcessor.emitZoweEvent to set Constant.IGNORE_VAULT_CHANGE", async () => { + const emitZoweEventOverride = jest.fn(); + const emitZoweEventMock = new MockedProperty(imperative.EventProcessor.prototype, "emitZoweEvent", { + set: emitZoweEventOverride, + configurable: true, + }); + SharedInit.watchConfigProfile(context); + expect(emitZoweEventOverride).toHaveBeenCalled(); + emitZoweEventMock[Symbol.dispose](); + }); + + it("should replace the function signature for EventProcessor.emitZoweEvent to set Constant.IGNORE_VAULT_CHANGE", async () => { + const emitZoweEventOverride = jest.fn(); + const emitZoweEventMock = new MockedProperty(imperative.EventProcessor.prototype, "emitZoweEvent", { + set: emitZoweEventOverride, + configurable: true, + }); + SharedInit.watchConfigProfile(context); + expect(emitZoweEventOverride).toHaveBeenCalled(); + emitZoweEventMock[Symbol.dispose](); + }); + + it("should subscribe to the ON_VAULT_CHANGED event using EventProcessor.subscribeUser", async () => { + const subscribeUser = jest.fn(); + const getWatcherMock = jest.spyOn(imperative.EventOperator, "getWatcher").mockReturnValue({ + subscribeUser, + } as any); + + SharedInit.watchConfigProfile(context); + expect(getWatcherMock).toHaveBeenCalled(); + expect(subscribeUser).toHaveBeenCalledWith(imperative.ZoweUserEvents.ON_VAULT_CHANGED, SharedInit.onVaultChanged); + }); }); describe("initSubscribers", () => { @@ -518,4 +552,33 @@ describe("Test src/shared/extension", () => { expect(remoteLookupUssSpy).not.toHaveBeenCalled(); }); }); + + describe("emitZoweEventHook", () => { + it("sets Constants.IGNORE_VAULT_CHANGE to true if emitZoweEvent is called and calls the original function", () => { + const originalEmitZoweEvent = new MockedProperty(SharedInit, "originalEmitZoweEvent", undefined, jest.fn()); + SharedInit.emitZoweEventHook(imperative.ZoweUserEvents.ON_VAULT_CHANGED); + expect(Constants.IGNORE_VAULT_CHANGE).toBe(true); + expect(originalEmitZoweEvent.mock).toHaveBeenCalled(); + originalEmitZoweEvent[Symbol.dispose](); + }); + }); + describe("onVaultChanged", () => { + it("resets Constants.IGNORE_VAULT_CHANGE if it is true and returns early", async () => { + const infoSpy = jest.spyOn(ZoweLogger, "info"); + Constants.IGNORE_VAULT_CHANGE = true; + await SharedInit.onVaultChanged(); + expect(Constants.IGNORE_VAULT_CHANGE).toBe(false); + expect(infoSpy).not.toHaveBeenCalled(); + }); + + it("calls SharedActions.refreshAll and ProfilesUtils.readConfigFromDisk on vault change", async () => { + const loggerInfo = jest.spyOn(ZoweLogger, "info").mockImplementation(); + const readCfgFromDisk = jest.spyOn(profUtils.ProfilesUtils, "readConfigFromDisk").mockImplementation(); + const refreshAll = jest.spyOn(SharedActions, "refreshAll").mockImplementation(); + await SharedInit.onVaultChanged(); + expect(loggerInfo).toHaveBeenCalledWith("Changes in the credential vault detected, refreshing Zowe Explorer."); + expect(readCfgFromDisk).toHaveBeenCalled(); + expect(refreshAll).toHaveBeenCalled(); + }); + }); }); diff --git a/packages/zowe-explorer/src/trees/shared/SharedInit.ts b/packages/zowe-explorer/src/trees/shared/SharedInit.ts index cc1f9fec43..767911b304 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedInit.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedInit.ts @@ -48,6 +48,8 @@ import { CertificateWizard } from "../../utils/CertificateWizard"; import { ZosConsoleViewProvider } from "../../zosconsole/ZosConsolePanel"; export class SharedInit { + private static originalEmitZoweEvent: typeof imperative.EventProcessor.prototype.emitEvent; + public static registerCommonCommands(context: vscode.ExtensionContext, providers: Definitions.IZoweProviders): void { ZoweLogger.trace("shared.init.registerCommonCommands called."); @@ -284,6 +286,24 @@ export class SharedInit { } } + public static emitZoweEventHook(this: void, eventName: string): void { + if (eventName === imperative.ZoweUserEvents.ON_VAULT_CHANGED) { + Constants.IGNORE_VAULT_CHANGE = true; + } + SharedInit.originalEmitZoweEvent.call(this, eventName); + } + + public static async onVaultChanged(this: void): Promise { + if (Constants.IGNORE_VAULT_CHANGE) { + Constants.IGNORE_VAULT_CHANGE = false; + return; + } + ZoweLogger.info(vscode.l10n.t("Changes in the credential vault detected, refreshing Zowe Explorer.")); + await ProfilesUtils.readConfigFromDisk(); + await SharedActions.refreshAll(); + ZoweExplorerApiRegister.getInstance().onVaultUpdateEmitter.fire(Validation.EventType.UPDATE); + } + public static watchConfigProfile(context: vscode.ExtensionContext): void { ZoweLogger.trace("shared.init.watchConfigProfile called."); const watchers: vscode.FileSystemWatcher[] = []; @@ -322,25 +342,15 @@ export class SharedInit { }); try { - // TODO Workaround to skip ON_VAULT_CHANGED events triggered by ZE and not by external app - // Remove this hack once https://github.com/zowe/zowe-cli/issues/2279 is implemented - const oldEmitZoweEvent = (imperative.EventProcessor.prototype as any).emitZoweEvent; - (imperative.EventProcessor.prototype as any).emitZoweEvent = function (eventName: string): void { - if (eventName === imperative.ZoweUserEvents.ON_VAULT_CHANGED) { - Constants.IGNORE_VAULT_CHANGE = true; - } - oldEmitZoweEvent.call(this, eventName); - }; - const zoweWatcher = imperative.EventOperator.getWatcher().subscribeUser(imperative.ZoweUserEvents.ON_VAULT_CHANGED, async () => { - if (Constants.IGNORE_VAULT_CHANGE) { - Constants.IGNORE_VAULT_CHANGE = false; - return; - } - ZoweLogger.info(vscode.l10n.t("Changes in the credential vault detected, refreshing Zowe Explorer.")); - await ProfilesUtils.readConfigFromDisk(); - await SharedActions.refreshAll(); - ZoweExplorerApiRegister.getInstance().onVaultUpdateEmitter.fire(Validation.EventType.UPDATE); - }); + // Workaround to skip ON_VAULT_CHANGED events triggered by ZE and not by external app + // TODO: Remove this hack once https://github.com/zowe/zowe-cli/issues/2279 is implemented + SharedInit.originalEmitZoweEvent = (imperative.EventProcessor.prototype as any).emitZoweEvent; + (imperative.EventProcessor.prototype as any).emitZoweEvent = SharedInit.emitZoweEventHook.bind(imperative.EventProcessor.prototype); + + const zoweWatcher = imperative.EventOperator.getWatcher().subscribeUser( + imperative.ZoweUserEvents.ON_VAULT_CHANGED, + SharedInit.onVaultChanged + ); context.subscriptions.push(new vscode.Disposable(zoweWatcher.close.bind(zoweWatcher))); } catch (err) { Gui.errorMessage("Unable to watch for vault changes. " + JSON.stringify(err)); From 6c4f8af6b9faa454725d3ed02219e7bc3d6bee01 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Fri, 27 Sep 2024 11:06:50 -0400 Subject: [PATCH 07/25] chore: update ZE API changelog Signed-off-by: Trae Yelovich --- packages/zowe-explorer-api/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/zowe-explorer-api/CHANGELOG.md b/packages/zowe-explorer-api/CHANGELOG.md index 945ae5230a..fe064a29f0 100644 --- a/packages/zowe-explorer-api/CHANGELOG.md +++ b/packages/zowe-explorer-api/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to the "zowe-explorer-api" extension will be documented in t ### Bug fixes +- Updated the `ZoweTreeNode.setProfileToChoice` function so that it propagates profile changes to its child nodes. [#3150](https://github.com/zowe/zowe-explorer-vscode/issues/3150) + ## `3.0.0-next.202409251932` ### New features and enhancements From fd3179999cb55f3227ecdeddfd1b1c410ab43b05 Mon Sep 17 00:00:00 2001 From: Billie Simmons <49491949+JillieBeanSim@users.noreply.github.com> Date: Mon, 30 Sep 2024 10:22:59 -0400 Subject: [PATCH 08/25] ran prepublish Signed-off-by: Billie Simmons <49491949+JillieBeanSim@users.noreply.github.com> --- packages/zowe-explorer/l10n/bundle.l10n.json | 2 +- packages/zowe-explorer/l10n/poeditor.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/zowe-explorer/l10n/bundle.l10n.json b/packages/zowe-explorer/l10n/bundle.l10n.json index d680aadbae..a693ab3477 100644 --- a/packages/zowe-explorer/l10n/bundle.l10n.json +++ b/packages/zowe-explorer/l10n/bundle.l10n.json @@ -334,10 +334,10 @@ ] }, "Enter a codepage (e.g., 1047, IBM-1047)": "Enter a codepage (e.g., 1047, IBM-1047)", + "Changes in the credential vault detected, refreshing Zowe Explorer.": "Changes in the credential vault detected, refreshing Zowe Explorer.", "Team config file created, refreshing Zowe Explorer.": "Team config file created, refreshing Zowe Explorer.", "Team config file deleted, refreshing Zowe Explorer.": "Team config file deleted, refreshing Zowe Explorer.", "Team config file updated.": "Team config file updated.", - "Changes in the credential vault detected, refreshing Zowe Explorer.": "Changes in the credential vault detected, refreshing Zowe Explorer.", "Changes in credential management detected, refreshing Zowe Explorer.": "Changes in credential management detected, refreshing Zowe Explorer.", "Zowe Explorer has activated successfully.": "Zowe Explorer has activated successfully.", "Type the new pattern to add to history": "Type the new pattern to add to history", diff --git a/packages/zowe-explorer/l10n/poeditor.json b/packages/zowe-explorer/l10n/poeditor.json index 5aa4422076..d346e0f553 100644 --- a/packages/zowe-explorer/l10n/poeditor.json +++ b/packages/zowe-explorer/l10n/poeditor.json @@ -554,10 +554,10 @@ "Choose encoding for {0}": "", "Current encoding is {0}": "", "Enter a codepage (e.g., 1047, IBM-1047)": "", + "Changes in the credential vault detected, refreshing Zowe Explorer.": "", "Team config file created, refreshing Zowe Explorer.": "", "Team config file deleted, refreshing Zowe Explorer.": "", "Team config file updated.": "", - "Changes in the credential vault detected, refreshing Zowe Explorer.": "", "Changes in credential management detected, refreshing Zowe Explorer.": "", "Zowe Explorer has activated successfully.": "", "Type the new pattern to add to history": "", From db860cb4a8c1c285b903b5866ed9be14bc7e62fb Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Mon, 30 Sep 2024 10:49:27 -0400 Subject: [PATCH 09/25] fix: pass processor as function arg instead of bind Signed-off-by: Trae Yelovich --- packages/zowe-explorer/src/trees/shared/SharedInit.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/zowe-explorer/src/trees/shared/SharedInit.ts b/packages/zowe-explorer/src/trees/shared/SharedInit.ts index 767911b304..ac6aff17d4 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedInit.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedInit.ts @@ -286,11 +286,11 @@ export class SharedInit { } } - public static emitZoweEventHook(this: void, eventName: string): void { + public static emitZoweEventHook(this: void, processor: imperative.EventProcessor, eventName: string): void { if (eventName === imperative.ZoweUserEvents.ON_VAULT_CHANGED) { Constants.IGNORE_VAULT_CHANGE = true; } - SharedInit.originalEmitZoweEvent.call(this, eventName); + SharedInit.originalEmitZoweEvent.call(processor, eventName); } public static async onVaultChanged(this: void): Promise { @@ -345,7 +345,9 @@ export class SharedInit { // Workaround to skip ON_VAULT_CHANGED events triggered by ZE and not by external app // TODO: Remove this hack once https://github.com/zowe/zowe-cli/issues/2279 is implemented SharedInit.originalEmitZoweEvent = (imperative.EventProcessor.prototype as any).emitZoweEvent; - (imperative.EventProcessor.prototype as any).emitZoweEvent = SharedInit.emitZoweEventHook.bind(imperative.EventProcessor.prototype); + (imperative.EventProcessor.prototype as any).emitZoweEvent = function (eventName: string): void { + SharedInit.emitZoweEventHook(this, eventName); + }; const zoweWatcher = imperative.EventOperator.getWatcher().subscribeUser( imperative.ZoweUserEvents.ON_VAULT_CHANGED, From 7cb58d1fe9c772050857b0d4a5d13b1cf1755b2a Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Mon, 30 Sep 2024 10:57:58 -0400 Subject: [PATCH 10/25] fix(tests): Pass fake event processor to hook function Signed-off-by: Trae Yelovich --- .../__tests__/__unit__/trees/shared/SharedInit.unit.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts index 6eab47aac6..ecd155e9f1 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts @@ -556,7 +556,7 @@ describe("Test src/shared/extension", () => { describe("emitZoweEventHook", () => { it("sets Constants.IGNORE_VAULT_CHANGE to true if emitZoweEvent is called and calls the original function", () => { const originalEmitZoweEvent = new MockedProperty(SharedInit, "originalEmitZoweEvent", undefined, jest.fn()); - SharedInit.emitZoweEventHook(imperative.ZoweUserEvents.ON_VAULT_CHANGED); + SharedInit.emitZoweEventHook({} as any, imperative.ZoweUserEvents.ON_VAULT_CHANGED); expect(Constants.IGNORE_VAULT_CHANGE).toBe(true); expect(originalEmitZoweEvent.mock).toHaveBeenCalled(); originalEmitZoweEvent[Symbol.dispose](); From 509abc572195784bb7a28ba02b4003d05c08d4ee Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Mon, 30 Sep 2024 17:03:32 -0400 Subject: [PATCH 11/25] Make error handling consistent across all trees Signed-off-by: Timothy Johnson --- .../__unit__/tree/ZoweTreeNode.unit.test.ts | 1 + .../src/tree/IZoweTreeNode.ts | 10 +++ .../src/tree/ZoweTreeNode.ts | 8 ++- .../dataset/ZoweDatasetNode.unit.test.ts | 33 ++++++++- .../trees/uss/ZoweUSSNode.unit.test.ts | 2 +- .../src/trees/ZoweTreeProvider.ts | 3 +- .../src/trees/dataset/DatasetActions.ts | 52 -------------- .../src/trees/dataset/DatasetTree.ts | 22 ++---- .../src/trees/dataset/ZoweDatasetNode.ts | 21 +++--- .../zowe-explorer/src/trees/job/JobTree.ts | 9 ++- .../src/trees/job/ZoweJobNode.ts | 69 ++++++++----------- .../src/trees/shared/SharedInit.ts | 4 +- .../zowe-explorer/src/trees/uss/USSTree.ts | 14 ++-- .../src/trees/uss/ZoweUSSNode.ts | 49 ++++++------- 14 files changed, 135 insertions(+), 162 deletions(-) diff --git a/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts b/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts index c5a97b17b6..c8a0ae5618 100644 --- a/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts +++ b/packages/zowe-explorer-api/__tests__/__unit__/tree/ZoweTreeNode.unit.test.ts @@ -94,6 +94,7 @@ describe("ZoweTreeNode", () => { it("setProfileToChoice should update profile for associated FSProvider entry", () => { const node = makeNode("test", vscode.TreeItemCollapsibleState.None, undefined); + node.resourceUri = vscode.Uri.file(__dirname); const fsEntry = { metadata: { profile: { name: "oldProfile" }, diff --git a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts index d65612211e..392ad02020 100644 --- a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts @@ -181,6 +181,11 @@ export interface IZoweDatasetTreeNode extends IZoweTreeNode { */ encoding?: string; + /** + * Use Dataset-specific tree node for children. + */ + children?: IZoweDatasetTreeNode[]; + /** * Retrieves child nodes of this IZoweDatasetTreeNode * @@ -296,6 +301,11 @@ export interface IZoweUSSTreeNode extends IZoweTreeNode { */ onUpdateEmitter?: vscode.EventEmitter; + /** + * Use USS-specific tree node for children. + */ + children?: IZoweUSSTreeNode[]; + /** * Event that fires whenever an existing node is updated. */ diff --git a/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts index 18f3fffc2b..bdcc061f99 100644 --- a/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts @@ -107,9 +107,11 @@ export class ZoweTreeNode extends vscode.TreeItem { // Don't reassign profile, we want to keep object reference shared across nodes this.profile.profile = aProfile.profile; } - const fsEntry = fsProvider?.lookup(this.resourceUri, true); - if (fsEntry != null) { - fsEntry.metadata.profile = aProfile; + if (this.resourceUri != null) { + const fsEntry = fsProvider?.lookup(this.resourceUri, true); + if (fsEntry != null) { + fsEntry.metadata.profile = aProfile; + } } for (const child of this.children) { (child as unknown as ZoweTreeNode).setProfileToChoice(aProfile, fsProvider); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts index 05b31159f4..87b23b26ca 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts @@ -125,7 +125,7 @@ describe("ZoweDatasetNode Unit Tests", () => { /************************************************************************************************************* * Checks that returning an unsuccessful response results in an error being thrown and caught *************************************************************************************************************/ - it("Checks that when List.dataSet/allMembers() returns an unsuccessful response, " + "it returns a label of 'No data sets found'", async () => { + it("Checks that when List.dataSet/allMembers() throws an error, " + "it returns a label of 'No data sets found'", async () => { Object.defineProperty(Profiles, "getInstance", { value: jest.fn(() => { return { @@ -148,7 +148,36 @@ describe("ZoweDatasetNode Unit Tests", () => { parentNode: rootNode, profile: profileOne, }); - jest.spyOn(subNode as any, "getDatasets").mockReturnValueOnce([ + jest.spyOn(subNode as any, "getDatasets").mockRejectedValueOnce(new Error(subNode.label as string)); + subNode.dirty = true; + const response = await subNode.getChildren(); + expect(response[0].label).toBe("No data sets found"); + }); + + it("Checks that when List.dataSet/allMembers() returns an empty response, " + "it returns a label of 'No data sets found'", async () => { + Object.defineProperty(Profiles, "getInstance", { + value: jest.fn(() => { + return { + loadNamedProfile: jest.fn().mockReturnValue(profileOne), + }; + }), + }); + // Creating a rootNode + const rootNode = new ZoweDatasetNode({ + label: "root", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + session, + profile: profileOne, + }); + rootNode.contextValue = Constants.DS_SESSION_CONTEXT; + rootNode.dirty = true; + const subNode = new ZoweDatasetNode({ + label: "Response Fail", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + parentNode: rootNode, + profile: profileOne, + }); + jest.spyOn(subNode as any, "getDatasets").mockResolvedValueOnce([ { success: true, apiResponse: { diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts index f2ea39ae06..df510e6fe8 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts @@ -859,7 +859,7 @@ describe("ZoweUSSNode Unit Tests - Function node.getChildren()", () => { await blockMocks.childNode.getChildren(); expect(globalMocks.showErrorMessage.mock.calls.length).toEqual(1); expect(globalMocks.showErrorMessage.mock.calls[0][0]).toEqual( - "Retrieving response from uss-file-list Error: Throwing an error to check error handling for unit tests!" + "Retrieving response from uss-list Error: Throwing an error to check error handling for unit tests!" ); } ); diff --git a/packages/zowe-explorer/src/trees/ZoweTreeProvider.ts b/packages/zowe-explorer/src/trees/ZoweTreeProvider.ts index ecf8139bab..4c280aff72 100644 --- a/packages/zowe-explorer/src/trees/ZoweTreeProvider.ts +++ b/packages/zowe-explorer/src/trees/ZoweTreeProvider.ts @@ -128,9 +128,10 @@ export class ZoweTreeProvider { if (icon) { element.iconPath = icon.path; } - element.dirty = true; if (isOpen) { this.mOnDidChangeTreeData.fire(element); + } else { + element.dirty = true; } } diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetActions.ts b/packages/zowe-explorer/src/trees/dataset/DatasetActions.ts index a5e0b8cf3f..ab8919fdd8 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetActions.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetActions.ts @@ -1297,58 +1297,6 @@ export class DatasetActions { } } - /** - * Prompts the user for a pattern, and populates the [TreeView]{@link vscode.TreeView} based on the pattern - * - * @param {IZoweDatasetTreeNode} node - The session node - * @param datasetProvider - Current DatasetTree used to populate the TreeView - * @returns {Promise} - */ - // This function does not appear to be called by anything except unit and integration tests. - public static async enterPattern(node: IZoweDatasetTreeNode, datasetProvider: Types.IZoweDatasetTreeType): Promise { - ZoweLogger.trace("dataset.actions.enterPattern called."); - let pattern: string; - if (SharedContext.isSessionNotFav(node)) { - // manually entering a search - const options: vscode.InputBoxOptions = { - prompt: vscode.l10n.t("Search Data Sets: use a comma to separate multiple patterns"), - value: node.pattern, - }; - // get user input - pattern = await Gui.showInputBox(options); - if (!pattern) { - Gui.showMessage(vscode.l10n.t("You must enter a pattern.")); - return; - } - ZoweLogger.debug( - vscode.l10n.t({ - message: "Prompted for a data set pattern, recieved {0}.", - args: [pattern], - comment: ["Data Set pattern"], - }) - ); - } else { - // executing search from saved search in favorites - pattern = node.label.toString().substring(node.label.toString().indexOf(":") + 2); - const sessionName = node.label.toString().substring(node.label.toString().indexOf("[") + 1, node.label.toString().indexOf("]")); - await datasetProvider.addSession({ sessionName: sessionName.trim() }); - node = datasetProvider.mSessionNodes.find((tempNode) => tempNode.label.toString().trim() === sessionName.trim()) as IZoweDatasetTreeNode; - } - - // update the treeview with the new pattern - // TODO figure out why a label change is needed to refresh the treeview, - // instead of changing the collapsible state - // change label so the treeview updates - node.tooltip = node.pattern = pattern.toUpperCase(); - node.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; - node.dirty = true; - const icon = IconGenerator.getIconByNode(node); - if (icon) { - node.iconPath = icon.path; - } - datasetProvider.addSearchHistory(node.pattern); - } - /** * Copy data sets * diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index e6cbf56516..267d50e4d7 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -151,8 +151,10 @@ export class DatasetTree extends ZoweTreeProvider implemen try { response = await element.getChildren(); } catch (error) { - await AuthUtils.errorHandling(error, String(element.label)); - return []; + await AuthUtils.errorHandling(error, element.label.toString(), vscode.l10n.t("Retrieving response from ds-list")); + AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getMvsApi(profile), element.getSessionNode()); + element.dirty = false; + return element.children; } const finalResponse: IZoweDatasetTreeNode[] = []; @@ -169,16 +171,6 @@ export class DatasetTree extends ZoweTreeProvider implemen item.contextValue = SharedContext.withProfile(item); } - if (finalResponse.length === 0) { - return (element.children = [ - new ZoweDatasetNode({ - label: vscode.l10n.t("No data sets found"), - collapsibleState: vscode.TreeItemCollapsibleState.None, - parentNode: element, - contextOverride: Constants.INFORMATION_CONTEXT, - }), - ]); - } return finalResponse; } return this.mSessionNodes; @@ -1032,12 +1024,12 @@ export class DatasetTree extends ZoweTreeProvider implemen pattern = choice.label; } } - const options2: vscode.InputBoxOptions = { + const options: vscode.InputBoxOptions = { prompt: vscode.l10n.t("Search Data Sets: use a comma to separate multiple patterns"), value: pattern, }; // get user input - pattern = await Gui.showInputBox(options2); + pattern = await Gui.showInputBox(options); if (!pattern) { Gui.showMessage(vscode.l10n.t("You must enter a pattern.")); return; @@ -1055,7 +1047,6 @@ export class DatasetTree extends ZoweTreeProvider implemen } } // looking for members in pattern - node.children = []; node.dirty = true; AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getMvsApi(profile), sessionNode); @@ -1088,6 +1079,7 @@ export class DatasetTree extends ZoweTreeProvider implemen sessionNode.resourceUri = sessionNode.resourceUri.with({ query: `pattern=${pattern}` }); } await TreeViewUtils.expandNode(sessionNode, this); + this.refresh(); } public checkFilterPattern(dsName: string, itemName: string): boolean { diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts index f2b273dee4..dcf4a7e2a3 100644 --- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts @@ -102,13 +102,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod if (this.label !== vscode.l10n.t("Favorites")) { const sessionLabel = opts.profile?.name ?? SharedUtils.getSessionLabel(this); - if (this.getParent() == null || this.getParent().label === vscode.l10n.t("Favorites")) { - this.resourceUri = vscode.Uri.from({ - scheme: ZoweScheme.DS, - path: `/${sessionLabel}/`, - }); - DatasetFSProvider.instance.createDirectory(this.resourceUri); - } else if ( + if ( this.contextValue === Constants.DS_DS_CONTEXT || this.contextValue === Constants.DS_PDS_CONTEXT || this.contextValue === Constants.DS_MIGRATED_FILE_CONTEXT @@ -130,7 +124,13 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod }); this.command = { command: "vscode.open", title: "", arguments: [this.resourceUri] }; } else { - this.resourceUri = null; + this.resourceUri = vscode.Uri.from({ + scheme: ZoweScheme.DS, + path: `/${sessionLabel}/`, + }); + if (this.getParent() == null || this.getParent().label === vscode.l10n.t("Favorites")) { + DatasetFSProvider.instance.createDirectory(this.resourceUri); + } } if (opts.encoding != null) { @@ -232,9 +232,6 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod // Gets the datasets from the pattern or members of the dataset and displays any thrown errors const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); const responses = await this.getDatasets(cachedProfile); - if (responses.length === 0) { - return; - } // push nodes to an object with property names to avoid duplicates const elementChildren: { [k: string]: ZoweDatasetNode } = {}; @@ -243,7 +240,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod // The dataSetsMatchingPattern API may return success=false and apiResponse=[] when no data sets found if (!response.success && !(Array.isArray(response.apiResponse) && response.apiResponse.length === 0)) { await AuthUtils.errorHandling(vscode.l10n.t("The response from Zowe CLI was not successful")); - return; + return this.children; } // Loops through all the returned dataset members and creates nodes for them diff --git a/packages/zowe-explorer/src/trees/job/JobTree.ts b/packages/zowe-explorer/src/trees/job/JobTree.ts index 176447cb89..0db39a7cb2 100644 --- a/packages/zowe-explorer/src/trees/job/JobTree.ts +++ b/packages/zowe-explorer/src/trees/job/JobTree.ts @@ -174,7 +174,14 @@ export class JobTree extends ZoweTreeProvider implements Types if (element.contextValue && element.contextValue === Constants.FAV_PROFILE_CONTEXT) { return this.loadProfilesForFavorites(this.log, element); } - return element.getChildren(); + try { + return await element.getChildren(); + } catch (error) { + await AuthUtils.errorHandling(error, element.getProfileName(), vscode.l10n.t("Retrieving response from jobs-list")); + AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), element.getSessionNode()); + element.dirty = false; + return element.children; + } } return this.mSessionNodes; } diff --git a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts index 46edc1c6a9..f373efa056 100644 --- a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts @@ -22,7 +22,6 @@ import { IconGenerator } from "../../icons/IconGenerator"; import { ZoweLogger } from "../../tools/ZoweLogger"; import { SharedContext } from "../shared/SharedContext"; import { SharedUtils } from "../shared/SharedUtils"; -import { AuthUtils } from "../../utils/AuthUtils"; import type { Definitions } from "../../configuration/Definitions"; export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { @@ -158,7 +157,6 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { parentNode: this, profile: this.getProfile(), }); - noSpoolNode.iconPath = undefined; return [noSpoolNode]; } spools.forEach((spool) => { @@ -210,7 +208,6 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { profile: this.getProfile(), }); noJobsNode.contextValue = Constants.INFORMATION_CONTEXT; - noJobsNode.iconPath = undefined; noJobsNode.command = { command: "zowe.placeholderCommand", title: "Placeholder", @@ -375,44 +372,38 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { ZoweLogger.trace("ZoweJobNode.getJobs called."); let jobsInternal: zosjobs.IJob[] = []; const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); - try { - if (this.searchId.length > 0) { - jobsInternal.push(await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJob(searchId)); - } else { - if (!ZoweExplorerApiRegister.getJesApi(cachedProfile).getSession(cachedProfile)) { - throw new imperative.ImperativeError({ - msg: vscode.l10n.t("Profile auth error"), - additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), - errorCode: `${imperative.RestConstants.HTTP_STATUS_401}`, - }); - } - jobsInternal = await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJobsByParameters({ - owner, - prefix, - status, - execData: true, + if (this.searchId.length > 0) { + jobsInternal.push(await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJob(searchId)); + } else { + if (!ZoweExplorerApiRegister.getJesApi(cachedProfile).getSession(cachedProfile)) { + throw new imperative.ImperativeError({ + msg: vscode.l10n.t("Profile auth error"), + additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), + errorCode: `${imperative.RestConstants.HTTP_STATUS_401}`, }); - - /** - * Note: Temporary fix - * This current fix is necessary since in certain instances the Zowe - * Explorer JES API returns duplicate jobs. The following reduce function - * filters only the unique jobs present by comparing the ids of these returned - * jobs. - */ - jobsInternal = jobsInternal.reduce((acc: zosjobs.IJob[], current) => { - const duplicateJobExists = acc.find((job) => job.jobid === current.jobid); - if (!duplicateJobExists) { - return acc.concat([current]); - } else { - return acc; - } - }, []); } - } catch (error) { - ZoweLogger.trace("Error getting jobs from Rest API."); - await AuthUtils.errorHandling(error, cachedProfile.name, vscode.l10n.t("Retrieving response from zowe.GetJobs")); - AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode()); + jobsInternal = await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJobsByParameters({ + owner, + prefix, + status, + execData: true, + }); + + /** + * Note: Temporary fix + * This current fix is necessary since in certain instances the Zowe + * Explorer JES API returns duplicate jobs. The following reduce function + * filters only the unique jobs present by comparing the ids of these returned + * jobs. + */ + jobsInternal = jobsInternal.reduce((acc: zosjobs.IJob[], current) => { + const duplicateJobExists = acc.find((job) => job.jobid === current.jobid); + if (!duplicateJobExists) { + return acc.concat([current]); + } else { + return acc; + } + }, []); } return jobsInternal; } diff --git a/packages/zowe-explorer/src/trees/shared/SharedInit.ts b/packages/zowe-explorer/src/trees/shared/SharedInit.ts index ac6aff17d4..2aeea0c756 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedInit.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedInit.ts @@ -380,10 +380,10 @@ export class SharedInit { context.subscriptions.push(theTreeView); context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(async (e) => SharedInit.setupRemoteWorkspaceFolders(e))); theTreeView.onDidCollapseElement(async (e) => { - await theProvider.flipState(e.element, false); + theProvider.flipState(e.element, false); }); theTreeView.onDidExpandElement(async (e) => { - await theProvider.flipState(e.element, true); + theProvider.flipState(e.element, true); }); } diff --git a/packages/zowe-explorer/src/trees/uss/USSTree.ts b/packages/zowe-explorer/src/trees/uss/USSTree.ts index 3cae74470b..34c8cbe3b4 100644 --- a/packages/zowe-explorer/src/trees/uss/USSTree.ts +++ b/packages/zowe-explorer/src/trees/uss/USSTree.ts @@ -462,7 +462,14 @@ export class USSTree extends ZoweTreeProvider implements Types const favsForProfile = await this.loadProfilesForFavorites(this.log, element); return favsForProfile; } - return element.getChildren(); + try { + return await element.getChildren(); + } catch (error) { + await AuthUtils.errorHandling(error, element.getProfileName(), vscode.l10n.t("Retrieving response from uss-list")); + AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getUssApi(profile), element.getSessionNode()); + element.dirty = false; + return element.children; + } } return this.mSessionNodes; } @@ -676,9 +683,7 @@ export class USSTree extends ZoweTreeProvider implements Types */ public async getAllLoadedItems(): Promise { ZoweLogger.trace("USSTree.getAllLoadedItems called."); - if (this.log) { - ZoweLogger.debug(vscode.l10n.t("Prompting the user to choose a member from the filtered list")); - } + ZoweLogger.debug(vscode.l10n.t("Prompting the user to choose a member from the filtered list")); const loadedNodes: IZoweUSSTreeNode[] = []; const sessions = await this.getChildren(); @@ -984,7 +989,6 @@ export class USSTree extends ZoweTreeProvider implements Types parentPath: parentNode.fullPath, }); infoNode.contextValue = Constants.INFORMATION_CONTEXT; - infoNode.iconPath = undefined; return [infoNode]; } } catch (error) { diff --git a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts index 5931fc94cc..ac5e751ebd 100644 --- a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts +++ b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts @@ -205,35 +205,26 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { } // Get the list of files/folders at the given USS path and handle any errors + const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); let response: zosfiles.IZosFilesResponse; - const sessNode = this.getSessionNode(); - let nodeProfile; - try { - const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); - if (!ZoweExplorerApiRegister.getUssApi(cachedProfile).getSession(cachedProfile)) { - throw new imperative.ImperativeError({ - msg: vscode.l10n.t("Profile auth error"), - additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), - errorCode: `${imperative.RestConstants.HTTP_STATUS_401 as number}`, - }); - } - nodeProfile = cachedProfile; - if (SharedContext.isSession(this)) { - response = await UssFSProvider.instance.listFiles( - nodeProfile, - SharedContext.isFavorite(this) - ? this.resourceUri - : this.resourceUri.with({ - path: path.posix.join(this.resourceUri.path, this.fullPath), - }) - ); - } else { - response = await UssFSProvider.instance.listFiles(nodeProfile, this.resourceUri); - } - } catch (err) { - await AuthUtils.errorHandling(err, this.label.toString(), vscode.l10n.t("Retrieving response from uss-file-list")); - AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getUssApi(profile), sessNode); - return this.children; + if (!ZoweExplorerApiRegister.getUssApi(cachedProfile).getSession(cachedProfile)) { + throw new imperative.ImperativeError({ + msg: vscode.l10n.t("Profile auth error"), + additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), + errorCode: `${imperative.RestConstants.HTTP_STATUS_401 as number}`, + }); + } + if (SharedContext.isSession(this)) { + response = await UssFSProvider.instance.listFiles( + cachedProfile, + SharedContext.isFavorite(this) + ? this.resourceUri + : this.resourceUri.with({ + path: path.posix.join(this.resourceUri.path, this.fullPath), + }) + ); + } else { + response = await UssFSProvider.instance.listFiles(cachedProfile, this.resourceUri); } // If search path has changed, invalidate all children @@ -271,7 +262,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { collapsibleState: collapseState, parentNode: this, parentPath: this.fullPath, - profile: nodeProfile, + profile: cachedProfile, encoding: isDir ? undefined : await this.getEncodingInMap(`${this.fullPath}/${item.name as string}`), }); if (isDir) { From 52db6de6d30aaa3dc0d2ae13a577d381a97ad700 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Mon, 30 Sep 2024 23:40:08 -0400 Subject: [PATCH 12/25] Move 401 error handling back into node methods Signed-off-by: Timothy Johnson --- packages/zowe-explorer/CHANGELOG.md | 2 +- .../__tests__/__mocks__/mockCreators/jobs.ts | 2 +- .../trees/dataset/DatasetActions.unit.test.ts | 51 --------- .../dataset/ZoweDatasetNode.unit.test.ts | 33 +----- packages/zowe-explorer/l10n/bundle.l10n.json | 17 ++- packages/zowe-explorer/l10n/poeditor.json | 12 +- .../src/trees/dataset/DatasetTree.ts | 16 ++- .../src/trees/dataset/ZoweDatasetNode.ts | 81 ++++++------- .../zowe-explorer/src/trees/job/JobTree.ts | 9 +- .../src/trees/job/ZoweJobNode.ts | 106 ++++++++++-------- .../zowe-explorer/src/trees/uss/USSTree.ts | 11 +- .../src/trees/uss/ZoweUSSNode.ts | 57 ++++++---- 12 files changed, 163 insertions(+), 234 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 2f76831b7f..a7798d4c95 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen ### Bug fixes -- Fixed issue where 401 error could occur when opening PDS members after updating credentials. [#3150](https://github.com/zowe/zowe-explorer-vscode/issues/3150) +- Fixed an issue where a 401 error could occur when opening PDS members after updating credentials. [#3150](https://github.com/zowe/zowe-explorer-vscode/issues/3150) ## `3.0.0-next.202409251932` diff --git a/packages/zowe-explorer/__tests__/__mocks__/mockCreators/jobs.ts b/packages/zowe-explorer/__tests__/__mocks__/mockCreators/jobs.ts index 5ff05ac1ba..5c787e51a1 100644 --- a/packages/zowe-explorer/__tests__/__mocks__/mockCreators/jobs.ts +++ b/packages/zowe-explorer/__tests__/__mocks__/mockCreators/jobs.ts @@ -160,8 +160,8 @@ export function createJobInfoNode(session: any, profile: imperative.IProfileLoad collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: session.getSessionNode(), profile, + contextOverride: Constants.INFORMATION_CONTEXT, }); - jobNode.contextValue = Constants.INFORMATION_CONTEXT; return jobNode; } diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetActions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetActions.unit.test.ts index c8d1301585..dcfa23e152 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetActions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetActions.unit.test.ts @@ -871,57 +871,6 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { }); }); -describe("Dataset Actions Unit Tests - Function enterPattern", () => { - afterAll(() => jest.restoreAllMocks()); - - it("Checking common dataset filter action", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocksShared(); - const node = new ZoweDatasetNode({ - label: "node", - collapsibleState: vscode.TreeItemCollapsibleState.None, - parentNode: blockMocks.datasetSessionNode, - }); - node.pattern = "TEST"; - node.contextValue = Constants.DS_SESSION_CONTEXT; - - const mySpy = mocked(vscode.window.showInputBox).mockResolvedValue("test"); - await DatasetActions.enterPattern(node, blockMocks.testDatasetTree); - - expect(mySpy).toHaveBeenCalledWith( - expect.objectContaining({ - prompt: "Search Data Sets: use a comma to separate multiple patterns", - value: node.pattern, - }) - ); - expect(mocked(Gui.showMessage)).not.toHaveBeenCalled(); - }); - it("Checking common dataset filter failed attempt", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocksShared(); - const node = new ZoweDatasetNode({ - label: "node", - collapsibleState: vscode.TreeItemCollapsibleState.None, - parentNode: blockMocks.datasetSessionNode, - }); - node.pattern = "TEST"; - node.contextValue = Constants.DS_SESSION_CONTEXT; - - mocked(vscode.window.showInputBox).mockResolvedValueOnce(""); - await DatasetActions.enterPattern(node, blockMocks.testDatasetTree); - - expect(mocked(Gui.showMessage)).toHaveBeenCalledWith("You must enter a pattern."); - }); - it("Checking favorite dataset filter action", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocksShared(); - const favoriteSample = new ZoweDatasetNode({ label: "[sestest]: HLQ.TEST", collapsibleState: vscode.TreeItemCollapsibleState.None }); - - await DatasetActions.enterPattern(favoriteSample, blockMocks.testDatasetTree); - expect(blockMocks.testDatasetTree.addSession).toHaveBeenCalledWith({ sessionName: "sestest" }); - }); -}); - describe("Dataset Actions Unit Tests - Function showAttributes", () => { function createBlockMocks() { const session = createISession(); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts index 87b23b26ca..19e14d2fe4 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts @@ -125,35 +125,6 @@ describe("ZoweDatasetNode Unit Tests", () => { /************************************************************************************************************* * Checks that returning an unsuccessful response results in an error being thrown and caught *************************************************************************************************************/ - it("Checks that when List.dataSet/allMembers() throws an error, " + "it returns a label of 'No data sets found'", async () => { - Object.defineProperty(Profiles, "getInstance", { - value: jest.fn(() => { - return { - loadNamedProfile: jest.fn().mockReturnValue(profileOne), - }; - }), - }); - // Creating a rootNode - const rootNode = new ZoweDatasetNode({ - label: "root", - collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, - session, - profile: profileOne, - }); - rootNode.contextValue = Constants.DS_SESSION_CONTEXT; - rootNode.dirty = true; - const subNode = new ZoweDatasetNode({ - label: "Response Fail", - collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, - parentNode: rootNode, - profile: profileOne, - }); - jest.spyOn(subNode as any, "getDatasets").mockRejectedValueOnce(new Error(subNode.label as string)); - subNode.dirty = true; - const response = await subNode.getChildren(); - expect(response[0].label).toBe("No data sets found"); - }); - it("Checks that when List.dataSet/allMembers() returns an empty response, " + "it returns a label of 'No data sets found'", async () => { Object.defineProperty(Profiles, "getInstance", { value: jest.fn(() => { @@ -200,6 +171,7 @@ describe("ZoweDatasetNode Unit Tests", () => { collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, session, profile: profileOne, + contextOverride: Constants.DS_SESSION_CONTEXT, }); const infoChild = new ZoweDatasetNode({ label: "Use the search button to display data sets", @@ -212,7 +184,6 @@ describe("ZoweDatasetNode Unit Tests", () => { command: "zowe.placeholderCommand", title: "Placeholder", }; - rootNode.contextValue = Constants.DS_SESSION_CONTEXT; rootNode.dirty = false; expect(await rootNode.getChildren()).toEqual([infoChild]); }); @@ -227,6 +198,7 @@ describe("ZoweDatasetNode Unit Tests", () => { collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, session, profile: profileOne, + contextOverride: Constants.DS_SESSION_CONTEXT, }); const infoChild = new ZoweDatasetNode({ label: "Use the search button to display data sets", @@ -239,7 +211,6 @@ describe("ZoweDatasetNode Unit Tests", () => { command: "zowe.placeholderCommand", title: "Placeholder", }; - rootNode.contextValue = Constants.DS_SESSION_CONTEXT; expect(await rootNode.getChildren()).toEqual([infoChild]); }); diff --git a/packages/zowe-explorer/l10n/bundle.l10n.json b/packages/zowe-explorer/l10n/bundle.l10n.json index a693ab3477..809ba92fcc 100644 --- a/packages/zowe-explorer/l10n/bundle.l10n.json +++ b/packages/zowe-explorer/l10n/bundle.l10n.json @@ -143,9 +143,6 @@ "Favorites": "Favorites", "Use the search button to list USS files": "Use the search button to list USS files", "Invalid node": "Invalid node", - "Profile auth error": "Profile auth error", - "Profile is not authenticated, please log in to continue": "Profile is not authenticated, please log in to continue", - "Retrieving response from uss-file-list": "Retrieving response from uss-file-list", "Delete action was cancelled.": "Delete action was cancelled.", "Unable to delete node: {0}/Error message": { "message": "Unable to delete node: {0}", @@ -171,6 +168,9 @@ "Required API functions for pasting (fileList and copy/uploadFromBuffer) were not found.": "Required API functions for pasting (fileList and copy/uploadFromBuffer) were not found.", "Uploading USS files...": "Uploading USS files...", "Error uploading files": "Error uploading files", + "Profile auth error": "Profile auth error", + "Profile is not authenticated, please log in to continue": "Profile is not authenticated, please log in to continue", + "Retrieving response from USS list API": "Retrieving response from USS list API", "The 'move' function is not implemented for this USS API.": "The 'move' function is not implemented for this USS API.", "Could not list USS files: Empty path provided in URI": "Could not list USS files: Empty path provided in URI", "Profile does not exist for this file.": "Profile does not exist for this file.", @@ -352,7 +352,7 @@ "Use the search button to display jobs": "Use the search button to display jobs", "There are no JES spool messages to display": "There are no JES spool messages to display", "No jobs found": "No jobs found", - "Retrieving response from zowe.GetJobs": "Retrieving response from zowe.GetJobs", + "Retrieving response from JES list API": "Retrieving response from JES list API", "$(list-ordered) Job ID (default)": "$(list-ordered) Job ID (default)", "$(calendar) Date Submitted": "$(calendar) Date Submitted", "$(calendar) Date Completed": "$(calendar) Date Completed", @@ -425,6 +425,7 @@ "Job label" ] }, + "Retrieving response from jobs-list": "Retrieving response from jobs-list", "Get JCL": "Get JCL", "Display in Tree": "Display in Tree", "Cancel": "Cancel", @@ -538,6 +539,7 @@ "The response from Zowe CLI was not successful": "The response from Zowe CLI was not successful", "{0} members failed to load due to invalid name errors for {1}": "{0} members failed to load due to invalid name errors for {1}", "No data sets found": "No data sets found", + "Retrieving response from MVS list API": "Retrieving response from MVS list API", "Cannot download, item invalid.": "Cannot download, item invalid.", "$(case-sensitive) Name (default)": "$(case-sensitive) Name (default)", "$(calendar) Date Created": "$(calendar) Date Created", @@ -637,6 +639,7 @@ "Node label" ] }, + "Retrieving response from ds-list": "Retrieving response from ds-list", "Getting data set templates.": "Getting data set templates.", "Resetting data set templates array.": "Resetting data set templates array.", "Updating data set templates.": "Updating data set templates.", @@ -801,12 +804,6 @@ "Stringified JSON error" ] }, - "Prompted for a data set pattern, recieved {0}./Data Set pattern": { - "message": "Prompted for a data set pattern, recieved {0}.", - "comment": [ - "Data Set pattern" - ] - }, "Cannot perform the copy operation as the data sets selected have different types": "Cannot perform the copy operation as the data sets selected have different types", "Migration of data set {0} requested./Data Set name": { "message": "Migration of data set {0} requested.", diff --git a/packages/zowe-explorer/l10n/poeditor.json b/packages/zowe-explorer/l10n/poeditor.json index d346e0f553..284d115e3d 100644 --- a/packages/zowe-explorer/l10n/poeditor.json +++ b/packages/zowe-explorer/l10n/poeditor.json @@ -478,9 +478,6 @@ "Favorites": "", "Use the search button to list USS files": "", "Invalid node": "", - "Profile auth error": "", - "Profile is not authenticated, please log in to continue": "", - "Retrieving response from uss-file-list": "", "Delete action was cancelled.": "", "Unable to delete node: {0}": "", "The item {0} has been deleted.": "", @@ -491,6 +488,9 @@ "Required API functions for pasting (fileList and copy/uploadFromBuffer) were not found.": "", "Uploading USS files...": "", "Error uploading files": "", + "Profile auth error": "", + "Profile is not authenticated, please log in to continue": "", + "Retrieving response from USS list API": "", "The 'move' function is not implemented for this USS API.": "", "Could not list USS files: Empty path provided in URI": "", "Profile does not exist for this file.": "", @@ -572,7 +572,7 @@ "Use the search button to display jobs": "", "There are no JES spool messages to display": "", "No jobs found": "", - "Retrieving response from zowe.GetJobs": "", + "Retrieving response from JES list API": "", "$(list-ordered) Job ID (default)": "", "$(calendar) Date Submitted": "", "$(calendar) Date Completed": "", @@ -604,6 +604,7 @@ "$(check) Filter cleared for {0}": "", "Enter local filter...": "", "$(check) Filter updated for {0}": "", + "Retrieving response from jobs-list": "", "Get JCL": "", "Display in Tree": "", "Cancel": "", @@ -648,6 +649,7 @@ "The response from Zowe CLI was not successful": "", "{0} members failed to load due to invalid name errors for {1}": "", "No data sets found": "", + "Retrieving response from MVS list API": "", "Cannot download, item invalid.": "", "$(case-sensitive) Name (default)": "", "$(calendar) Date Created": "", @@ -676,6 +678,7 @@ "Invalid date format specified": "", "Enter a value to filter by": "", "Invalid filter specified": "", + "Retrieving response from ds-list": "", "Getting data set templates.": "", "Resetting data set templates array.": "", "Updating data set templates.": "", @@ -740,7 +743,6 @@ "Item invalid.": "", "$(sync~spin) Fetching data set...": "", "Error encountered when refreshing data set view. {0}": "", - "Prompted for a data set pattern, recieved {0}.": "", "Cannot perform the copy operation as the data sets selected have different types": "", "Migration of data set {0} requested.": "", "Recall of data set {0} requested.": "", diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index 267d50e4d7..754845ff6f 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -147,15 +147,7 @@ export class DatasetTree extends ZoweTreeProvider implemen if (element.contextValue && element.contextValue === Constants.FAV_PROFILE_CONTEXT) { return this.loadProfilesForFavorites(this.log, element); } - let response: IZoweDatasetTreeNode[] = []; - try { - response = await element.getChildren(); - } catch (error) { - await AuthUtils.errorHandling(error, element.label.toString(), vscode.l10n.t("Retrieving response from ds-list")); - AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getMvsApi(profile), element.getSessionNode()); - element.dirty = false; - return element.children; - } + const response = await element.getChildren(); const finalResponse: IZoweDatasetTreeNode[] = []; for (const item of response) { @@ -1522,4 +1514,10 @@ export class DatasetTree extends ZoweTreeProvider implemen await node.openDs(true, false, this); } } + + public async errorHandling(node: IZoweDatasetTreeNode, error: Error): Promise { + await AuthUtils.errorHandling(error, node.getProfileName(), vscode.l10n.t("Retrieving response from ds-list")); + AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getMvsApi(profile), node.getSessionNode()); + node.dirty = false; + } } diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts index dcf4a7e2a3..ea88a35622 100644 --- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts @@ -130,6 +130,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod }); if (this.getParent() == null || this.getParent().label === vscode.l10n.t("Favorites")) { DatasetFSProvider.instance.createDirectory(this.resourceUri); + } else if (this.contextValue === Constants.INFORMATION_CONTEXT) { + this.command = { command: "zowe.placeholderCommand", title: "Placeholder" }; } } @@ -208,12 +210,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: this, contextOverride: Constants.INFORMATION_CONTEXT, - profile: null, + profile: this.getProfile(), }); - placeholder.command = { - command: "zowe.placeholderCommand", - title: "Placeholder", - }; return [placeholder]; } if (SharedContext.isDocument(this) || SharedContext.isInformation(this)) { @@ -240,7 +238,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod // The dataSetsMatchingPattern API may return success=false and apiResponse=[] when no data sets found if (!response.success && !(Array.isArray(response.apiResponse) && response.apiResponse.length === 0)) { await AuthUtils.errorHandling(vscode.l10n.t("The response from Zowe CLI was not successful")); - return this.children; + return []; } // Loops through all the returned dataset members and creates nodes for them @@ -373,10 +371,6 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod parentNode: this, contextOverride: Constants.INFORMATION_CONTEXT, }); - placeholder.command = { - command: "zowe.placeholderCommand", - title: "Placeholder", - }; this.children = [placeholder]; } else { const newChildren = Object.keys(elementChildren) @@ -542,7 +536,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod attributes: true, responseTimeout: profile.profile.responseTimeout, }; - if (SharedContext.isSession(this) || SharedContext.isFavoriteSearch(this)) { + const isSession = SharedContext.isSession(this) || SharedContext.isFavoriteSearch(this); + if (isSession) { const fullPattern = SharedContext.isFavoriteSearch(this) ? (this.label as string) : this.pattern; const dsTree = SharedTreeProviders.ds as DatasetTree; this.patternMatches = dsTree.extractPatterns(fullPattern); @@ -554,38 +549,46 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } this.tooltip = this.pattern = dsPattern.toUpperCase(); } + } - const dsPatterns = [ - ...new Set( - this.pattern - .toUpperCase() - .split(",") - .map((p) => p.trim()) - ), - ]; - const mvsApi = ZoweExplorerApiRegister.getMvsApi(profile); - if (!mvsApi.getSession(profile)) { - throw new imperative.ImperativeError({ - msg: vscode.l10n.t("Profile auth error"), - additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), - errorCode: `${imperative.RestConstants.HTTP_STATUS_401}`, - }); - } - if (mvsApi.dataSetsMatchingPattern) { - responses.push(await mvsApi.dataSetsMatchingPattern(dsPatterns)); - } else { - for (const dsp of dsPatterns) { - responses.push(await mvsApi.dataSet(dsp)); + try { + if (isSession) { + const dsPatterns = [ + ...new Set( + this.pattern + .toUpperCase() + .split(",") + .map((p) => p.trim()) + ), + ]; + const mvsApi = ZoweExplorerApiRegister.getMvsApi(profile); + if (!mvsApi.getSession(profile)) { + throw new imperative.ImperativeError({ + msg: vscode.l10n.t("Profile auth error"), + additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), + errorCode: `${imperative.RestConstants.HTTP_STATUS_401}`, + }); } - } - } else if (this.memberPattern) { - this.memberPattern = this.memberPattern.toUpperCase(); - for (const memPattern of this.memberPattern.split(",")) { - options.pattern = memPattern; + if (mvsApi.dataSetsMatchingPattern) { + responses.push(await mvsApi.dataSetsMatchingPattern(dsPatterns)); + } else { + for (const dsp of dsPatterns) { + responses.push(await mvsApi.dataSet(dsp)); + } + } + } else if (this.memberPattern) { + this.memberPattern = this.memberPattern.toUpperCase(); + for (const memPattern of this.memberPattern.split(",")) { + options.pattern = memPattern; + responses.push(await ZoweExplorerApiRegister.getMvsApi(profile).allMembers(this.label as string, options)); + } + } else { responses.push(await ZoweExplorerApiRegister.getMvsApi(profile).allMembers(this.label as string, options)); } - } else { - responses.push(await ZoweExplorerApiRegister.getMvsApi(profile).allMembers(this.label as string, options)); + } catch (error) { + await AuthUtils.errorHandling(error, this.label.toString(), vscode.l10n.t("Retrieving response from MVS list API")); + AuthUtils.syncSessionNode((prof) => ZoweExplorerApiRegister.getMvsApi(prof), this.getSessionNode()); + this.dirty = false; } return responses; } diff --git a/packages/zowe-explorer/src/trees/job/JobTree.ts b/packages/zowe-explorer/src/trees/job/JobTree.ts index 0db39a7cb2..176447cb89 100644 --- a/packages/zowe-explorer/src/trees/job/JobTree.ts +++ b/packages/zowe-explorer/src/trees/job/JobTree.ts @@ -174,14 +174,7 @@ export class JobTree extends ZoweTreeProvider implements Types if (element.contextValue && element.contextValue === Constants.FAV_PROFILE_CONTEXT) { return this.loadProfilesForFavorites(this.log, element); } - try { - return await element.getChildren(); - } catch (error) { - await AuthUtils.errorHandling(error, element.getProfileName(), vscode.l10n.t("Retrieving response from jobs-list")); - AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), element.getSessionNode()); - element.dirty = false; - return element.children; - } + return element.getChildren(); } return this.mSessionNodes; } diff --git a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts index f373efa056..cab7c066a1 100644 --- a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts @@ -22,6 +22,7 @@ import { IconGenerator } from "../../icons/IconGenerator"; import { ZoweLogger } from "../../tools/ZoweLogger"; import { SharedContext } from "../shared/SharedContext"; import { SharedUtils } from "../shared/SharedUtils"; +import { AuthUtils } from "../../utils/AuthUtils"; import type { Definitions } from "../../configuration/Definitions"; export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { @@ -102,6 +103,8 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { if (this.getParent()?.label !== vscode.l10n.t("Favorites") && !SharedContext.isFavorite(this)) { this.id = this.label as string; } + } else if (this.contextValue === Constants.INFORMATION_CONTEXT) { + this.command = { command: "zowe.placeholderCommand", title: "Placeholder" }; } else if (this.job != null) { this.resourceUri = vscode.Uri.from({ scheme: ZoweScheme.Jobs, @@ -129,10 +132,6 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { profile: thisSessionNode.getProfile(), contextOverride: Constants.INFORMATION_CONTEXT, }); - placeholder.command = { - command: "zowe.placeholderCommand", - title: "Placeholder", - }; return [placeholder]; } @@ -143,13 +142,7 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { const elementChildren: Record = {}; if (SharedContext.isJob(this)) { // Fetch spool files under job node - const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); - const spools: zosjobs.IJobFile[] = ( - (await ZoweExplorerApiRegister.getJesApi(cachedProfile).getSpoolFiles(this.job.jobname, this.job.jobid)) ?? [] - ) - // filter out all the objects which do not seem to be correct Job File Document types - // see an issue #845 for the details - .filter((item) => !(item.id === undefined && item.ddname === undefined && item.stepname === undefined)); + const spools = await this.getSpoolFiles(this.job); if (!spools.length) { const noSpoolNode = new ZoweSpoolNode({ label: vscode.l10n.t("There are no JES spool messages to display"), @@ -206,12 +199,8 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: this, profile: this.getProfile(), + contextOverride: Constants.INFORMATION_CONTEXT, }); - noJobsNode.contextValue = Constants.INFORMATION_CONTEXT; - noJobsNode.command = { - command: "zowe.placeholderCommand", - title: "Placeholder", - }; return [noJobsNode]; } jobs.forEach((job) => { @@ -371,42 +360,65 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { private async getJobs(owner: string, prefix: string, searchId: string, status: string): Promise { ZoweLogger.trace("ZoweJobNode.getJobs called."); let jobsInternal: zosjobs.IJob[] = []; - const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); - if (this.searchId.length > 0) { - jobsInternal.push(await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJob(searchId)); - } else { - if (!ZoweExplorerApiRegister.getJesApi(cachedProfile).getSession(cachedProfile)) { - throw new imperative.ImperativeError({ - msg: vscode.l10n.t("Profile auth error"), - additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), - errorCode: `${imperative.RestConstants.HTTP_STATUS_401}`, + try { + const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); + if (this.searchId.length > 0) { + jobsInternal.push(await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJob(searchId)); + } else { + if (!ZoweExplorerApiRegister.getJesApi(cachedProfile).getSession(cachedProfile)) { + throw new imperative.ImperativeError({ + msg: vscode.l10n.t("Profile auth error"), + additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), + errorCode: `${imperative.RestConstants.HTTP_STATUS_401}`, + }); + } + jobsInternal = await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJobsByParameters({ + owner, + prefix, + status, + execData: true, }); - } - jobsInternal = await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJobsByParameters({ - owner, - prefix, - status, - execData: true, - }); - /** - * Note: Temporary fix - * This current fix is necessary since in certain instances the Zowe - * Explorer JES API returns duplicate jobs. The following reduce function - * filters only the unique jobs present by comparing the ids of these returned - * jobs. - */ - jobsInternal = jobsInternal.reduce((acc: zosjobs.IJob[], current) => { - const duplicateJobExists = acc.find((job) => job.jobid === current.jobid); - if (!duplicateJobExists) { - return acc.concat([current]); - } else { - return acc; - } - }, []); + /** + * Note: Temporary fix + * This current fix is necessary since in certain instances the Zowe + * Explorer JES API returns duplicate jobs. The following reduce function + * filters only the unique jobs present by comparing the ids of these returned + * jobs. + */ + jobsInternal = jobsInternal.reduce((acc: zosjobs.IJob[], current) => { + const duplicateJobExists = acc.find((job) => job.jobid === current.jobid); + if (!duplicateJobExists) { + return acc.concat([current]); + } else { + return acc; + } + }, []); + } + } catch (error) { + await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from JES list API")); + AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode()); + this.dirty = false; } return jobsInternal; } + + private async getSpoolFiles(job: zosjobs.IJob = this.job): Promise { + ZoweLogger.trace("ZoweJobNode.getSpoolFiles called."); + let spools: zosjobs.IJobFile[] = []; + try { + const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); + spools = (await ZoweExplorerApiRegister.getJesApi(cachedProfile).getSpoolFiles(job.jobname, job.jobid)) ?? []; + // filter out all the objects which do not seem to be correct Job File Document types + // see an issue #845 for the details + spools = spools.filter((item) => !(item.id === undefined && item.ddname === undefined && item.stepname === undefined)); + } catch (error) { + await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from JES list API")); + AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode()); + this.dirty = false; + } + return spools; + } } export class ZoweSpoolNode extends ZoweJobNode { diff --git a/packages/zowe-explorer/src/trees/uss/USSTree.ts b/packages/zowe-explorer/src/trees/uss/USSTree.ts index 34c8cbe3b4..7670473f0c 100644 --- a/packages/zowe-explorer/src/trees/uss/USSTree.ts +++ b/packages/zowe-explorer/src/trees/uss/USSTree.ts @@ -462,14 +462,7 @@ export class USSTree extends ZoweTreeProvider implements Types const favsForProfile = await this.loadProfilesForFavorites(this.log, element); return favsForProfile; } - try { - return await element.getChildren(); - } catch (error) { - await AuthUtils.errorHandling(error, element.getProfileName(), vscode.l10n.t("Retrieving response from uss-list")); - AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getUssApi(profile), element.getSessionNode()); - element.dirty = false; - return element.children; - } + return element.getChildren(); } return this.mSessionNodes; } @@ -987,8 +980,8 @@ export class USSTree extends ZoweTreeProvider implements Types collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode, parentPath: parentNode.fullPath, + contextOverride: Constants.INFORMATION_CONTEXT, }); - infoNode.contextValue = Constants.INFORMATION_CONTEXT; return [infoNode]; } } catch (error) { diff --git a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts index ac5e751ebd..883821c344 100644 --- a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts +++ b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts @@ -112,6 +112,8 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { }); if (isSession) { UssFSProvider.instance.createDirectory(this.resourceUri); + } else if (this.contextValue === Constants.INFORMATION_CONTEXT) { + this.command = { command: "zowe.placeholderCommand", title: "Placeholder" }; } else if (this.collapsibleState === vscode.TreeItemCollapsibleState.None) { this.command = { command: "vscode.open", title: "", arguments: [this.resourceUri] }; } @@ -185,10 +187,6 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { parentNode: this, contextOverride: Constants.INFORMATION_CONTEXT, }); - placeholder.command = { - command: "zowe.placeholderCommand", - title: "Placeholder", - }; return [placeholder]; } @@ -206,25 +204,9 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { // Get the list of files/folders at the given USS path and handle any errors const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); - let response: zosfiles.IZosFilesResponse; - if (!ZoweExplorerApiRegister.getUssApi(cachedProfile).getSession(cachedProfile)) { - throw new imperative.ImperativeError({ - msg: vscode.l10n.t("Profile auth error"), - additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), - errorCode: `${imperative.RestConstants.HTTP_STATUS_401 as number}`, - }); - } - if (SharedContext.isSession(this)) { - response = await UssFSProvider.instance.listFiles( - cachedProfile, - SharedContext.isFavorite(this) - ? this.resourceUri - : this.resourceUri.with({ - path: path.posix.join(this.resourceUri.path, this.fullPath), - }) - ); - } else { - response = await UssFSProvider.instance.listFiles(cachedProfile, this.resourceUri); + const response = await this.getUssFiles(cachedProfile); + if (!response.success) { + return []; } // If search path has changed, invalidate all children @@ -675,4 +657,33 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { await AuthUtils.errorHandling(error, this.label.toString(), vscode.l10n.t("Error uploading files")); } } + + private async getUssFiles(profile: imperative.IProfileLoaded): Promise { + try { + if (!ZoweExplorerApiRegister.getUssApi(profile).getSession(profile)) { + throw new imperative.ImperativeError({ + msg: vscode.l10n.t("Profile auth error"), + additionalDetails: vscode.l10n.t("Profile is not authenticated, please log in to continue"), + errorCode: `${imperative.RestConstants.HTTP_STATUS_401 as number}`, + }); + } + if (SharedContext.isSession(this)) { + return await UssFSProvider.instance.listFiles( + profile, + SharedContext.isFavorite(this) + ? this.resourceUri + : this.resourceUri.with({ + path: path.posix.join(this.resourceUri.path, this.fullPath), + }) + ); + } else { + return await UssFSProvider.instance.listFiles(profile, this.resourceUri); + } + } catch (error) { + await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from USS list API")); + AuthUtils.syncSessionNode((prof) => ZoweExplorerApiRegister.getUssApi(prof), this.getSessionNode()); + this.dirty = false; + return { success: false, commandResponse: null }; + } + } } From d6e32dcc3486bdb3d8eab29488e41d8957d4f6a8 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 1 Oct 2024 10:53:30 -0400 Subject: [PATCH 13/25] Update unit tests for error handling and placeholder nodes Signed-off-by: Timothy Johnson --- .../trees/ZoweTreeProvider.unit.test.ts | 5 +- .../trees/dataset/DatasetTree.unit.test.ts | 24 -------- .../dataset/ZoweDatasetNode.unit.test.ts | 33 +++++++++- .../__unit__/trees/job/JobTree.unit.test.ts | 3 - .../trees/job/ZoweJobNode.unit.test.ts | 60 +++++++++++++++---- .../trees/uss/ZoweUSSNode.unit.test.ts | 39 ++++++------ packages/zowe-explorer/l10n/bundle.l10n.json | 4 +- packages/zowe-explorer/l10n/poeditor.json | 4 +- .../src/trees/ZoweTreeProvider.ts | 1 + .../src/trees/dataset/DatasetTree.ts | 6 -- .../src/trees/job/ZoweJobNode.ts | 11 ++-- .../src/trees/uss/ZoweUSSNode.ts | 4 +- 12 files changed, 111 insertions(+), 83 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/ZoweTreeProvider.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/ZoweTreeProvider.unit.test.ts index cd838c7550..c7bbf3c1a1 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/ZoweTreeProvider.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/ZoweTreeProvider.unit.test.ts @@ -276,7 +276,7 @@ describe("Tree Provider unit tests, function getTreeItem", () => { }); }); -describe("Tree Provider unit tests, function getTreeItem", () => { +describe("Tree Provider unit tests, function flipState", () => { it("Testing that expand tree is executed successfully", async () => { const globalMocks = await createGlobalMocks(); const spy = jest.spyOn(ZoweLogger, "trace"); @@ -287,14 +287,17 @@ describe("Tree Provider unit tests, function getTreeItem", () => { session: globalMocks.testSession, }); folder.contextValue = Constants.USS_DIR_CONTEXT; + folder.dirty = false; // Testing flipState to open await globalMocks.testUSSTree.flipState(folder, true); expect(JSON.stringify(folder.iconPath)).toContain("folder-open.svg"); + expect(folder.dirty).toBe(false); // Testing flipState to closed await globalMocks.testUSSTree.flipState(folder, false); expect(JSON.stringify(folder.iconPath)).toContain("folder-closed.svg"); + expect(folder.dirty).toBe(true); expect(spy).toHaveBeenCalled(); spy.mockClear(); }); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts index f08c3e802e..1a4471847a 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts @@ -549,30 +549,6 @@ describe("Dataset Tree Unit Tests - Function getChildren", () => { expect(loadProfilesForFavoritesSpy).toHaveBeenCalledWith(log, favProfileNode); }); - - it("returns 'No data sets found' if there are no children", async () => { - createGlobalMocks(); - const blockMocks = createBlockMocks(); - - mocked(Profiles.getInstance).mockReturnValue(blockMocks.profile); - mocked(vscode.window.createTreeView).mockReturnValueOnce(blockMocks.treeView); - const testTree = new DatasetTree(); - testTree.mSessionNodes.push(blockMocks.datasetSessionNode); - const parent = new ZoweDatasetNode({ - label: "BRTVS99.PUBLIC", - collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, - parentNode: testTree.mSessionNodes[1], - }); - parent.dirty = true; - jest.spyOn(parent, "getChildren").mockResolvedValueOnce([]); - - const children = await testTree.getChildren(parent); - - // This function should never return undefined. - expect(children).toBeDefined(); - expect(children).toHaveLength(1); - expect(children[0].label).toBe("No data sets found"); - }); }); describe("Dataset Tree Unit Tests - Function loadProfilesForFavorites", () => { function createBlockMocks() { diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts index 19e14d2fe4..5ece492b10 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts @@ -125,7 +125,7 @@ describe("ZoweDatasetNode Unit Tests", () => { /************************************************************************************************************* * Checks that returning an unsuccessful response results in an error being thrown and caught *************************************************************************************************************/ - it("Checks that when List.dataSet/allMembers() returns an empty response, " + "it returns a label of 'No data sets found'", async () => { + it("Checks that when List.dataSet/allMembers() throws an error, it returns a label of 'No data sets found'", async () => { Object.defineProperty(Profiles, "getInstance", { value: jest.fn(() => { return { @@ -139,8 +139,37 @@ describe("ZoweDatasetNode Unit Tests", () => { collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, session, profile: profileOne, + contextOverride: Constants.DS_SESSION_CONTEXT, + }); + rootNode.dirty = true; + const subNode = new ZoweDatasetNode({ + label: "Response Fail", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + parentNode: rootNode, + profile: profileOne, + }); + jest.spyOn(zosfiles.List, "allMembers").mockRejectedValueOnce(new Error(subNode.label as string)); + subNode.dirty = true; + const response = await subNode.getChildren(); + expect(response[0].label).toBe("No data sets found"); + }); + + it("Checks that when List.dataSet/allMembers() returns an empty response, it returns a label of 'No data sets found'", async () => { + Object.defineProperty(Profiles, "getInstance", { + value: jest.fn(() => { + return { + loadNamedProfile: jest.fn().mockReturnValue(profileOne), + }; + }), + }); + // Creating a rootNode + const rootNode = new ZoweDatasetNode({ + label: "root", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + session, + profile: profileOne, + contextOverride: Constants.DS_SESSION_CONTEXT, }); - rootNode.contextValue = Constants.DS_SESSION_CONTEXT; rootNode.dirty = true; const subNode = new ZoweDatasetNode({ label: "Response Fail", diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/job/JobTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/job/JobTree.unit.test.ts index 0fa961cfc4..5e573cdbec 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/job/JobTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/job/JobTree.unit.test.ts @@ -96,7 +96,6 @@ async function createGlobalMocks() { mockDeleteJobs: jest.fn(), mockShowInputBox: jest.fn(), mockDeleteJob: jest.fn(), - mockGetJobsByOwnerAndPrefix: jest.fn(), mockShowInformationMessage: jest.fn(), mockShowWarningMessage: jest.fn(), mockLoadNamedProfile: jest.fn(), @@ -110,7 +109,6 @@ async function createGlobalMocks() { testSessionNoCred: createISessionWithoutCredentials(), testProfile: createIProfile(), testIJob: createIJobObject(), - testIJobComplete: createIJobObject(), testJobNode: null, testSessionNode: null, mockIJobFile: createIJobFile(), @@ -237,7 +235,6 @@ async function createGlobalMocks() { Object.defineProperty(ZoweLogger, "trace", { value: jest.fn(), configurable: true }); globalMocks.testSessionNode = createJobSessionNode(globalMocks.testSession, globalMocks.testProfile); globalMocks.mockGetJob.mockReturnValue(globalMocks.testIJob); - globalMocks.mockGetJobsByOwnerAndPrefix.mockReturnValue([globalMocks.testIJob, globalMocks.testIJobComplete]); globalMocks.mockProfileInstance.editSession = jest.fn(() => globalMocks.testProfile); globalMocks.mockGetConfiguration.mockReturnValue({ persistence: true, diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts index fa5309ff4d..25ee26faa6 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts @@ -30,7 +30,7 @@ import { Profiles } from "../../../../src/configuration/Profiles"; import { ZoweExplorerApiRegister } from "../../../../src/extending/ZoweExplorerApiRegister"; import { ZoweLocalStorage } from "../../../../src/tools/ZoweLocalStorage"; import { JobFSProvider } from "../../../../src/trees/job/JobFSProvider"; -import { ZoweJobNode } from "../../../../src/trees/job/ZoweJobNode"; +import { ZoweJobNode, ZoweSpoolNode } from "../../../../src/trees/job/ZoweJobNode"; import { SharedContext } from "../../../../src/trees/shared/SharedContext"; import { SharedTreeProviders } from "../../../../src/trees/shared/SharedTreeProviders"; import { JobInit } from "../../../../src/trees/job/JobInit"; @@ -52,7 +52,7 @@ async function createGlobalMocks() { mockDeleteJobs: jest.fn(), mockShowInputBox: jest.fn(), mockDeleteJob: jest.fn(), - mockGetJobsByOwnerAndPrefix: jest.fn(), + mockGetJobsByParameters: jest.fn(), mockShowInformationMessage: jest.fn(), mockLoadNamedProfile: jest.fn(), mockCreateQuickPick: jest.fn(), @@ -114,6 +114,10 @@ async function createGlobalMocks() { configurable: true, }); Object.defineProperty(globalMocks.mockGetJobs, "getJob", { value: globalMocks.mockGetJob, configurable: true }); + Object.defineProperty(globalMocks.mockGetJobs, "getJobsByParameters", { + value: globalMocks.mockGetJobsByParameters, + configurable: true, + }); Object.defineProperty(zosmf.ZosmfSession, "createSessCfgFromArgs", { value: globalMocks.mockCreateSessCfgFromArgs, configurable: true, @@ -174,7 +178,7 @@ async function createGlobalMocks() { globalMocks.mockCreateSessCfgFromArgs.mockReturnValue(globalMocks.testSession); globalMocks.testSessionNode = createJobSessionNode(globalMocks.testSession, globalMocks.testProfile); globalMocks.mockGetJob.mockReturnValue(globalMocks.testIJob); - globalMocks.mockGetJobsByOwnerAndPrefix.mockReturnValue([globalMocks.testIJob, globalMocks.testIJobComplete]); + globalMocks.mockGetJobsByParameters.mockReturnValue([globalMocks.testIJob, globalMocks.testIJobComplete]); globalMocks.mockProfileInstance.editSession = jest.fn(() => globalMocks.testProfile); globalMocks.testJobNode = new ZoweJobNode({ label: "jobtest", @@ -298,17 +302,18 @@ describe("ZoweJobNode unit tests - Function onDidConfiguration", () => { }); describe("ZoweJobNode unit tests - Function getChildren", () => { - xit("Tests that getChildren returns the jobs of the session, when called on the session", async () => { + it("Tests that getChildren returns the jobs of the session, when called on the session", async () => { const globalMocks = await createGlobalMocks(); await globalMocks.testJobsProvider.addSession("fake"); + globalMocks.testJobsProvider.mSessionNodes[1].filtered = true; const jobs = await globalMocks.testJobsProvider.mSessionNodes[1].getChildren(); expect(jobs.length).toBe(2); expect(jobs[0].job.jobid).toEqual(globalMocks.testIJob.jobid); - expect(jobs[0].tooltip).toEqual("TESTJOB(JOB1234)"); + expect(jobs[0].tooltip).toEqual("TESTJOB(JOB1234) - ACTIVE"); expect(jobs[1].job.jobid).toEqual(globalMocks.testIJobComplete.jobid); - expect(jobs[1].tooltip).toEqual("TESTJOB(JOB1235) - 0"); + expect(jobs[1].tooltip).toEqual("TESTJOB(JOB1235) - sampleMember - 0"); }); it("Tests that getChildren updates existing job nodes with new statuses", async () => { @@ -379,7 +384,23 @@ describe("ZoweJobNode unit tests - Function getChildren", () => { globalMocks.testJobNode.dirty = true; const spoolFilesAfter = await globalMocks.testJobNode.getChildren(); expect(spoolFilesAfter.length).toBe(1); - expect(spoolFilesAfter[0].label).toEqual("There are no JES spool messages to display"); + expect(spoolFilesAfter[0].label).toEqual("No spool files found"); + }); + + it("Tests that getChildren returns a placeholder node if there is error retrieving spool files", async () => { + const globalMocks = await createGlobalMocks(); + const spool = new ZoweSpoolNode({ + label: "No spool files found", + collapsibleState: vscode.TreeItemCollapsibleState.None, + parentNode: globalMocks.testJobNode, + contextOverride: Constants.INFORMATION_CONTEXT, + }); + jest.spyOn(ZoweExplorerApiRegister, "getJesApi").mockReturnValueOnce({ + getSpoolFiles: jest.fn().mockResolvedValue(new Error("Response Fail")), + } as any); + globalMocks.testJobNode.dirty = true; + const spools = await globalMocks.testJobNode.getChildren(); + expect(spools[0]).toEqual(spool); }); it("Tests that getChildren returns the spool files if user/owner is not defined", async () => { @@ -420,13 +441,8 @@ describe("ZoweJobNode unit tests - Function getChildren", () => { label: "No jobs found", collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: globalMocks.testJobsProvider.mSessionNodes[1], + contextOverride: Constants.INFORMATION_CONTEXT, }); - job.iconPath = undefined; - job.contextValue = "information"; - job.command = { - title: "Placeholder", - command: "zowe.placeholderCommand", - }; await globalMocks.testJobsProvider.addSession("fake"); globalMocks.testJobsProvider.mSessionNodes[1].filtered = true; jest.spyOn(globalMocks.testJobsProvider.mSessionNodes[1], "getJobs").mockResolvedValue([]); @@ -434,6 +450,24 @@ describe("ZoweJobNode unit tests - Function getChildren", () => { expect(jobs[0]).toEqual(job); }); + it("should return 'No jobs found' if there is error retrieving jobs", async () => { + const globalMocks = await createGlobalMocks(); + const job = new ZoweJobNode({ + label: "No jobs found", + collapsibleState: vscode.TreeItemCollapsibleState.None, + parentNode: globalMocks.testJobsProvider.mSessionNodes[1], + contextOverride: Constants.INFORMATION_CONTEXT, + }); + await globalMocks.testJobsProvider.addSession("fake"); + globalMocks.testJobsProvider.mSessionNodes[1].filtered = true; + globalMocks.mockGetJobsByParameters.mockRejectedValue(new Error("Response Fail")); + jest.spyOn(ZoweExplorerApiRegister, "getJesApi").mockReturnValueOnce({ + getSession: jest.fn().mockReturnValue(globalMocks.testSession), + } as any); + const jobs = await globalMocks.testJobsProvider.mSessionNodes[1].getChildren(); + expect(jobs[0]).toEqual(job); + }); + it("To check smfid field in Jobs Tree View", async () => { const globalMocks = await createGlobalMocks(); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts index df510e6fe8..c1cc0c3027 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts @@ -842,27 +842,24 @@ describe("ZoweUSSNode Unit Tests - Function node.getChildren()", () => { expect(blockMocks.rootNode.getChildren()).rejects.toEqual(Error("Invalid node")); }); - it( - "Tests that when zowe.List. causes an error on the zowe call, " + "node.getChildren() throws an error and the catch block is reached", - async () => { - const globalMocks = createGlobalMocks(); - const blockMocks = createBlockMocks(globalMocks); - - blockMocks.childNode.contextValue = Constants.USS_SESSION_CONTEXT; - blockMocks.childNode.fullPath = "Throw Error"; - blockMocks.childNode.dirty = true; - blockMocks.childNode.profile = globalMocks.profileOne; - jest.spyOn(UssFSProvider.instance, "listFiles").mockImplementation(() => { - throw new Error("Throwing an error to check error handling for unit tests!"); - }); - - await blockMocks.childNode.getChildren(); - expect(globalMocks.showErrorMessage.mock.calls.length).toEqual(1); - expect(globalMocks.showErrorMessage.mock.calls[0][0]).toEqual( - "Retrieving response from uss-list Error: Throwing an error to check error handling for unit tests!" - ); - } - ); + it("Tests that when List.fileList throws an error, node.getChildren() throws an error and the catch block is reached", async () => { + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); + + blockMocks.childNode.contextValue = Constants.USS_SESSION_CONTEXT; + blockMocks.childNode.fullPath = "Throw Error"; + blockMocks.childNode.dirty = true; + blockMocks.childNode.profile = globalMocks.profileOne; + jest.spyOn(UssFSProvider.instance, "listFiles").mockImplementation(() => { + throw new Error("Throwing an error to check error handling for unit tests!"); + }); + + await blockMocks.childNode.getChildren(); + expect(globalMocks.showErrorMessage.mock.calls.length).toEqual(1); + expect(globalMocks.showErrorMessage.mock.calls[0][0]).toEqual( + "Retrieving response from USS list API Error: Throwing an error to check error handling for unit tests!" + ); + }); it("Tests that when passing a globalMocks.session node that is not dirty the node.getChildren() method is exited early", async () => { const globalMocks = createGlobalMocks(); diff --git a/packages/zowe-explorer/l10n/bundle.l10n.json b/packages/zowe-explorer/l10n/bundle.l10n.json index 809ba92fcc..69f6380b85 100644 --- a/packages/zowe-explorer/l10n/bundle.l10n.json +++ b/packages/zowe-explorer/l10n/bundle.l10n.json @@ -350,7 +350,7 @@ "Select a recent member to open": "Select a recent member to open", "No recent members found.": "No recent members found.", "Use the search button to display jobs": "Use the search button to display jobs", - "There are no JES spool messages to display": "There are no JES spool messages to display", + "No spool files found": "No spool files found", "No jobs found": "No jobs found", "Retrieving response from JES list API": "Retrieving response from JES list API", "$(list-ordered) Job ID (default)": "$(list-ordered) Job ID (default)", @@ -425,7 +425,6 @@ "Job label" ] }, - "Retrieving response from jobs-list": "Retrieving response from jobs-list", "Get JCL": "Get JCL", "Display in Tree": "Display in Tree", "Cancel": "Cancel", @@ -639,7 +638,6 @@ "Node label" ] }, - "Retrieving response from ds-list": "Retrieving response from ds-list", "Getting data set templates.": "Getting data set templates.", "Resetting data set templates array.": "Resetting data set templates array.", "Updating data set templates.": "Updating data set templates.", diff --git a/packages/zowe-explorer/l10n/poeditor.json b/packages/zowe-explorer/l10n/poeditor.json index 284d115e3d..6e38e9d54b 100644 --- a/packages/zowe-explorer/l10n/poeditor.json +++ b/packages/zowe-explorer/l10n/poeditor.json @@ -570,7 +570,7 @@ "Select a recent member to open": "", "No recent members found.": "", "Use the search button to display jobs": "", - "There are no JES spool messages to display": "", + "No spool files found": "", "No jobs found": "", "Retrieving response from JES list API": "", "$(list-ordered) Job ID (default)": "", @@ -604,7 +604,6 @@ "$(check) Filter cleared for {0}": "", "Enter local filter...": "", "$(check) Filter updated for {0}": "", - "Retrieving response from jobs-list": "", "Get JCL": "", "Display in Tree": "", "Cancel": "", @@ -678,7 +677,6 @@ "Invalid date format specified": "", "Enter a value to filter by": "", "Invalid filter specified": "", - "Retrieving response from ds-list": "", "Getting data set templates.": "", "Resetting data set templates array.": "", "Updating data set templates.": "", diff --git a/packages/zowe-explorer/src/trees/ZoweTreeProvider.ts b/packages/zowe-explorer/src/trees/ZoweTreeProvider.ts index 4c280aff72..5a2a99a44b 100644 --- a/packages/zowe-explorer/src/trees/ZoweTreeProvider.ts +++ b/packages/zowe-explorer/src/trees/ZoweTreeProvider.ts @@ -131,6 +131,7 @@ export class ZoweTreeProvider { if (isOpen) { this.mOnDidChangeTreeData.fire(element); } else { + // Don't mark as dirty when expanded to avoid duplicate refresh element.dirty = true; } } diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index 754845ff6f..973dc370e3 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -1514,10 +1514,4 @@ export class DatasetTree extends ZoweTreeProvider implemen await node.openDs(true, false, this); } } - - public async errorHandling(node: IZoweDatasetTreeNode, error: Error): Promise { - await AuthUtils.errorHandling(error, node.getProfileName(), vscode.l10n.t("Retrieving response from ds-list")); - AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getMvsApi(profile), node.getSessionNode()); - node.dirty = false; - } } diff --git a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts index cab7c066a1..752b70c158 100644 --- a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts @@ -145,12 +145,13 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { const spools = await this.getSpoolFiles(this.job); if (!spools.length) { const noSpoolNode = new ZoweSpoolNode({ - label: vscode.l10n.t("There are no JES spool messages to display"), + label: vscode.l10n.t("No spool files found"), collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: this, profile: this.getProfile(), + contextOverride: Constants.INFORMATION_CONTEXT, }); - return [noSpoolNode]; + return (this.children = [noSpoolNode]); } spools.forEach((spool) => { const procstep = spool.procstep ? spool.procstep : undefined; @@ -201,7 +202,7 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { profile: this.getProfile(), contextOverride: Constants.INFORMATION_CONTEXT, }); - return [noJobsNode]; + return (this.children = [noJobsNode]); } jobs.forEach((job) => { let nodeTitle: string; @@ -359,9 +360,9 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { private async getJobs(owner: string, prefix: string, searchId: string, status: string): Promise { ZoweLogger.trace("ZoweJobNode.getJobs called."); + const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); let jobsInternal: zosjobs.IJob[] = []; try { - const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); if (this.searchId.length > 0) { jobsInternal.push(await ZoweExplorerApiRegister.getJesApi(cachedProfile).getJob(searchId)); } else { @@ -405,9 +406,9 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { private async getSpoolFiles(job: zosjobs.IJob = this.job): Promise { ZoweLogger.trace("ZoweJobNode.getSpoolFiles called."); + const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); let spools: zosjobs.IJobFile[] = []; try { - const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); spools = (await ZoweExplorerApiRegister.getJesApi(cachedProfile).getSpoolFiles(job.jobname, job.jobid)) ?? []; // filter out all the objects which do not seem to be correct Job File Document types // see an issue #845 for the details diff --git a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts index 883821c344..a12ef927d8 100644 --- a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts +++ b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts @@ -206,7 +206,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); const response = await this.getUssFiles(cachedProfile); if (!response.success) { - return []; + return this.children; } // If search path has changed, invalidate all children @@ -245,7 +245,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { parentNode: this, parentPath: this.fullPath, profile: cachedProfile, - encoding: isDir ? undefined : await this.getEncodingInMap(`${this.fullPath}/${item.name as string}`), + encoding: isDir ? undefined : this.getEncodingInMap(`${this.fullPath}/${item.name as string}`), }); if (isDir) { // Create an entry for the USS folder if it doesn't exist. From 2a9f8a8d2740a427cd43b36f503b6fe76be92035 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 1 Oct 2024 12:22:30 -0400 Subject: [PATCH 14/25] Fix duplicate expand for jobs, clean up logging, update changelog Signed-off-by: Timothy Johnson --- packages/zowe-explorer/CHANGELOG.md | 2 +- .../src/trees/dataset/DatasetInit.ts | 3 +- .../src/trees/dataset/DatasetTree.ts | 2 +- .../src/trees/dataset/ZoweDatasetNode.ts | 2 +- .../zowe-explorer/src/trees/job/JobInit.ts | 4 +- .../zowe-explorer/src/trees/job/JobTree.ts | 90 ++++++++----------- .../src/trees/job/ZoweJobNode.ts | 10 +-- .../zowe-explorer/src/trees/uss/USSInit.ts | 4 +- .../zowe-explorer/src/trees/uss/USSTree.ts | 4 +- .../src/trees/uss/ZoweUSSNode.ts | 2 +- 10 files changed, 51 insertions(+), 72 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 7818357ddf..a6bcaf93eb 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -8,7 +8,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen ### Bug fixes -- Fixed an issue where a 401 error could occur when opening PDS members after updating credentials. [#3150](https://github.com/zowe/zowe-explorer-vscode/issues/3150) +- Fixed an issue where a 401 error could occur when opening PDS members after updating credentials within the same user session. [#3150](https://github.com/zowe/zowe-explorer-vscode/issues/3150) ## `3.0.0-next.202409251932` diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetInit.ts b/packages/zowe-explorer/src/trees/dataset/DatasetInit.ts index 3575bf2404..8a7a60d428 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetInit.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetInit.ts @@ -23,6 +23,7 @@ import { SharedUtils } from "../shared/SharedUtils"; export class DatasetInit { public static async createDatasetTree(log: imperative.Logger): Promise { + ZoweLogger.trace("DatasetInit.createDatasetTree called."); const tree = new DatasetTree(); await tree.initializeFavorites(log); await tree.addSession(); @@ -30,7 +31,7 @@ export class DatasetInit { } public static async initDatasetProvider(context: vscode.ExtensionContext): Promise { - ZoweLogger.trace("dataset.init.initDatasetProvider called."); + ZoweLogger.trace("DatasetInit.initDatasetProvider called."); context.subscriptions.push(vscode.workspace.registerFileSystemProvider(ZoweScheme.DS, DatasetFSProvider.instance, { isCaseSensitive: true })); const datasetProvider = await DatasetInit.createDatasetTree(ZoweLogger.log); if (datasetProvider == null) { diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index 973dc370e3..cec5c41ee0 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -987,7 +987,6 @@ export class DatasetTree extends ZoweTreeProvider implemen public async datasetFilterPrompt(node: IZoweDatasetTreeNode): Promise { ZoweLogger.trace("DatasetTree.datasetFilterPrompt called."); - ZoweLogger.debug(vscode.l10n.t("Prompting the user for a data set pattern")); let pattern: string; await this.checkCurrentProfile(node); const sessionNode = node; @@ -995,6 +994,7 @@ export class DatasetTree extends ZoweTreeProvider implemen if (Profiles.getInstance().validProfile !== Validation.ValidationType.INVALID) { if (SharedContext.isSessionNotFav(node)) { if (this.mHistory.getSearchHistory().length > 0) { + ZoweLogger.debug(vscode.l10n.t("Prompting the user for a data set pattern")); const createPick = new FilterDescriptor(DatasetTree.defaultDialogText); const items: vscode.QuickPickItem[] = this.mHistory.getSearchHistory().map((element) => new FilterItem({ text: element })); const quickpick = Gui.createQuickPick(); diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts index ea88a35622..be56000f4a 100644 --- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts @@ -203,7 +203,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod * @returns {Promise} */ public async getChildren(): Promise { - ZoweLogger.trace("ZoweDatasetNode.getChildren called."); + ZoweLogger.trace(`ZoweDatasetNode.getChildren called for ${this.label as string}.`); if (!this.pattern && SharedContext.isSessionNotFav(this)) { const placeholder = new ZoweDatasetNode({ label: vscode.l10n.t("Use the search button to display data sets"), diff --git a/packages/zowe-explorer/src/trees/job/JobInit.ts b/packages/zowe-explorer/src/trees/job/JobInit.ts index 7367955e39..2f4516ba08 100644 --- a/packages/zowe-explorer/src/trees/job/JobInit.ts +++ b/packages/zowe-explorer/src/trees/job/JobInit.ts @@ -32,7 +32,7 @@ export class JobInit { * @implements {vscode.TreeDataProvider} */ public static async createJobsTree(log: imperative.Logger): Promise { - ZoweLogger.trace("ZosJobsProvider.createJobsTree called."); + ZoweLogger.trace("JobInit.createJobsTree called."); const tree = new JobTree(); await tree.initializeJobsTree(log); await tree.addSession(); @@ -40,7 +40,7 @@ export class JobInit { } public static async initJobsProvider(context: vscode.ExtensionContext): Promise { - ZoweLogger.trace("job.init.initJobsProvider called."); + ZoweLogger.trace("JobInit.initJobsProvider called."); context.subscriptions.push(vscode.workspace.registerFileSystemProvider(ZoweScheme.Jobs, JobFSProvider.instance, { isCaseSensitive: false })); const jobsProvider = await JobInit.createJobsTree(ZoweLogger.log); if (jobsProvider == null) { diff --git a/packages/zowe-explorer/src/trees/job/JobTree.ts b/packages/zowe-explorer/src/trees/job/JobTree.ts index 176447cb89..e9a40471ef 100644 --- a/packages/zowe-explorer/src/trees/job/JobTree.ts +++ b/packages/zowe-explorer/src/trees/job/JobTree.ts @@ -32,21 +32,6 @@ import { AuthUtils } from "../../utils/AuthUtils"; import { PollProvider } from "./JobPollProvider"; import { Definitions } from "../../configuration/Definitions"; -/** - * Creates the Job tree that contains nodes of sessions, jobs and spool items - * - * @export - * @class ZosJobsProvider - * @implements {vscode.TreeDataProvider} - */ -export async function createJobsTree(log: imperative.Logger): Promise { - ZoweLogger.trace("ZosJobsProvider.createJobsTree called."); - const tree = new JobTree(); - await tree.initializeJobsTree(log); - await tree.addSession(); - return tree; -} - export class JobTree extends ZoweTreeProvider implements Types.IZoweJobTreeType { public static readonly JobId = "JobId: "; public static readonly Owner = "Owner: "; @@ -139,7 +124,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param {IZoweJobTreeNode} node */ public saveSearch(node: IZoweJobTreeNode): void { - ZoweLogger.trace("ZosJobsProvider.saveSearch called."); + ZoweLogger.trace("JobTree.saveSearch called."); node.contextValue = SharedContext.asFavorite(node); } public saveFile(_document: vscode.TextDocument): void { @@ -162,7 +147,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @returns {IZoweJobTreeNode[] | Promise} */ public async getChildren(element?: IZoweJobTreeNode | undefined): Promise { - ZoweLogger.trace("ZosJobsProvider.getChildren called."); + ZoweLogger.trace("JobTree.getChildren called."); if (element) { // solution for optional credentials. Owner is having error on initialization. if (element.owner === "") { @@ -185,7 +170,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @returns {vscode.TreeView} */ public getTreeView(): vscode.TreeView { - ZoweLogger.trace("ZosJobsProvider.getTreeView called."); + ZoweLogger.trace("JobTree.getTreeView called."); return this.treeView; } @@ -194,7 +179,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param profile the profile to add to the tree */ public async addSingleSession(profile: imperative.IProfileLoaded): Promise { - ZoweLogger.trace("ZosJobsProvider.addSingleSession called."); + ZoweLogger.trace("JobTree.addSingleSession called."); if (profile) { // If session is already added, do nothing if (this.mSessionNodes.find((tNode) => tNode.label.toString() === profile.name)) { @@ -231,7 +216,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public async delete(node: IZoweJobTreeNode): Promise { - ZoweLogger.trace("ZosJobsProvider.delete called."); + ZoweLogger.trace("JobTree.delete called."); await JobFSProvider.instance.delete(node.resourceUri, { recursive: false, deleteRemote: true }); const favNode = this.relabelFavoritedJob(node); @@ -248,7 +233,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @returns {IZoweJobTreeNode | undefined} Returns matching profile node if found. Otherwise, returns undefined. */ public findMatchingProfileInArray(jobsProvider: IZoweJobTreeNode[], profileName: string): IZoweJobTreeNode | undefined { - ZoweLogger.trace("ZosJobsProvider.findMatchingProfileInArray called."); + ZoweLogger.trace("JobTree.findMatchingProfileInArray called."); return jobsProvider.find((treeNode) => treeNode.label === profileName); } @@ -257,7 +242,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param node */ public findFavoritedNode(node: IZoweJobTreeNode): IZoweJobTreeNode { - ZoweLogger.trace("ZosJobsProvider.findFavoritedNode called."); + ZoweLogger.trace("JobTree.findFavoritedNode called."); const profileNodeInFavorites = this.findMatchingProfileInArray(this.mFavorites, node.getProfileName()); return profileNodeInFavorites?.children.find( (temp) => temp.label === node.getLabel().toString() && temp.contextValue.includes(node.contextValue) @@ -269,7 +254,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param node */ public findNonFavoritedNode(node: IZoweJobTreeNode): IZoweJobTreeNode { - ZoweLogger.trace("ZosJobsProvider.findNonFavoritedNode called."); + ZoweLogger.trace("JobTree.findNonFavoritedNode called."); const profileName = node.getProfileName(); const sessionNode = this.mSessionNodes.find((session) => session.label.toString().trim() === profileName); return sessionNode?.children.find((temp) => temp.label === node.label); @@ -280,7 +265,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param node */ public findEquivalentNode(node: IZoweJobTreeNode, isFavorite: boolean): IZoweJobTreeNode { - ZoweLogger.trace("ZosJobsProvider.findEquivalentNode called."); + ZoweLogger.trace("JobTree.findEquivalentNode called."); return isFavorite ? this.findNonFavoritedNode(node) : this.findFavoritedNode(node); } @@ -290,7 +275,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @returns {ZoweJobNode} */ public async createProfileNodeForFavs(profileName: string): Promise { - ZoweLogger.trace("ZosJobsProvider.createProfileNodeForFavs called."); + ZoweLogger.trace("JobTree.createProfileNodeForFavs called."); let favProfileNode: ZoweJobNode; try { const profile = Profiles.getInstance().loadNamedProfile(profileName); @@ -334,7 +319,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param log - Logger */ public async initializeJobsTree(log: imperative.Logger): Promise { - ZoweLogger.trace("ZosJobsProvider.initializeJobsTree called."); + ZoweLogger.trace("JobTree.initializeJobsTree called."); this.log = log; ZoweLogger.debug(vscode.l10n.t("Initializing profiles with jobs favorites.")); const lines: string[] = this.mHistory.readFavorites(); @@ -369,7 +354,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @returns IZoweJobTreeNode */ public initializeFavChildNodeForProfile(label: string, contextValue: string, parentNode: IZoweJobTreeNode): ZoweJobNode { - ZoweLogger.trace("ZosJobsProvider.initializeFavChildNodeForProfile called."); + ZoweLogger.trace("JobTree.initializeFavChildNodeForProfile called."); let favJob: ZoweJobNode; if (contextValue.startsWith(Constants.JOBS_JOB_CONTEXT)) { favJob = new ZoweJobNode({ @@ -406,7 +391,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param parentNode */ public async loadProfilesForFavorites(log: imperative.Logger, parentNode: IZoweJobTreeNode): Promise { - ZoweLogger.trace("ZosJobsProvider.loadProfilesForFavorites called."); + ZoweLogger.trace("JobTree.loadProfilesForFavorites called."); const profileName = parentNode.label as string; const updatedFavsForProfile: IZoweJobTreeNode[] = []; let profile: imperative.IProfileLoaded; @@ -490,7 +475,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param {IZoweJobTreeNode} node */ public async addFavorite(node: IZoweJobTreeNode): Promise { - ZoweLogger.trace("ZosJobsProvider.addFavorite called."); + ZoweLogger.trace("JobTree.addFavorite called."); let favJob: ZoweJobNode; // Get node's profile node in favorites const profileName = node.getProfileName(); @@ -544,7 +529,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param {IZoweJobTreeNode} node */ public async removeFavorite(node: IZoweJobTreeNode): Promise { - ZoweLogger.trace("ZosJobsProvider.removeFavorite called."); + ZoweLogger.trace("JobTree.removeFavorite called."); // Get node's profile node in favorites const profileName = node.getProfileName(); const profileNodeInFavorites = this.findMatchingProfileInArray(this.mFavorites, profileName); @@ -565,7 +550,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public updateFavorites(): void { - ZoweLogger.trace("ZosJobsProvider.updateFavorites called."); + ZoweLogger.trace("JobTree.updateFavorites called."); const favoritesArray = []; this.mFavorites.forEach((profileNode) => { profileNode.children.forEach((fav) => { @@ -589,7 +574,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param userSelected True if the function is being called directly because the user selected to remove the profile from Favorites */ public async removeFavProfile(profileName: string, userSelected: boolean): Promise { - ZoweLogger.trace("ZosJobsProvider.removeFavProfile called."); + ZoweLogger.trace("JobTree.removeFavProfile called."); // If user selected the "Remove profile from Favorites option", confirm they are okay with deleting all favorited items for that profile. let cancelled = false; if (userSelected) { @@ -626,17 +611,17 @@ export class JobTree extends ZoweTreeProvider implements Types } public removeSearchHistory(name: string): void { - ZoweLogger.trace("ZosJobsProvider.removeSearchHistory called."); + ZoweLogger.trace("JobTree.removeSearchHistory called."); this.mHistory.removeSearchHistory(name); } public removeSession(name: string): void { - ZoweLogger.trace("ZosJobsProvider.removeSession called."); + ZoweLogger.trace("JobTree.removeSession called."); this.mHistory.removeSession(name); } public resetSearchHistory(): void { - ZoweLogger.trace("ZosJobsProvider.resetSearchHistory called."); + ZoweLogger.trace("JobTree.resetSearchHistory called."); this.mHistory.resetSearchHistory(); } @@ -656,7 +641,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public async getUserJobsMenuChoice(): Promise { - ZoweLogger.trace("ZosJobsProvider.getUserJobsMenuChoice called."); + ZoweLogger.trace("JobTree.getUserJobsMenuChoice called."); const items: FilterItem[] = this.mHistory .getSearchHistory() .map((element) => new FilterItem({ text: element, menuType: Definitions.JobPickerTypes.History })); @@ -677,7 +662,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public async getUserSearchQueryInput(choice: FilterItem, node: IZoweJobTreeNode): Promise { - ZoweLogger.trace("ZosJobsProvider.getUserSearchQueryInput called."); + ZoweLogger.trace("JobTree.getUserSearchQueryInput called."); if (!choice) { return undefined; } @@ -703,7 +688,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public async applyRegularSessionSearchLabel(node: IZoweJobTreeNode): Promise { - ZoweLogger.trace("ZosJobsProvider.applyRegularSessionsSearchLabel called."); + ZoweLogger.trace("JobTree.applyRegularSessionsSearchLabel called."); const choice = await this.getUserJobsMenuChoice(); const searchCriteriaObj = await this.getUserSearchQueryInput(choice, node); @@ -721,7 +706,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public async handleSearchByJobId(jobId?: string): Promise { - ZoweLogger.trace("ZosJobsProvider.handleSearchByJobId called."); + ZoweLogger.trace("JobTree.handleSearchByJobId called."); const options = { prompt: vscode.l10n.t("Enter a job ID"), value: jobId, @@ -740,7 +725,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public parseJobSearchQuery(searchCriteria: string): Definitions.IJobSearchCriteria { - ZoweLogger.trace("ZosJobsProvider.parseJobSearchQuery called."); + ZoweLogger.trace("JobTree.parseJobSearchQuery called."); const searchCriteriaObj: Definitions.IJobSearchCriteria = { Owner: undefined, Prefix: undefined, @@ -769,7 +754,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public getPopulatedPickerValues(searchObj: Definitions.IJobSearchCriteria): Definitions.IJobPickerOption[] { - ZoweLogger.trace("ZosJobsProvider.getPopulatedPickerValues called."); + ZoweLogger.trace("JobTree.getPopulatedPickerValues called."); const historyPopulatedItems = this.JOB_PROPERTIES; historyPopulatedItems.forEach((prop) => { if (prop.key === "owner") { @@ -786,7 +771,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public async applySavedFavoritesSearchLabel(node): Promise { - ZoweLogger.trace("ZosJobsProvider.applySavedFavoritesSearchLabel called."); + ZoweLogger.trace("JobTree.applySavedFavoritesSearchLabel called."); // executing search from saved search in favorites const searchCriteria = node.label as string; const session = node.getProfileName(); @@ -808,7 +793,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @returns {Promise} */ public async searchPrompt(node: IZoweJobTreeNode): Promise { - ZoweLogger.trace("ZosJobsProvider.searchPrompt called."); + ZoweLogger.trace("JobTree.searchPrompt called."); await this.checkCurrentProfile(node); let searchCriteria: string = ""; if (Profiles.getInstance().validProfile !== Validation.ValidationType.INVALID) { @@ -847,7 +832,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public async onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent): Promise { - ZoweLogger.trace("ZosJobsProvider.onDidChangeConfiguration called."); + ZoweLogger.trace("JobTree.onDidChangeConfiguration called."); if (e.affectsConfiguration(JobTree.persistenceSchema)) { const setting: any = { ...SettingsConfig.getDirectValue(JobTree.persistenceSchema), @@ -861,7 +846,7 @@ export class JobTree extends ZoweTreeProvider implements Types } public deleteSession(node: IZoweJobTreeNode, hideFromAllTrees?: boolean): void { - ZoweLogger.trace("ZosJobsProvider.deleteSession called."); + ZoweLogger.trace("JobTree.deleteSession called."); super.deleteSession(node, hideFromAllTrees); } @@ -872,7 +857,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param jobid - A specific jobid search item */ public createSearchLabel(owner: string, prefix: string, jobid: string, status: string): string { - ZoweLogger.trace("ZosJobsProvider.createSearchLabel called."); + ZoweLogger.trace("JobTree.createSearchLabel called."); const alphaNumeric = new RegExp("^w+$"); if (jobid && !alphaNumeric.exec(jobid.trim())) { return JobTree.JobId + jobid.toUpperCase().trim(); @@ -892,12 +877,12 @@ export class JobTree extends ZoweTreeProvider implements Types } public addFileHistory(_criteria: string): void { - ZoweLogger.trace("ZosJobsProvider.addFileHistory called."); + ZoweLogger.trace("JobTree.addFileHistory called."); throw new Error("Method not implemented."); } private async setJobStatus(node: IZoweJobTreeNode): Promise { - ZoweLogger.trace("ZosJobsProvider.setJobStatus called."); + ZoweLogger.trace("JobTree.setJobStatus called."); const jobStatusSelection = ZoweExplorerApiRegister.getJesApi(node.getProfile()).getJobsByParameters ? Constants.JOB_STATUS : Constants.JOB_STATUS_UNSUPPORTED; @@ -912,7 +897,7 @@ export class JobTree extends ZoweTreeProvider implements Types jobProperties: Definitions.IJobPickerOption[], node: IZoweJobTreeNode ): Promise { - ZoweLogger.trace("ZosJobsProvider.handleEditingMultiJobParameters called."); + ZoweLogger.trace("JobTree.handleEditingMultiJobParameters called."); const editableItems: vscode.QuickPickItem[] = [new FilterItem({ text: JobTree.submitJobQueryLabel, show: true }), Constants.SEPARATORS.BLANK]; jobProperties.forEach((prop) => { if (prop.key === "owner" && !prop.value) { @@ -949,7 +934,6 @@ export class JobTree extends ZoweTreeProvider implements Types }; this.resetJobProperties(jobProperties); - await TreeViewUtils.expandNode(node, this); return searchCriteriaObj; } default: { @@ -967,7 +951,7 @@ export class JobTree extends ZoweTreeProvider implements Types return this.handleEditingMultiJobParameters(jobProperties, node); } private resetJobProperties(jobProperties: Definitions.IJobPickerOption[]): Definitions.IJobPickerOption[] { - ZoweLogger.trace("ZosJobsProvider.resetJobProperties called."); + ZoweLogger.trace("JobTree.resetJobProperties called."); jobProperties.forEach((prop) => { if (prop.key === "owner") { prop.value = ""; @@ -988,7 +972,7 @@ export class JobTree extends ZoweTreeProvider implements Types * @param storedSearch - The original search string */ private applySearchLabelToNode(node: IZoweJobTreeNode, storedSearchObj: Definitions.IJobSearchCriteria): void { - ZoweLogger.trace("ZosJobsProvider.applySearchLabelToNode called."); + ZoweLogger.trace("JobTree.applySearchLabelToNode called."); if (storedSearchObj) { node.searchId = storedSearchObj.JobId || ""; node.owner = storedSearchObj.Owner || "*"; @@ -998,7 +982,7 @@ export class JobTree extends ZoweTreeProvider implements Types } private relabelFavoritedJob(node: IZoweJobTreeNode): IZoweJobTreeNode { - ZoweLogger.trace("ZosJobsProvider.relabelFavoritedJob called."); + ZoweLogger.trace("JobTree.relabelFavoritedJob called."); node.label = node.label.toString().substring(0, node.label.toString().lastIndexOf(")") + 1); return node; } diff --git a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts index 752b70c158..1951f698be 100644 --- a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts @@ -119,23 +119,19 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { * @returns {Promise} */ public async getChildren(): Promise { - const thisSessionNode = this.getSessionNode(); - ZoweLogger.trace(`ZoweJobNode.getChildren called for ${String(thisSessionNode.label)}.`); - if (this?.filter !== undefined) { - return this.children; - } + ZoweLogger.trace(`ZoweJobNode.getChildren called for ${this.label as string}.`); if (SharedContext.isSession(this) && !this.filtered && !SharedContext.isFavorite(this)) { const placeholder = new ZoweJobNode({ label: vscode.l10n.t("Use the search button to display jobs"), collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: this, - profile: thisSessionNode.getProfile(), + profile: this.getProfile(), contextOverride: Constants.INFORMATION_CONTEXT, }); return [placeholder]; } - if (!this.dirty) { + if (!this.dirty || this.filter !== undefined) { return this.children; } diff --git a/packages/zowe-explorer/src/trees/uss/USSInit.ts b/packages/zowe-explorer/src/trees/uss/USSInit.ts index 638c98a1b9..91bba48005 100644 --- a/packages/zowe-explorer/src/trees/uss/USSInit.ts +++ b/packages/zowe-explorer/src/trees/uss/USSInit.ts @@ -27,7 +27,7 @@ export class USSInit { * @export */ public static async createUSSTree(log: imperative.Logger): Promise { - ZoweLogger.trace("uss.USSTree.createUSSTree called."); + ZoweLogger.trace("USSInit.createUSSTree called."); const tree = new USSTree(); await tree.initializeFavorites(log); await tree.addSession(); @@ -35,7 +35,7 @@ export class USSInit { } public static async initUSSProvider(context: vscode.ExtensionContext): Promise { - ZoweLogger.trace("init.initUSSProvider called."); + ZoweLogger.trace("USSInit.initUSSProvider called."); context.subscriptions.push(vscode.workspace.registerFileSystemProvider(ZoweScheme.USS, UssFSProvider.instance, { isCaseSensitive: true })); const ussFileProvider: USSTree = await USSInit.createUSSTree(ZoweLogger.log); diff --git a/packages/zowe-explorer/src/trees/uss/USSTree.ts b/packages/zowe-explorer/src/trees/uss/USSTree.ts index 7670473f0c..70b259e6fd 100644 --- a/packages/zowe-explorer/src/trees/uss/USSTree.ts +++ b/packages/zowe-explorer/src/trees/uss/USSTree.ts @@ -713,14 +713,12 @@ export class USSTree extends ZoweTreeProvider implements Types */ public async filterPrompt(node: IZoweUSSTreeNode): Promise { ZoweLogger.trace("USSTree.filterPrompt called."); - if (this.log) { - ZoweLogger.debug(vscode.l10n.t("Prompting the user for a USS path")); - } await this.checkCurrentProfile(node); if (Profiles.getInstance().validProfile !== Validation.ValidationType.INVALID) { let remotepath: string; if (SharedContext.isSessionNotFav(node)) { if (this.mHistory.getSearchHistory().length > 0) { + ZoweLogger.debug(vscode.l10n.t("Prompting the user for a USS path")); const createPick = new FilterDescriptor(USSTree.defaultDialogText); const items: vscode.QuickPickItem[] = this.mHistory.getSearchHistory().map((element) => new FilterItem({ text: element })); const quickpick = Gui.createQuickPick(); diff --git a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts index a12ef927d8..892b82076e 100644 --- a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts +++ b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts @@ -179,7 +179,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { * @returns {Promise} */ public async getChildren(): Promise { - ZoweLogger.trace("ZoweUSSNode.getChildren called."); + ZoweLogger.trace(`ZoweUSSNode.getChildren called for ${this.label as string}.`); if ((!this.fullPath && SharedContext.isSession(this)) || SharedContext.isDocument(this)) { const placeholder = new ZoweUSSNode({ label: vscode.l10n.t("Use the search button to list USS files"), From 0f97a77969382b4d7d22f41d1cc64cdef3aeca82 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 1 Oct 2024 12:43:34 -0400 Subject: [PATCH 15/25] Fix log statements in wrong place Signed-off-by: Timothy Johnson --- packages/zowe-explorer/src/configuration/Profiles.ts | 5 +---- packages/zowe-explorer/src/trees/dataset/DatasetTree.ts | 2 +- packages/zowe-explorer/src/trees/uss/USSTree.ts | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/zowe-explorer/src/configuration/Profiles.ts b/packages/zowe-explorer/src/configuration/Profiles.ts index 8622b3a4f9..80816a0947 100644 --- a/packages/zowe-explorer/src/configuration/Profiles.ts +++ b/packages/zowe-explorer/src/configuration/Profiles.ts @@ -822,10 +822,7 @@ export class Profiles extends ProfilesCache { const configApi = profInfo.getTeamConfig(); const profAttrs = await this.getProfileFromConfig(profileName); if (profAttrs.profLoc.jsonLoc) { - configApi.set( - `${profAttrs.profLoc.jsonLoc}.secure`, - loginTokenType?.startsWith("apimlAuthenticationToken") ? [] : ["tokenValue"] - ); + configApi.set(`${profAttrs.profLoc.jsonLoc}.secure`, loginTokenType?.startsWith("apimlAuthenticationToken") ? [] : ["tokenValue"]); } configApi.delete(profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "user")?.argLoc.jsonLoc); configApi.delete(profInfo.mergeArgsForProfile(profAttrs).knownArgs.find((arg) => arg.argName === "password")?.argLoc.jsonLoc); diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index cec5c41ee0..a61d1fa116 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -993,8 +993,8 @@ export class DatasetTree extends ZoweTreeProvider implemen if (Profiles.getInstance().validProfile !== Validation.ValidationType.INVALID) { if (SharedContext.isSessionNotFav(node)) { + ZoweLogger.debug(vscode.l10n.t("Prompting the user for a data set pattern")); if (this.mHistory.getSearchHistory().length > 0) { - ZoweLogger.debug(vscode.l10n.t("Prompting the user for a data set pattern")); const createPick = new FilterDescriptor(DatasetTree.defaultDialogText); const items: vscode.QuickPickItem[] = this.mHistory.getSearchHistory().map((element) => new FilterItem({ text: element })); const quickpick = Gui.createQuickPick(); diff --git a/packages/zowe-explorer/src/trees/uss/USSTree.ts b/packages/zowe-explorer/src/trees/uss/USSTree.ts index 70b259e6fd..1342aca85c 100644 --- a/packages/zowe-explorer/src/trees/uss/USSTree.ts +++ b/packages/zowe-explorer/src/trees/uss/USSTree.ts @@ -717,8 +717,8 @@ export class USSTree extends ZoweTreeProvider implements Types if (Profiles.getInstance().validProfile !== Validation.ValidationType.INVALID) { let remotepath: string; if (SharedContext.isSessionNotFav(node)) { + ZoweLogger.debug(vscode.l10n.t("Prompting the user for a USS path")); if (this.mHistory.getSearchHistory().length > 0) { - ZoweLogger.debug(vscode.l10n.t("Prompting the user for a USS path")); const createPick = new FilterDescriptor(USSTree.defaultDialogText); const items: vscode.QuickPickItem[] = this.mHistory.getSearchHistory().map((element) => new FilterItem({ text: element })); const quickpick = Gui.createQuickPick(); From 2af831ae81a57975341f8ed4bb364495c5495f8e Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 1 Oct 2024 15:32:24 -0400 Subject: [PATCH 16/25] Use contextOverride when building session nodes Signed-off-by: Timothy Johnson --- packages/zowe-explorer/src/trees/dataset/DatasetTree.ts | 6 +++++- packages/zowe-explorer/src/trees/job/JobTree.ts | 2 +- packages/zowe-explorer/src/trees/uss/USSTree.ts | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index a61d1fa116..c38dbc1115 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -441,8 +441,12 @@ export class DatasetTree extends ZoweTreeProvider implemen collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, session, profile, + contextOverride: Constants.DS_SESSION_CONTEXT, }); - node.contextValue = Constants.DS_SESSION_CONTEXT + (profile.type !== "zosmf" ? `.profile=${profile.type}.` : ""); + if (profile.type !== "zosmf") { + // TODO: Why do we inject profiles in context value only for DS tree? + node.contextValue += `.profile=${profile.type}.`; + } await this.refreshHomeProfileContext(node); const icon = IconGenerator.getIconByNode(node); if (icon) { diff --git a/packages/zowe-explorer/src/trees/job/JobTree.ts b/packages/zowe-explorer/src/trees/job/JobTree.ts index e9a40471ef..0262ab49ec 100644 --- a/packages/zowe-explorer/src/trees/job/JobTree.ts +++ b/packages/zowe-explorer/src/trees/job/JobTree.ts @@ -202,8 +202,8 @@ export class JobTree extends ZoweTreeProvider implements Types collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, session, profile, + contextOverride: Constants.JOBS_SESSION_CONTEXT, }); - node.contextValue = Constants.JOBS_SESSION_CONTEXT; await this.refreshHomeProfileContext(node); const icon = IconGenerator.getIconByNode(node); if (icon) { diff --git a/packages/zowe-explorer/src/trees/uss/USSTree.ts b/packages/zowe-explorer/src/trees/uss/USSTree.ts index 1342aca85c..3f7945431e 100644 --- a/packages/zowe-explorer/src/trees/uss/USSTree.ts +++ b/packages/zowe-explorer/src/trees/uss/USSTree.ts @@ -495,8 +495,8 @@ export class USSTree extends ZoweTreeProvider implements Types collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, session, profile, + contextOverride: Constants.USS_SESSION_CONTEXT, }); - node.contextValue = Constants.USS_SESSION_CONTEXT; await this.refreshHomeProfileContext(node); const icon = IconGenerator.getIconByNode(node); if (icon) { From 08f705820dfeec474a8bd744b3b0772783da4687 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 1 Oct 2024 16:11:20 -0400 Subject: [PATCH 17/25] Add tests for memberPattern on ds node Signed-off-by: Timothy Johnson --- .../dataset/ZoweDatasetNode.unit.test.ts | 44 ++++++++++++++++++- .../src/trees/dataset/ZoweDatasetNode.ts | 2 +- .../src/trees/uss/ZoweUSSNode.ts | 2 +- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts index 5ece492b10..cf581061c8 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts @@ -330,9 +330,9 @@ describe("ZoweDatasetNode Unit Tests", () => { parentNode: sessionNode, session, profile: profileOne, + contextOverride: Constants.DS_PDS_CONTEXT, }); pds.dirty = true; - pds.contextValue = Constants.DS_PDS_CONTEXT; const allMembers = jest.fn().mockReturnValueOnce({ success: true, apiResponse: { @@ -352,6 +352,48 @@ describe("ZoweDatasetNode Unit Tests", () => { getSessionNodeSpy.mockRestore(); getStatsMock.mockRestore(); }); + it("Testing what happens when response has multiple members and member pattern is set", async () => { + Object.defineProperty(Profiles, "getInstance", { + value: jest.fn(() => { + return { + loadNamedProfile: jest.fn().mockReturnValue(profileOne), + }; + }), + }); + + const getStatsMock = jest.spyOn(ZoweDatasetNode.prototype, "getStats").mockImplementation(); + + const sessionNode = createDatasetSessionNode(session, profileOne); + const getSessionNodeSpy = jest.spyOn(ZoweDatasetNode.prototype, "getSessionNode").mockReturnValue(sessionNode); + // Creating a rootNode + const pds = new ZoweDatasetNode({ + label: "[root]: something", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + parentNode: sessionNode, + session, + profile: profileOne, + contextOverride: Constants.DS_PDS_CONTEXT, + }); + pds.dirty = true; + pds.memberPattern = "MEM*"; + const allMembers = jest.fn().mockReturnValueOnce({ + success: true, + apiResponse: { + items: [{ member: "MEMBER1" }], + returnedRows: 1, + }, + }); + jest.spyOn(DatasetFSProvider.instance, "exists").mockReturnValue(false); + jest.spyOn(DatasetFSProvider.instance, "writeFile").mockImplementation(); + jest.spyOn(DatasetFSProvider.instance, "createDirectory").mockImplementation(); + Object.defineProperty(zosfiles.List, "allMembers", { value: allMembers }); + const pdsChildren = await pds.getChildren(); + expect(pdsChildren[0].label).toEqual("MEMBER1"); + expect(pdsChildren[0].contextValue).toEqual(Constants.DS_MEMBER_CONTEXT); + expect(allMembers).toHaveBeenCalledWith(expect.any(imperative.Session), pds.label, expect.objectContaining({ pattern: pds.memberPattern })); + getSessionNodeSpy.mockRestore(); + getStatsMock.mockRestore(); + }); /************************************************************************************************************* * Profile properties have changed diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts index be56000f4a..6764cf73ed 100644 --- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts @@ -212,7 +212,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod contextOverride: Constants.INFORMATION_CONTEXT, profile: this.getProfile(), }); - return [placeholder]; + return (this.children = [placeholder]); } if (SharedContext.isDocument(this) || SharedContext.isInformation(this)) { return []; diff --git a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts index 892b82076e..3e1921fdcd 100644 --- a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts +++ b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts @@ -187,7 +187,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { parentNode: this, contextOverride: Constants.INFORMATION_CONTEXT, }); - return [placeholder]; + return (this.children = [placeholder]); } if (!this.dirty) { From 470c126d1060dcf30866d41a31b71399e8a26c4b Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Wed, 2 Oct 2024 09:20:43 -0400 Subject: [PATCH 18/25] Add unit test for USS placeholder node Signed-off-by: Timothy Johnson --- .../dataset/ZoweDatasetNode.unit.test.ts | 10 ---------- .../trees/job/ZoweJobNode.unit.test.ts | 7 +------ .../trees/uss/ZoweUSSNode.unit.test.ts | 19 +++++++++++++++++-- .../src/trees/dataset/ZoweDatasetNode.ts | 1 - .../src/trees/job/ZoweJobNode.ts | 1 - 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts index cf581061c8..70482e2a88 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts @@ -206,13 +206,8 @@ describe("ZoweDatasetNode Unit Tests", () => { label: "Use the search button to display data sets", collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: rootNode, - profile: profileOne, contextOverride: Constants.INFORMATION_CONTEXT, }); - infoChild.command = { - command: "zowe.placeholderCommand", - title: "Placeholder", - }; rootNode.dirty = false; expect(await rootNode.getChildren()).toEqual([infoChild]); }); @@ -233,13 +228,8 @@ describe("ZoweDatasetNode Unit Tests", () => { label: "Use the search button to display data sets", collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: rootNode, - profile: profileOne, contextOverride: Constants.INFORMATION_CONTEXT, }); - infoChild.command = { - command: "zowe.placeholderCommand", - title: "Placeholder", - }; expect(await rootNode.getChildren()).toEqual([infoChild]); }); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts index 25ee26faa6..d1025b5ccf 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts @@ -416,19 +416,14 @@ describe("ZoweJobNode unit tests - Function getChildren", () => { expect(spoolFiles[0].owner).toEqual("*"); }); - it("should return a new job if not owner and is a session", async () => { + it("should return placeholder node if session node expanded without search params", async () => { const globalMocks = await createGlobalMocks(); const expectedJob = new ZoweJobNode({ label: "Use the search button to display jobs", collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: globalMocks.testJobNode, - profile: globalMocks.testProfile, contextOverride: Constants.INFORMATION_CONTEXT, }); - expectedJob.command = { - command: "zowe.placeholderCommand", - title: "Placeholder", - }; globalMocks.testJobNode._owner = null; jest.spyOn(SharedContext, "isSession").mockReturnValueOnce(true); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts index c1cc0c3027..34e76bc095 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts @@ -831,7 +831,7 @@ describe("ZoweUSSNode Unit Tests - Function node.getChildren()", () => { setAttrsMock.mockRestore(); }); - it("Tests that error is thrown when node label is blank", async () => { + it("Tests that error is thrown when node label is blank", () => { const globalMocks = createGlobalMocks(); const blockMocks = createBlockMocks(globalMocks); @@ -861,7 +861,7 @@ describe("ZoweUSSNode Unit Tests - Function node.getChildren()", () => { ); }); - it("Tests that when passing a globalMocks.session node that is not dirty the node.getChildren() method is exited early", async () => { + it("Tests that when passing a session node that is not dirty the node.getChildren() method is exited early", async () => { const globalMocks = createGlobalMocks(); const blockMocks = createBlockMocks(globalMocks); @@ -871,6 +871,21 @@ describe("ZoweUSSNode Unit Tests - Function node.getChildren()", () => { expect(await blockMocks.rootNode.getChildren()).toEqual([]); }); + + it("Tests that when passing a session node without path the node.getChildren() method is exited early", async () => { + const globalMocks = createGlobalMocks(); + const blockMocks = createBlockMocks(globalMocks); + + blockMocks.rootNode.contextValue = Constants.USS_SESSION_CONTEXT; + const expectedNode = new ZoweUSSNode({ + label: "Use the search button to list USS files", + collapsibleState: vscode.TreeItemCollapsibleState.None, + parentNode: blockMocks.rootNode, + contextOverride: Constants.INFORMATION_CONTEXT, + }); + + expect(await blockMocks.rootNode.getChildren()).toEqual([expectedNode]); + }); }); describe("ZoweUSSNode Unit Tests - Function node.openUSS()", () => { diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts index 6764cf73ed..df3a65ccee 100644 --- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts @@ -210,7 +210,6 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: this, contextOverride: Constants.INFORMATION_CONTEXT, - profile: this.getProfile(), }); return (this.children = [placeholder]); } diff --git a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts index 1951f698be..c36a713b36 100644 --- a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts @@ -125,7 +125,6 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { label: vscode.l10n.t("Use the search button to display jobs"), collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: this, - profile: this.getProfile(), contextOverride: Constants.INFORMATION_CONTEXT, }); return [placeholder]; From 23c7fa1033cf6dcb900f60c9c962336b6f6362e2 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Mon, 7 Oct 2024 12:45:32 -0400 Subject: [PATCH 19/25] Fix extra tree refresh on credential update Signed-off-by: Timothy Johnson --- .../trees/dataset/DatasetTree.unit.test.ts | 1 - .../trees/shared/SharedInit.unit.test.ts | 20 +++++++++------- .../__unit__/utils/ProfilesUtils.unit.test.ts | 2 +- .../src/configuration/Constants.ts | 3 +-- .../src/trees/dataset/DatasetTree.ts | 24 +++++++++---------- .../src/trees/shared/SharedInit.ts | 8 ++++--- .../zowe-explorer/src/trees/uss/USSTree.ts | 10 ++++---- .../zowe-explorer/src/utils/ProfilesUtils.ts | 7 +++++- 8 files changed, 41 insertions(+), 34 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts index 1a4471847a..1cb7e8149f 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts @@ -3314,7 +3314,6 @@ describe("Dataset Tree Unit Tests - Function applyPatternsToChildren", () => { const withProfileMock = jest.spyOn(SharedContext, "withProfile").mockImplementation((child) => String(child.contextValue)); testTree.applyPatternsToChildren(fakeChildren as any[], [{ dsn: "HLQ.PROD.PDS", member: "A*" }], fakeSessionNode as any); expect(SharedContext.isFilterFolder(fakeChildren[0])).toBe(true); - expect(fakeSessionNode.dirty).toBe(true); withProfileMock.mockRestore(); }); it("applies a closed filter folder icon to the PDS if collapsed", () => { diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts index ecd155e9f1..600c3a11d9 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedInit.unit.test.ts @@ -323,7 +323,8 @@ describe("Test src/shared/extension", () => { describe("watchConfigProfile", () => { let context: any; let watcherPromise: any; - const spyReadFile = jest.fn().mockReturnValue("test"); + const fakeUri = { fsPath: "fsPath" }; + const spyReadFile = jest.fn().mockReturnValue(Buffer.from("test")); const mockEmitter = jest.fn(); const watcher: any = { onDidCreate: jest.fn(), @@ -333,9 +334,12 @@ describe("Test src/shared/extension", () => { beforeEach(() => { context = { subscriptions: [] }; jest.clearAllMocks(); - Object.defineProperty(vscode.workspace, "workspaceFolders", { value: [{ uri: { fsPath: "fsPath" } }], configurable: true }); + Object.defineProperty(vscode.workspace, "workspaceFolders", { value: [{ uri: fakeUri }], configurable: true }); Object.defineProperty(vscode.workspace, "fs", { value: { readFile: spyReadFile }, configurable: true }); - Object.defineProperty(Constants, "SAVED_PROFILE_CONTENTS", { value: "test", configurable: true }); + Object.defineProperty(Constants, "SAVED_PROFILE_CONTENTS", { + value: new Map(Object.entries({ [fakeUri.fsPath]: Buffer.from("test") })), + configurable: true, + }); jest.spyOn(vscode.workspace, "createFileSystemWatcher").mockReturnValue(watcher); jest.spyOn(ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter, "fire").mockImplementation(mockEmitter); }); @@ -366,23 +370,23 @@ describe("Test src/shared/extension", () => { it("should be able to trigger onDidChange listener", async () => { const spyRefreshAll = jest.spyOn(SharedActions, "refreshAll").mockImplementation(); - watcher.onDidChange.mockImplementationOnce((fun) => (watcherPromise = fun("uri"))); + watcher.onDidChange.mockImplementationOnce((fun) => (watcherPromise = fun(fakeUri))); SharedInit.watchConfigProfile(context); await watcherPromise; expect(context.subscriptions).toContain(watcher); - expect(spyReadFile).toHaveBeenCalledWith("uri"); + expect(spyReadFile).toHaveBeenCalledWith(fakeUri); expect(spyRefreshAll).not.toHaveBeenCalled(); expect(mockEmitter).not.toHaveBeenCalled(); }); it("should be able to trigger onDidChange listener with changes", async () => { const spyRefreshAll = jest.spyOn(SharedActions, "refreshAll").mockImplementation(); - spyReadFile.mockReturnValueOnce("other"); - watcher.onDidChange.mockImplementationOnce((fun) => (watcherPromise = fun("uri"))); + spyReadFile.mockReturnValueOnce(Buffer.from("other")); + watcher.onDidChange.mockImplementationOnce((fun) => (watcherPromise = fun(fakeUri))); SharedInit.watchConfigProfile(context); await watcherPromise; expect(context.subscriptions).toContain(watcher); - expect(spyReadFile).toHaveBeenCalledWith("uri"); + expect(spyReadFile).toHaveBeenCalledWith(fakeUri); expect(spyRefreshAll).toHaveBeenCalledTimes(1); expect(mockEmitter).toHaveBeenCalledTimes(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 520639e15b..6044ce1d46 100644 --- a/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/utils/ProfilesUtils.unit.test.ts @@ -285,7 +285,7 @@ describe("ProfilesUtils unit tests", () => { const mockReadProfilesFromDisk = jest.fn(); const profInfoSpy = jest.spyOn(ProfilesUtils, "getProfileInfo").mockReturnValue({ readProfilesFromDisk: mockReadProfilesFromDisk, - getTeamConfig: () => ({ exists: true }), + getTeamConfig: () => ({ exists: true, layers: [] }), } as never); await expect(ProfilesUtils.readConfigFromDisk()).resolves.not.toThrow(); expect(mockReadProfilesFromDisk).toHaveBeenCalledTimes(1); diff --git a/packages/zowe-explorer/src/configuration/Constants.ts b/packages/zowe-explorer/src/configuration/Constants.ts index 493ba07376..24a965867f 100644 --- a/packages/zowe-explorer/src/configuration/Constants.ts +++ b/packages/zowe-explorer/src/configuration/Constants.ts @@ -16,7 +16,6 @@ import { imperative, PersistenceSchemaEnum } from "@zowe/zowe-explorer-api"; import type { Profiles } from "./Profiles"; export class Constants { - public static CONFIG_PATH: string; public static readonly COMMAND_COUNT = 99; public static readonly MAX_SEARCH_HISTORY = 5; public static readonly MAX_FILE_HISTORY = 10; @@ -82,7 +81,7 @@ export class Constants { public static DS_NAME_REGEX_CHECK = /^[a-zA-Z#@$][a-zA-Z0-9#@$-]{0,7}(\.[a-zA-Z#@$][a-zA-Z0-9#@$-]{0,7})*$/; public static MEMBER_NAME_REGEX_CHECK = /^[a-zA-Z#@$][a-zA-Z0-9#@$]{0,7}$/; public static ACTIVATED = false; - public static SAVED_PROFILE_CONTENTS = new Uint8Array(); + public static SAVED_PROFILE_CONTENTS = new Map(); public static IGNORE_VAULT_CHANGE = false; public static readonly JOBS_MAX_PREFIX = 8; public static PROFILES_CACHE: Profiles; diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index c38dbc1115..b99db6fd30 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -391,13 +391,13 @@ export class DatasetTree extends ZoweTreeProvider implemen for (const favorite of favsForProfile) { // If profile and session already exists for favorite node, add to updatedFavsForProfile and go to next array item if (favorite.getProfile() && favorite.getSession()) { - updatedFavsForProfile.push(favorite as IZoweDatasetTreeNode); + updatedFavsForProfile.push(favorite); continue; } // If no profile/session for favorite node yet, then add session and profile to favorite node: favorite.setProfileToChoice(profile); favorite.setSessionToChoice(session); - updatedFavsForProfile.push(favorite as IZoweDatasetTreeNode); + updatedFavsForProfile.push(favorite); } // This updates the profile node's children in the this.mFavorites array, as well. return updatedFavsForProfile; @@ -559,7 +559,7 @@ export class DatasetTree extends ZoweTreeProvider implemen if (matchingNode) { matchingNode.label = afterLabel; matchingNode.tooltip = afterLabel; - this.refreshElement(matchingNode as IZoweDatasetTreeNode); + this.refreshElement(matchingNode); } } } @@ -880,10 +880,10 @@ export class DatasetTree extends ZoweTreeProvider implemen if (session.children) { for (const node of session.children) { if (node.contextValue !== Constants.INFORMATION_CONTEXT) { - loadedItems.push(node as IZoweDatasetTreeNode); + loadedItems.push(node); for (const member of node.children) { if (member.contextValue !== Constants.INFORMATION_CONTEXT) { - loadedItems.push(member as IZoweDatasetTreeNode); + loadedItems.push(member); } } } @@ -971,8 +971,7 @@ export class DatasetTree extends ZoweTreeProvider implemen let setIcon: IconUtils.IIconItem; if (child.collapsibleState === vscode.TreeItemCollapsibleState.Collapsed) { setIcon = IconGenerator.getIconById(IconUtils.IconId.filterFolder); - } - if (child.collapsibleState === vscode.TreeItemCollapsibleState.Expanded) { + } else if (child.collapsibleState === vscode.TreeItemCollapsibleState.Expanded) { setIcon = IconGenerator.getIconById(IconUtils.IconId.filterFolderOpen); } if (setIcon) { @@ -980,7 +979,6 @@ export class DatasetTree extends ZoweTreeProvider implemen } } } - sessionNode.dirty = true; const icon = IconGenerator.getIconByNode(sessionNode); if (icon) { sessionNode.iconPath = icon.path; @@ -1264,7 +1262,7 @@ export class DatasetTree extends ZoweTreeProvider implemen } c.children.sort(ZoweDatasetNode.sortBy(node.sort)); - this.nodeDataChanged(c as IZoweDatasetTreeNode); + this.nodeDataChanged(c); } } } @@ -1377,7 +1375,7 @@ export class DatasetTree extends ZoweTreeProvider implemen if (node.children?.length > 0) { // children nodes already exist, sort and repaint to avoid extra refresh for (const c of node.children) { - const asDs = c as IZoweDatasetTreeNode; + const asDs = c; // PDS-level filters should have precedence over a session-level filter if (asDs.filter != null) { @@ -1388,15 +1386,15 @@ export class DatasetTree extends ZoweTreeProvider implemen // If there was an old session-wide filter set: refresh to get any // missing nodes - new filter will be applied if (oldFilter != null) { - this.refreshElement(c as IZoweDatasetTreeNode); + this.refreshElement(c); continue; } if (newFilter != null && c.children?.length > 0) { c.children = c.children.filter(ZoweDatasetNode.filterBy(newFilter)); - this.nodeDataChanged(c as IZoweDatasetTreeNode); + this.nodeDataChanged(c); } else { - this.refreshElement(c as IZoweDatasetTreeNode); + this.refreshElement(c); } } } diff --git a/packages/zowe-explorer/src/trees/shared/SharedInit.ts b/packages/zowe-explorer/src/trees/shared/SharedInit.ts index 2aeea0c756..85716d0cf3 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedInit.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedInit.ts @@ -46,6 +46,7 @@ import { SharedContext } from "./SharedContext"; import { TreeViewUtils } from "../../utils/TreeViewUtils"; import { CertificateWizard } from "../../utils/CertificateWizard"; import { ZosConsoleViewProvider } from "../../zosconsole/ZosConsolePanel"; +import * as path from "path"; export class SharedInit { private static originalEmitZoweEvent: typeof imperative.EventProcessor.prototype.emitEvent; @@ -331,11 +332,12 @@ export class SharedInit { }); watcher.onDidChange(async (uri: vscode.Uri) => { ZoweLogger.info(vscode.l10n.t("Team config file updated.")); - const newProfileContents = await vscode.workspace.fs.readFile(uri); - if (newProfileContents.toString() === Constants.SAVED_PROFILE_CONTENTS.toString()) { + const newProfileContents = Buffer.from(await vscode.workspace.fs.readFile(uri)); + const normalizedPath = path.normalize(uri.fsPath); + if (Constants.SAVED_PROFILE_CONTENTS.get(normalizedPath)?.equals(newProfileContents)) { return; } - Constants.SAVED_PROFILE_CONTENTS = newProfileContents; + Constants.SAVED_PROFILE_CONTENTS.set(normalizedPath, newProfileContents); void SharedActions.refreshAll(); ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter.fire(Validation.EventType.UPDATE); }); diff --git a/packages/zowe-explorer/src/trees/uss/USSTree.ts b/packages/zowe-explorer/src/trees/uss/USSTree.ts index 3f7945431e..f0098ac766 100644 --- a/packages/zowe-explorer/src/trees/uss/USSTree.ts +++ b/packages/zowe-explorer/src/trees/uss/USSTree.ts @@ -689,7 +689,7 @@ export class USSTree extends ZoweTreeProvider implements Types const children = nodeToCheck.children; if (children.length !== 0) { for (const child of children) { - await checkForChildren(child as IZoweUSSTreeNode); + await checkForChildren(child); } } loadedNodes.push(nodeToCheck); @@ -1011,13 +1011,13 @@ export class USSTree extends ZoweTreeProvider implements Types for (const favorite of favsForProfile) { // If profile and session already exists for favorite node, add to updatedFavsForProfile and go to next array item if (favorite.getProfile() && favorite.getSession()) { - updatedFavsForProfile.push(favorite as IZoweUSSTreeNode); + updatedFavsForProfile.push(favorite); continue; } // If no profile/session for favorite node yet, then add session and profile to favorite node: favorite.setProfileToChoice(profile); favorite.setSessionToChoice(session); - updatedFavsForProfile.push(favorite as IZoweUSSTreeNode); + updatedFavsForProfile.push(favorite); } // This updates the profile node's children in the this.mFavorites array, as well. return updatedFavsForProfile; @@ -1118,11 +1118,11 @@ export class USSTree extends ZoweTreeProvider implements Types } const isFullPathChild: boolean = SharedUtils.checkIfChildPath(node.fullPath, fullPath); if (isFullPathChild) { - return this.findMatchInLoadedChildren(node as IZoweUSSTreeNode, fullPath); + return this.findMatchInLoadedChildren(node, fullPath); } } } - return match as IZoweUSSTreeNode; + return match; } public async openWithEncoding(node: IZoweUSSTreeNode, encoding?: ZosEncoding): Promise { diff --git a/packages/zowe-explorer/src/utils/ProfilesUtils.ts b/packages/zowe-explorer/src/utils/ProfilesUtils.ts index 857cedd08a..7f26eb780a 100644 --- a/packages/zowe-explorer/src/utils/ProfilesUtils.ts +++ b/packages/zowe-explorer/src/utils/ProfilesUtils.ts @@ -332,7 +332,12 @@ export class ProfilesUtils { Gui.warningMessage(schemaWarning); ZoweLogger.warn(schemaWarning); } - Constants.CONFIG_PATH = rootPath ? rootPath : FileManagement.getZoweDir(); + Constants.SAVED_PROFILE_CONTENTS.clear(); + for (const layer of mProfileInfo.getTeamConfig().layers) { + if (layer.exists) { + Constants.SAVED_PROFILE_CONTENTS.set(layer.path, fs.readFileSync(layer.path)); + } + } ZoweLogger.info(`Zowe Explorer is using the team configuration file "${mProfileInfo.getTeamConfig().configName}"`); const layers = mProfileInfo.getTeamConfig().layers || []; const layerSummary = layers.map( From fc9cf6647845ddc5f627b1076cb163d6ba69e76b Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Mon, 7 Oct 2024 14:40:58 -0400 Subject: [PATCH 20/25] Store saved profile contents by URI path Signed-off-by: Timothy Johnson --- packages/zowe-explorer/src/trees/shared/SharedInit.ts | 6 ++---- packages/zowe-explorer/src/utils/ProfilesUtils.ts | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/zowe-explorer/src/trees/shared/SharedInit.ts b/packages/zowe-explorer/src/trees/shared/SharedInit.ts index 85716d0cf3..63c92d7437 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedInit.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedInit.ts @@ -46,7 +46,6 @@ import { SharedContext } from "./SharedContext"; import { TreeViewUtils } from "../../utils/TreeViewUtils"; import { CertificateWizard } from "../../utils/CertificateWizard"; import { ZosConsoleViewProvider } from "../../zosconsole/ZosConsolePanel"; -import * as path from "path"; export class SharedInit { private static originalEmitZoweEvent: typeof imperative.EventProcessor.prototype.emitEvent; @@ -333,11 +332,10 @@ export class SharedInit { watcher.onDidChange(async (uri: vscode.Uri) => { ZoweLogger.info(vscode.l10n.t("Team config file updated.")); const newProfileContents = Buffer.from(await vscode.workspace.fs.readFile(uri)); - const normalizedPath = path.normalize(uri.fsPath); - if (Constants.SAVED_PROFILE_CONTENTS.get(normalizedPath)?.equals(newProfileContents)) { + if (Constants.SAVED_PROFILE_CONTENTS.get(uri.fsPath)?.equals(newProfileContents)) { return; } - Constants.SAVED_PROFILE_CONTENTS.set(normalizedPath, newProfileContents); + Constants.SAVED_PROFILE_CONTENTS.set(uri.fsPath, newProfileContents); void SharedActions.refreshAll(); ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter.fire(Validation.EventType.UPDATE); }); diff --git a/packages/zowe-explorer/src/utils/ProfilesUtils.ts b/packages/zowe-explorer/src/utils/ProfilesUtils.ts index 2a67cda8ed..9e1a87b569 100644 --- a/packages/zowe-explorer/src/utils/ProfilesUtils.ts +++ b/packages/zowe-explorer/src/utils/ProfilesUtils.ts @@ -340,7 +340,7 @@ export class ProfilesUtils { Constants.SAVED_PROFILE_CONTENTS.clear(); for (const layer of mProfileInfo.getTeamConfig().layers) { if (layer.exists) { - Constants.SAVED_PROFILE_CONTENTS.set(layer.path, fs.readFileSync(layer.path)); + Constants.SAVED_PROFILE_CONTENTS.set(vscode.Uri.file(layer.path).fsPath, fs.readFileSync(layer.path)); } } ZoweLogger.info(`Zowe Explorer is using the team configuration file "${mProfileInfo.getTeamConfig().configName}"`); From cec1890189c2ffe864c3db503463b729d6b1faeb Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Mon, 7 Oct 2024 17:54:02 -0400 Subject: [PATCH 21/25] Fix update creds cancelling and node not refreshing Signed-off-by: Timothy Johnson --- .../src/configuration/Profiles.ts | 9 ++++--- .../src/trees/dataset/ZoweDatasetNode.ts | 5 ++-- .../src/trees/job/ZoweJobNode.ts | 10 +++---- .../src/trees/shared/SharedInit.ts | 4 +-- .../src/trees/uss/ZoweUSSNode.ts | 5 ++-- packages/zowe-explorer/src/utils/AuthUtils.ts | 27 ++++++++++++------- 6 files changed, 32 insertions(+), 28 deletions(-) diff --git a/packages/zowe-explorer/src/configuration/Profiles.ts b/packages/zowe-explorer/src/configuration/Profiles.ts index dd66d4ee25..de3fd1b590 100644 --- a/packages/zowe-explorer/src/configuration/Profiles.ts +++ b/packages/zowe-explorer/src/configuration/Profiles.ts @@ -760,7 +760,7 @@ export class Profiles extends ProfilesCache { return filteredProfile; } - public async ssoLogin(node?: Types.IZoweNodeType, label?: string): Promise { + public async ssoLogin(node?: Types.IZoweNodeType, label?: string): Promise { ZoweLogger.trace("Profiles.ssoLogin called."); let loginTokenType: string; let serviceProfile: imperative.IProfileLoaded; @@ -772,7 +772,7 @@ export class Profiles extends ProfilesCache { // This check will handle service profiles that have username and password if (AuthUtils.isProfileUsingBasicAuth(serviceProfile)) { Gui.showMessage(vscode.l10n.t(`This profile is using basic authentication and does not support token authentication.`)); - return; + return false; } const zeInstance = ZoweExplorerApiRegister.getInstance(); @@ -787,7 +787,7 @@ export class Profiles extends ProfilesCache { comment: [`Service profile name`], }) ); - return; + return false; } try { let loginOk = false; @@ -814,6 +814,7 @@ export class Profiles extends ProfilesCache { } else { Gui.showMessage(this.profilesOpCancelled); } + return loginOk; } catch (err) { const message = vscode.l10n.t({ message: `Unable to log in with {0}. {1}`, @@ -822,7 +823,7 @@ export class Profiles extends ProfilesCache { }); ZoweLogger.error(message); Gui.errorMessage(message); - return; + return false; } } diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts index 46c7e56042..11d5733639 100644 --- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts @@ -586,9 +586,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod responses.push(await ZoweExplorerApiRegister.getMvsApi(profile).allMembers(this.label as string, options)); } } catch (error) { - await AuthUtils.errorHandling(error, this.label.toString(), vscode.l10n.t("Retrieving response from MVS list API")); - AuthUtils.syncSessionNode((prof) => ZoweExplorerApiRegister.getMvsApi(prof), this.getSessionNode()); - this.dirty = false; + const updated = await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from MVS list API")); + AuthUtils.syncSessionNode((prof) => ZoweExplorerApiRegister.getMvsApi(prof), this.getSessionNode(), updated && this); } return responses; } diff --git a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts index c36a713b36..3e143e08a4 100644 --- a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts @@ -392,9 +392,8 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { }, []); } } catch (error) { - await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from JES list API")); - AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode()); - this.dirty = false; + const updated = await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from JES list API")); + AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode(), updated && this); } return jobsInternal; } @@ -409,9 +408,8 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { // see an issue #845 for the details spools = spools.filter((item) => !(item.id === undefined && item.ddname === undefined && item.stepname === undefined)); } catch (error) { - await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from JES list API")); - AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode()); - this.dirty = false; + const updated = await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from JES list API")); + AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode(), updated && this); } return spools; } diff --git a/packages/zowe-explorer/src/trees/shared/SharedInit.ts b/packages/zowe-explorer/src/trees/shared/SharedInit.ts index 63c92d7437..daefdba139 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedInit.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedInit.ts @@ -379,10 +379,10 @@ export class SharedInit { const theTreeView = theProvider.getTreeView(); context.subscriptions.push(theTreeView); context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(async (e) => SharedInit.setupRemoteWorkspaceFolders(e))); - theTreeView.onDidCollapseElement(async (e) => { + theTreeView.onDidCollapseElement((e) => { theProvider.flipState(e.element, false); }); - theTreeView.onDidExpandElement(async (e) => { + theTreeView.onDidExpandElement((e) => { theProvider.flipState(e.element, true); }); } diff --git a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts index 3e1921fdcd..78503884f9 100644 --- a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts +++ b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts @@ -680,9 +680,8 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { return await UssFSProvider.instance.listFiles(profile, this.resourceUri); } } catch (error) { - await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from USS list API")); - AuthUtils.syncSessionNode((prof) => ZoweExplorerApiRegister.getUssApi(prof), this.getSessionNode()); - this.dirty = false; + const updated = await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from USS list API")); + AuthUtils.syncSessionNode((prof) => ZoweExplorerApiRegister.getUssApi(prof), this.getSessionNode(), updated && this); return { success: false, commandResponse: null }; } } diff --git a/packages/zowe-explorer/src/utils/AuthUtils.ts b/packages/zowe-explorer/src/utils/AuthUtils.ts index cbfcabbeb3..163962ffee 100644 --- a/packages/zowe-explorer/src/utils/AuthUtils.ts +++ b/packages/zowe-explorer/src/utils/AuthUtils.ts @@ -14,6 +14,7 @@ import * as vscode from "vscode"; import { imperative, Gui, MainframeInteraction, IZoweTreeNode } from "@zowe/zowe-explorer-api"; import { Constants } from "../configuration/Constants"; import { ZoweLogger } from "../tools/ZoweLogger"; +import { SharedTreeProviders } from "../trees/shared/SharedTreeProviders"; export class AuthUtils { /************************************************************************************************************* @@ -22,7 +23,7 @@ export class AuthUtils { * @param {label} - additional information such as profile name, credentials, messageID etc * @param {moreInfo} - additional/customized error messages *************************************************************************************************************/ - public static async errorHandling(errorDetails: Error | string, label?: string, moreInfo?: string): Promise { + public static async errorHandling(errorDetails: Error | string, label?: string, moreInfo?: string): Promise { // Use util.inspect instead of JSON.stringify to handle circular references // eslint-disable-next-line @typescript-eslint/restrict-template-expressions ZoweLogger.error(`${errorDetails.toString()}\n` + util.inspect({ errorDetails, label, moreInfo }, { depth: null })); @@ -38,7 +39,7 @@ export class AuthUtils { if (prof.profName === label.trim()) { const filePath = prof.profLoc.osLoc[0]; await Constants.PROFILES_CACHE.openConfigFile(filePath); - return; + return false; } } } else if ( @@ -68,16 +69,16 @@ export class AuthUtils { if (tokenError.includes("Token is not valid or expired.") || isTokenAuth) { const message = vscode.l10n.t("Log in to Authentication Service"); - Gui.showMessage(errToken, { items: [message] }).then(async (selection) => { + const success = Gui.showMessage(errToken, { items: [message] }).then(async (selection) => { if (selection) { - await Constants.PROFILES_CACHE.ssoLogin(null, label); + return Constants.PROFILES_CACHE.ssoLogin(null, label); } }); - return; + return success; } } const checkCredsButton = vscode.l10n.t("Update Credentials"); - await Gui.errorMessage(errMsg, { + const creds = await Gui.errorMessage(errMsg, { items: [checkCredsButton], vsCodeOpts: { modal: true }, }).then(async (selection) => { @@ -85,13 +86,13 @@ export class AuthUtils { Gui.showMessage(vscode.l10n.t("Operation Cancelled")); return; } - await Constants.PROFILES_CACHE.promptCredentials(label.trim(), true); + return Constants.PROFILES_CACHE.promptCredentials(label.trim(), true); }); - return; + return creds != null ? true : false; } } if (errorDetails.toString().includes("Could not find profile")) { - return; + return false; } if (moreInfo === undefined) { moreInfo = errorDetails.toString().includes("Error") ? "" : "Error: "; @@ -100,6 +101,7 @@ export class AuthUtils { } // Try to keep message readable since VS Code doesn't support newlines in error messages Gui.errorMessage(moreInfo + errorDetails.toString().replace(/\n/g, " | ")); + return false; } /** @@ -110,7 +112,8 @@ export class AuthUtils { */ public static syncSessionNode( getCommonApi: (profile: imperative.IProfileLoaded) => MainframeInteraction.ICommon, - sessionNode: IZoweTreeNode + sessionNode: IZoweTreeNode, + nodeToRefresh?: IZoweTreeNode ): void { ZoweLogger.trace("ProfilesUtils.syncSessionNode called."); @@ -127,6 +130,10 @@ export class AuthUtils { sessionNode.setProfileToChoice(profile); const session = getCommonApi(profile).getSession(); sessionNode.setSessionToChoice(session); + if (nodeToRefresh != null) { + nodeToRefresh.dirty = true; + void nodeToRefresh.getChildren().then(() => SharedTreeProviders.getProviderForNode(nodeToRefresh).refreshElement(nodeToRefresh)); + } } /** From f63cb9f41c80af22ae031429b13dc1b72cb961c5 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Mon, 7 Oct 2024 18:18:07 -0400 Subject: [PATCH 22/25] Fix failing USS actions test Signed-off-by: Timothy Johnson --- .../__tests__/__unit__/trees/uss/USSActions.unit.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/uss/USSActions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/uss/USSActions.unit.test.ts index 6878e4fbe1..3cd04ee5c5 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/uss/USSActions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/uss/USSActions.unit.test.ts @@ -250,6 +250,7 @@ describe("USS Action Unit Tests - Function createUSSNode", () => { name: globalMocks.testProfile.name, status: "unverified", }), + loadNamedProfile: globalMocks.mockLoadNamedProfile, validProfile: Validation.ValidationType.UNVERIFIED, }; }), From e93c71d344b815b6cef8f02b007c610089ea0551 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Mon, 7 Oct 2024 20:39:41 -0400 Subject: [PATCH 23/25] Handle scenario of falsy node to refresh Signed-off-by: Timothy Johnson --- packages/zowe-explorer/src/utils/AuthUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zowe-explorer/src/utils/AuthUtils.ts b/packages/zowe-explorer/src/utils/AuthUtils.ts index 163962ffee..0f6fa561c4 100644 --- a/packages/zowe-explorer/src/utils/AuthUtils.ts +++ b/packages/zowe-explorer/src/utils/AuthUtils.ts @@ -130,7 +130,7 @@ export class AuthUtils { sessionNode.setProfileToChoice(profile); const session = getCommonApi(profile).getSession(); sessionNode.setSessionToChoice(session); - if (nodeToRefresh != null) { + if (nodeToRefresh) { nodeToRefresh.dirty = true; void nodeToRefresh.getChildren().then(() => SharedTreeProviders.getProviderForNode(nodeToRefresh).refreshElement(nodeToRefresh)); } From ad07311a92ce82a42ee4fa8ba43a00cf7aac6449 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 8 Oct 2024 07:03:03 -0400 Subject: [PATCH 24/25] Add unit test for syncSessionNode Signed-off-by: Timothy Johnson --- .../trees/shared/SharedUtils.unit.test.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedUtils.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedUtils.unit.test.ts index 3fda35525a..645f8de4d8 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedUtils.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedUtils.unit.test.ts @@ -24,6 +24,7 @@ import { ZoweJobNode } from "../../../../src/trees/job/ZoweJobNode"; import { SharedUtils } from "../../../../src/trees/shared/SharedUtils"; import { ZoweUSSNode } from "../../../../src/trees/uss/ZoweUSSNode"; import { AuthUtils } from "../../../../src/utils/AuthUtils"; +import { SharedTreeProviders } from "../../../../src/trees/shared/SharedTreeProviders"; function createGlobalMocks() { const newMocks = { @@ -110,6 +111,30 @@ describe("syncSessionNode shared util function", () => { expect(sessionNode.getSession()).toEqual(expectedSession); expect(sessionNode.getProfile()).toEqual(createIProfile()); }); + it("should update session node and refresh tree node if provided", async () => { + const globalMocks = createGlobalMocks(); + // given + Object.defineProperty(globalMocks.mockProfilesCache, "loadNamedProfile", { + value: jest.fn().mockReturnValue(createIProfile()), + }); + const getChildrenSpy = jest.spyOn(sessionNode, "getChildren").mockResolvedValueOnce([]); + const refreshElementMock = jest.fn(); + jest.spyOn(SharedTreeProviders, "getProviderForNode").mockReturnValueOnce({ + refreshElement: refreshElementMock, + } as any); + const getSessionMock = jest.fn().mockReturnValue(new imperative.Session({})); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + const sessionForProfile = (_profile) => + ({ + getSession: getSessionMock, + } as any); + // when + AuthUtils.syncSessionNode(sessionForProfile, sessionNode, sessionNode); + expect(getSessionMock).toHaveBeenCalled(); + expect(sessionNode.dirty).toBe(true); + expect(await getChildrenSpy).toHaveBeenCalled(); + expect(refreshElementMock).toHaveBeenCalledWith(sessionNode); + }); it("should do nothing, if there is no profile from provided node in the file system", () => { const profiles = createInstanceOfProfile(serviceProfile); profiles.loadNamedProfile = jest.fn(() => From 68093270f5fcf9c814a5ee86a291c06b98c4b7b3 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Tue, 8 Oct 2024 10:47:01 -0400 Subject: [PATCH 25/25] Don't show placeholder node when there is error Signed-off-by: Timothy Johnson --- .../dataset/ZoweDatasetNode.unit.test.ts | 4 ++-- .../trees/job/ZoweJobNode.unit.test.ts | 22 +++++-------------- .../src/trees/dataset/ZoweDatasetNode.ts | 6 ++++- .../src/trees/job/ZoweJobNode.ts | 14 ++++++++---- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts index 70482e2a88..db0ca1f12b 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/ZoweDatasetNode.unit.test.ts @@ -125,7 +125,7 @@ describe("ZoweDatasetNode Unit Tests", () => { /************************************************************************************************************* * Checks that returning an unsuccessful response results in an error being thrown and caught *************************************************************************************************************/ - it("Checks that when List.dataSet/allMembers() throws an error, it returns a label of 'No data sets found'", async () => { + it("Checks that when List.dataSet/allMembers() throws an error, it returns an empty list", async () => { Object.defineProperty(Profiles, "getInstance", { value: jest.fn(() => { return { @@ -151,7 +151,7 @@ describe("ZoweDatasetNode Unit Tests", () => { jest.spyOn(zosfiles.List, "allMembers").mockRejectedValueOnce(new Error(subNode.label as string)); subNode.dirty = true; const response = await subNode.getChildren(); - expect(response[0].label).toBe("No data sets found"); + expect(response).toEqual([]); }); it("Checks that when List.dataSet/allMembers() returns an empty response, it returns a label of 'No data sets found'", async () => { diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts index d1025b5ccf..623a0691f9 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/job/ZoweJobNode.unit.test.ts @@ -30,7 +30,7 @@ import { Profiles } from "../../../../src/configuration/Profiles"; import { ZoweExplorerApiRegister } from "../../../../src/extending/ZoweExplorerApiRegister"; import { ZoweLocalStorage } from "../../../../src/tools/ZoweLocalStorage"; import { JobFSProvider } from "../../../../src/trees/job/JobFSProvider"; -import { ZoweJobNode, ZoweSpoolNode } from "../../../../src/trees/job/ZoweJobNode"; +import { ZoweJobNode } from "../../../../src/trees/job/ZoweJobNode"; import { SharedContext } from "../../../../src/trees/shared/SharedContext"; import { SharedTreeProviders } from "../../../../src/trees/shared/SharedTreeProviders"; import { JobInit } from "../../../../src/trees/job/JobInit"; @@ -387,20 +387,14 @@ describe("ZoweJobNode unit tests - Function getChildren", () => { expect(spoolFilesAfter[0].label).toEqual("No spool files found"); }); - it("Tests that getChildren returns a placeholder node if there is error retrieving spool files", async () => { + it("Tests that getChildren returns empty list if there is error retrieving spool files", async () => { const globalMocks = await createGlobalMocks(); - const spool = new ZoweSpoolNode({ - label: "No spool files found", - collapsibleState: vscode.TreeItemCollapsibleState.None, - parentNode: globalMocks.testJobNode, - contextOverride: Constants.INFORMATION_CONTEXT, - }); jest.spyOn(ZoweExplorerApiRegister, "getJesApi").mockReturnValueOnce({ getSpoolFiles: jest.fn().mockResolvedValue(new Error("Response Fail")), } as any); globalMocks.testJobNode.dirty = true; const spools = await globalMocks.testJobNode.getChildren(); - expect(spools[0]).toEqual(spool); + expect(spools).toEqual([]); }); it("Tests that getChildren returns the spool files if user/owner is not defined", async () => { @@ -445,14 +439,8 @@ describe("ZoweJobNode unit tests - Function getChildren", () => { expect(jobs[0]).toEqual(job); }); - it("should return 'No jobs found' if there is error retrieving jobs", async () => { + it("should return empty list if there is error retrieving jobs", async () => { const globalMocks = await createGlobalMocks(); - const job = new ZoweJobNode({ - label: "No jobs found", - collapsibleState: vscode.TreeItemCollapsibleState.None, - parentNode: globalMocks.testJobsProvider.mSessionNodes[1], - contextOverride: Constants.INFORMATION_CONTEXT, - }); await globalMocks.testJobsProvider.addSession("fake"); globalMocks.testJobsProvider.mSessionNodes[1].filtered = true; globalMocks.mockGetJobsByParameters.mockRejectedValue(new Error("Response Fail")); @@ -460,7 +448,7 @@ describe("ZoweJobNode unit tests - Function getChildren", () => { getSession: jest.fn().mockReturnValue(globalMocks.testSession), } as any); const jobs = await globalMocks.testJobsProvider.mSessionNodes[1].getChildren(); - expect(jobs[0]).toEqual(job); + expect(jobs).toEqual([]); }); it("To check smfid field in Jobs Tree View", async () => { diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts index 11d5733639..527d272260 100644 --- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts @@ -229,6 +229,9 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod // Gets the datasets from the pattern or members of the dataset and displays any thrown errors const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); const responses = await this.getDatasets(cachedProfile); + if (responses == null) { + return this.children; + } // push nodes to an object with property names to avoid duplicates const elementChildren: { [k: string]: ZoweDatasetNode } = {}; @@ -529,7 +532,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod return fileEntry?.etag; } - private async getDatasets(profile: imperative.IProfileLoaded): Promise { + private async getDatasets(profile: imperative.IProfileLoaded): Promise { ZoweLogger.trace("ZoweDatasetNode.getDatasets called."); const responses: zosfiles.IZosFilesResponse[] = []; const options: zosfiles.IListOptions = { @@ -588,6 +591,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } catch (error) { const updated = await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from MVS list API")); AuthUtils.syncSessionNode((prof) => ZoweExplorerApiRegister.getMvsApi(prof), this.getSessionNode(), updated && this); + return; } return responses; } diff --git a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts index 3e143e08a4..0daea1bab7 100644 --- a/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/trees/job/ZoweJobNode.ts @@ -138,7 +138,9 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { if (SharedContext.isJob(this)) { // Fetch spool files under job node const spools = await this.getSpoolFiles(this.job); - if (!spools.length) { + if (spools == null) { + return this.children; + } else if (!spools.length) { const noSpoolNode = new ZoweSpoolNode({ label: vscode.l10n.t("No spool files found"), collapsibleState: vscode.TreeItemCollapsibleState.None, @@ -189,7 +191,9 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { } else { // Fetch jobs under session node const jobs = await this.getJobs(this._owner, this._prefix, this._searchId, this._jobStatus); - if (jobs.length === 0) { + if (jobs == null) { + return this.children; + } else if (jobs.length === 0) { const noJobsNode = new ZoweJobNode({ label: vscode.l10n.t("No jobs found"), collapsibleState: vscode.TreeItemCollapsibleState.None, @@ -353,7 +357,7 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { return this._searchId; } - private async getJobs(owner: string, prefix: string, searchId: string, status: string): Promise { + private async getJobs(owner: string, prefix: string, searchId: string, status: string): Promise { ZoweLogger.trace("ZoweJobNode.getJobs called."); const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); let jobsInternal: zosjobs.IJob[] = []; @@ -394,11 +398,12 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { } catch (error) { const updated = await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from JES list API")); AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode(), updated && this); + return; } return jobsInternal; } - private async getSpoolFiles(job: zosjobs.IJob = this.job): Promise { + private async getSpoolFiles(job: zosjobs.IJob = this.job): Promise { ZoweLogger.trace("ZoweJobNode.getSpoolFiles called."); const cachedProfile = Profiles.getInstance().loadNamedProfile(this.getProfileName()); let spools: zosjobs.IJobFile[] = []; @@ -410,6 +415,7 @@ export class ZoweJobNode extends ZoweTreeNode implements IZoweJobTreeNode { } catch (error) { const updated = await AuthUtils.errorHandling(error, this.getProfileName(), vscode.l10n.t("Retrieving response from JES list API")); AuthUtils.syncSessionNode((profile) => ZoweExplorerApiRegister.getJesApi(profile), this.getSessionNode(), updated && this); + return; } return spools; }