Skip to content

Commit

Permalink
Merge pull request #3018 from zowe/fix/ext-refresh-errors-v2
Browse files Browse the repository at this point in the history
fix: Fix sessions disappearing from trees caused by `zowe.extRefresh`
  • Loading branch information
t1m0thyj authored Jul 26, 2024
2 parents ecd6153 + 6ac2caf commit 61394b8
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 64 deletions.
2 changes: 2 additions & 0 deletions packages/zowe-explorer-api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to the "zowe-explorer-api" extension will be documented in t

### Bug fixes

- Fixed an issue where the `ZoweVsCodeExtension.updateCredentials` method could remove credentials from session when input prompt was cancelled. [#3018](https://github.com/zowe/zowe-explorer-vscode/pull/3018)

## `2.17.0`

### New features and enhancements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,10 +555,11 @@ describe("ZoweVsCodeExtension", () => {
});

it("should do nothing if user input is cancelled", async () => {
const fakeProfile = { user: "fakeUser", password: "fakePass" };
const mockUpdateProperty = jest.fn();
jest.spyOn(ZoweVsCodeExtension as any, "profilesCache", "get").mockReturnValue({
getLoadedProfConfig: jest.fn().mockReturnValue({
profile: {},
profile: fakeProfile,
}),
getProfileInfo: jest.fn().mockReturnValue({
isSecured: jest.fn().mockReturnValue(true),
Expand All @@ -568,19 +569,22 @@ describe("ZoweVsCodeExtension", () => {
});
const showInputBoxSpy = jest.spyOn(Gui, "showInputBox").mockResolvedValueOnce(undefined);
const profileLoaded = await ZoweVsCodeExtension.updateCredentials(
promptCredsOptions,
{ ...promptCredsOptions, rePrompt: true },
undefined as unknown as ZoweExplorerApi.IApiRegisterClient
);
expect(profileLoaded).toBeUndefined();
expect(showInputBoxSpy).toHaveBeenCalledTimes(1);
expect(mockUpdateProperty).toHaveBeenCalledTimes(0);
expect(fakeProfile.user).toBeDefined();
expect(fakeProfile.password).toBeDefined();
});

it("should do nothing if password input is cancelled", async () => {
const fakeProfile = { user: "fakeUser", password: "fakePass" };
const mockUpdateProperty = jest.fn();
jest.spyOn(ZoweVsCodeExtension as any, "profilesCache", "get").mockReturnValue({
getLoadedProfConfig: jest.fn().mockReturnValue({
profile: {},
profile: fakeProfile,
}),
getProfileInfo: jest.fn().mockReturnValue({
isSecured: jest.fn().mockReturnValue(true),
Expand All @@ -590,12 +594,14 @@ describe("ZoweVsCodeExtension", () => {
});
const showInputBoxSpy = jest.spyOn(Gui, "showInputBox").mockResolvedValueOnce("fakeUser").mockResolvedValueOnce(undefined);
const profileLoaded = await ZoweVsCodeExtension.updateCredentials(
promptCredsOptions,
{ ...promptCredsOptions, rePrompt: true },
undefined as unknown as ZoweExplorerApi.IApiRegisterClient
);
expect(profileLoaded).toBeUndefined();
expect(showInputBoxSpy).toHaveBeenCalledTimes(2);
expect(mockUpdateProperty).toHaveBeenCalledTimes(0);
expect(fakeProfile.user).toBeDefined();
expect(fakeProfile.password).toBeDefined();
});

it("should do nothing if profile and sessionName args are not provided", async () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/zowe-explorer-api/src/vscode/ZoweVsCodeExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,6 @@ export class ZoweVsCodeExtension {
value: newUser,
...(options.userInputBoxOptions ?? {}),
});
options.session.user = newUser;
}
if (!newUser || (options.rePrompt && newUser === "")) {
return undefined;
Expand All @@ -355,13 +354,14 @@ export class ZoweVsCodeExtension {
value: newPass,
...(options.passwordInputBoxOptions ?? {}),
});
options.session.password = newPass;
}
if (!newPass || (options.rePrompt && newPass === "")) {
return undefined;
}

return [newUser.trim(), newPass.trim()];
options.session.user = newUser.trim();
options.session.password = newPass.trim();
return [options.session.user, options.session.password];
}

private static async promptCertificate(options: IPromptCertificateOptions): Promise<void> {
Expand Down
2 changes: 2 additions & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen

### Bug fixes

- Fixed issue where creating a new team configuration file could cause Zowe Explorer to crash, resulting in all sessions disappearing from trees. [#2906](https://github.com/zowe/zowe-explorer-vscode/issues/2906)

## `2.17.0`

### New features and enhancements
Expand Down
72 changes: 42 additions & 30 deletions packages/zowe-explorer/__tests__/__unit__/shared/init.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,7 @@ describe("Test src/shared/extension", () => {
{ spy: jest.spyOn(test.value, "affectsConfiguration"), arg: [globals.SETTINGS_TEMP_FOLDER_PATH], ret: false },
{ spy: jest.spyOn(test.value, "affectsConfiguration"), arg: [globals.SETTINGS_AUTOMATIC_PROFILE_VALIDATION], ret: true },
{ spy: jest.spyOn(Profiles, "getInstance"), arg: [], ret: { refresh: jest.fn() } },
{ spy: jest.spyOn(refreshActions, "refreshAll"), arg: ["ds"] },
{ spy: jest.spyOn(refreshActions, "refreshAll"), arg: ["uss"] },
{ spy: jest.spyOn(refreshActions, "refreshAll"), arg: ["job"] },
{ spy: jest.spyOn(refreshActions, "refreshAll"), arg: [] },
{ spy: jest.spyOn(test.value, "affectsConfiguration"), arg: [globals.SETTINGS_TEMP_FOLDER_HIDE], ret: false },
{ spy: jest.spyOn(test.value, "affectsConfiguration"), arg: [globals.SETTINGS_SECURE_CREDENTIALS_ENABLED], ret: false },
],
Expand Down Expand Up @@ -312,55 +310,69 @@ describe("Test src/shared/extension", () => {

describe("watchConfigProfile", () => {
let context: any;
let watcherPromise: any;
const spyReadFile = jest.fn().mockReturnValue("test");
const spyExecuteCommand = jest.fn();
const mockEmitter = jest.fn();
const watcher: any = {
onDidCreate: jest.fn().mockImplementation((fun) => fun()),
onDidDelete: jest.fn().mockImplementation((fun) => fun()),
onDidChange: jest.fn().mockImplementation((fun) => fun("uri")),
onDidCreate: jest.fn(),
onDidDelete: jest.fn(),
onDidChange: jest.fn(),
};
beforeEach(() => {
context = { subscriptions: [] };
jest.clearAllMocks();
Object.defineProperty(globals, "ISTHEIA", { value: false, configurable: true });
Object.defineProperty(vscode.workspace, "createFileSystemWatcher", { value: () => watcher, configurable: true });
Object.defineProperty(vscode.workspace, "workspaceFolders", { value: [{ uri: { fsPath: "fsPath" } }], configurable: true });
Object.defineProperty(vscode.commands, "executeCommand", { value: spyExecuteCommand, configurable: true });
Object.defineProperty(vscode.workspace, "fs", { value: { readFile: spyReadFile }, configurable: true });
Object.defineProperty(globals, "SAVED_PROFILE_CONTENTS", { value: "test", configurable: true });
jest.spyOn(vscode.workspace, "createFileSystemWatcher").mockReturnValue(watcher);
jest.spyOn(ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter, "fire").mockImplementation(mockEmitter);
});

afterAll(() => {
jest.restoreAllMocks();
});

it("should be able to trigger all listeners", async () => {
const spyRefreshAll = jest.spyOn(refreshActions, "refreshAll").mockImplementation(jest.fn());
jest.spyOn(ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter, "fire").mockImplementation();
await sharedExtension.watchConfigProfile(context, { ds: "ds", uss: "uss", job: "job" } as any);
expect(spyExecuteCommand).toHaveBeenCalledWith("zowe.extRefresh");
it("should be able to trigger onDidCreate listener", async () => {
const spyRefreshAll = jest.spyOn(refreshActions, "refreshAll").mockImplementation();
watcher.onDidCreate.mockImplementationOnce((fun) => (watcherPromise = fun()));
sharedExtension.watchConfigProfile(context);
await watcherPromise;
expect(context.subscriptions).toContain(watcher);
expect(spyReadFile).toHaveBeenCalledWith("uri");
expect(spyRefreshAll).not.toHaveBeenCalled();
expect(spyRefreshAll).toHaveBeenCalledTimes(1);
expect(mockEmitter).toHaveBeenCalledTimes(1);
});

spyReadFile.mockReturnValue("other");
await sharedExtension.watchConfigProfile(context, { ds: "ds", uss: "uss", job: "job" } as any);
expect(spyRefreshAll).toHaveBeenCalled();
it("should be able to trigger onDidDelete listener", async () => {
const spyRefreshAll = jest.spyOn(refreshActions, "refreshAll").mockImplementation();
watcher.onDidDelete.mockImplementationOnce((fun) => (watcherPromise = fun()));
sharedExtension.watchConfigProfile(context);
await watcherPromise;
expect(context.subscriptions).toContain(watcher);
expect(spyRefreshAll).toHaveBeenCalledTimes(1);
expect(mockEmitter).toHaveBeenCalledTimes(1);
});

it("should be able to refresh zowe explorer on theia after updating config file", async () => {
Object.defineProperty(globals, "ISTHEIA", { value: true, configurable: true });
jest.spyOn(ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter, "fire").mockImplementation();
const spyRefreshAll = jest.spyOn(refreshActions, "refreshAll").mockImplementation(jest.fn());
await sharedExtension.watchConfigProfile(context, { ds: "ds", uss: "uss", job: "job" } as any);
it("should be able to trigger onDidChange listener", async () => {
const spyRefreshAll = jest.spyOn(refreshActions, "refreshAll").mockImplementation();
watcher.onDidChange.mockImplementationOnce((fun) => (watcherPromise = fun("uri")));
sharedExtension.watchConfigProfile(context);
await watcherPromise;
expect(context.subscriptions).toContain(watcher);
expect(spyReadFile).toHaveBeenCalledWith("uri");
expect(spyRefreshAll).toHaveBeenCalled();
expect(spyRefreshAll).not.toHaveBeenCalled();
expect(mockEmitter).not.toHaveBeenCalled();
});

spyReadFile.mockReturnValue("other");
await sharedExtension.watchConfigProfile(context, { ds: "ds", uss: "uss", job: "job" } as any);
expect(spyRefreshAll).toHaveBeenCalled();
expect(spyExecuteCommand).toHaveBeenCalledWith("zowe.extRefresh");
it("should be able to trigger onDidChange listener with changes", async () => {
const spyRefreshAll = jest.spyOn(refreshActions, "refreshAll").mockImplementation();
spyReadFile.mockReturnValueOnce("other");
watcher.onDidChange.mockImplementationOnce((fun) => (watcherPromise = fun("uri")));
sharedExtension.watchConfigProfile(context);
await watcherPromise;
expect(context.subscriptions).toContain(watcher);
expect(spyReadFile).toHaveBeenCalledWith("uri");
expect(spyRefreshAll).toHaveBeenCalledTimes(1);
expect(mockEmitter).toHaveBeenCalledTimes(1);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
createInstanceOfProfile,
createIProfile,
createISessionWithoutCredentials,
createTreeProviders,
createTreeView,
} from "../../../__mocks__/mockCreators/shared";
import { createFavoriteUSSNode, createUSSNode, createUSSTree } from "../../../__mocks__/mockCreators/uss";
Expand All @@ -27,6 +28,8 @@ import * as globals from "../../../src/globals";
import * as sessUtils from "../../../src/utils/SessionUtils";
import { SettingsConfig } from "../../../src/utils/SettingsConfig";
import { ZoweLogger } from "../../../src/utils/LoggerUtils";
import { TreeProviders } from "../../../src/shared/TreeProviders";
import { TreeViewUtils } from "../../../src/utils/TreeViewUtils";

function createGlobalMocks() {
const globalMocks = {
Expand Down Expand Up @@ -157,4 +160,21 @@ describe("Refresh Unit Tests - Function refreshAll", () => {
await expect(refreshActions.refreshAll(blockMocks.testDatasetTree)).resolves.not.toThrow();
spy.mockClear();
});
it("should refresh all tree providers and update session nodes", async () => {
await createGlobalMocks();
jest.spyOn(TreeProviders, "providers", "get").mockReturnValue(createTreeProviders());
const removedProfNames = new Set<string>();
const addedProfTypes = new Set<string>();
const removeSessionSpy = jest
.spyOn(sessUtils, "removeSession")
.mockImplementation(async (treeProvider, profileName) => removedProfNames.add(profileName) as any);
const addDefaultSessionSpy = jest
.spyOn(TreeViewUtils, "addDefaultSession")
.mockImplementation(async (treeProvider, profileType) => addedProfTypes.add(profileType) as any);
await refreshActions.refreshAll();
expect(removeSessionSpy).toHaveBeenCalledTimes(6);
expect([...removedProfNames]).toEqual(["zosmf2", "zosmf"]);
expect(addDefaultSessionSpy).toHaveBeenCalledTimes(3);
expect([...addedProfTypes]).toEqual(["zosmf"]);
});
});
9 changes: 2 additions & 7 deletions packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { ZoweLogger } from "../utils/LoggerUtils";
import { TreeProviders } from "../shared/TreeProviders";
import { IZoweProviders } from "../shared/init";
import { resetValidationSettings } from "../shared/actions";
import { TreeViewUtils } from "../utils/TreeViewUtils";

// Set up localization
nls.config({
Expand Down Expand Up @@ -376,12 +377,6 @@ export class ZoweTreeProvider {
}
}
}
if (treeProvider.mSessionNodes.length === 1) {
try {
await treeProvider.addSingleSession(Profiles.getInstance().getDefaultProfile(profileType));
} catch (error) {
ZoweLogger.warn(error);
}
}
await TreeViewUtils.addDefaultSession(treeProvider, profileType);
}
}
2 changes: 1 addition & 1 deletion packages/zowe-explorer/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<ZoweEx
registerCommonCommands(context, providers);
ZoweExplorerExtender.createInstance(providers.ds, providers.uss, providers.job);
await SettingsConfig.standardizeSettings();
await watchConfigProfile(context, providers);
watchConfigProfile(context);
globals.setActivated(true);
findRecoveredFiles();
return ZoweExplorerApiRegister.getInstance();
Expand Down
23 changes: 6 additions & 17 deletions packages/zowe-explorer/src/shared/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function registerCommonCommands(context: vscode.ExtensionContext, provide
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(async (e) => {
// If the log folder location has been changed, update current log folder preference
if (e.affectsConfiguration(globals.SETTINGS_LOGS_FOLDER_PATH)) {
if (e.affectsConfiguration(globals.SETTINGS_LOGS_FOLDER_PATH) || e.affectsConfiguration(globals.LOGGER_SETTINGS)) {
await ZoweLogger.initializeZoweLogger(context);
}
// If the temp folder location has been changed, update current temp folder preference
Expand All @@ -125,20 +125,14 @@ export function registerCommonCommands(context: vscode.ExtensionContext, provide
}
if (e.affectsConfiguration(globals.SETTINGS_AUTOMATIC_PROFILE_VALIDATION)) {
await Profiles.getInstance().refresh(ZoweExplorerApiRegister.getInstance());
await refreshActions.refreshAll(providers.ds);
await refreshActions.refreshAll(providers.uss);
await refreshActions.refreshAll(providers.job);
await refreshActions.refreshAll();
}
if (e.affectsConfiguration(globals.SETTINGS_TEMP_FOLDER_HIDE)) {
await hideTempFolder(getZoweDir());
}

if (e.affectsConfiguration(globals.SETTINGS_SECURE_CREDENTIALS_ENABLED)) {
await vscode.commands.executeCommand("zowe.updateSecureCredentials");
}
if (e.affectsConfiguration(globals.LOGGER_SETTINGS)) {
await vscode.commands.executeCommand("zowe.extRefresh");
}
})
);

Expand Down Expand Up @@ -232,7 +226,7 @@ export function registerCredentialManager(context: vscode.ExtensionContext): voi
);
}

export function watchConfigProfile(context: vscode.ExtensionContext, providers: IZoweProviders): void {
export function watchConfigProfile(context: vscode.ExtensionContext): void {
ZoweLogger.trace("shared.init.watchConfigProfile called.");
const watchers: vscode.FileSystemWatcher[] = [];
watchers.push(vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(getZoweDir(), "{zowe.config,zowe.config.user}.json")));
Expand All @@ -250,12 +244,12 @@ export function watchConfigProfile(context: vscode.ExtensionContext, providers:
watchers.forEach((watcher) => {
watcher.onDidCreate(async () => {
ZoweLogger.info(localize("watchConfigProfile.create", "Team config file created, refreshing Zowe Explorer."));
await vscode.commands.executeCommand("zowe.extRefresh");
await refreshActions.refreshAll();
ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter.fire(EventTypes.CREATE);
});
watcher.onDidDelete(async () => {
ZoweLogger.info(localize("watchConfigProfile.delete", "Team config file deleted, refreshing Zowe Explorer."));
await vscode.commands.executeCommand("zowe.extRefresh");
await refreshActions.refreshAll();
ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter.fire(EventTypes.DELETE);
});
watcher.onDidChange(async (uri: vscode.Uri) => {
Expand All @@ -265,13 +259,8 @@ export function watchConfigProfile(context: vscode.ExtensionContext, providers:
return;
}
globals.setSavedProfileContents(newProfileContents);
await refreshActions.refreshAll(providers.ds);
await refreshActions.refreshAll(providers.uss);
await refreshActions.refreshAll(providers.job);
await refreshActions.refreshAll();
ZoweExplorerApiRegister.getInstance().onProfilesUpdateEmitter.fire(EventTypes.UPDATE);
if (globals.ISTHEIA) {
await vscode.commands.executeCommand("zowe.extRefresh");
}
});
});
}
Expand Down
Loading

0 comments on commit 61394b8

Please sign in to comment.