Skip to content

Commit

Permalink
Merge branch 'main' into fix/node-after-ds-recall
Browse files Browse the repository at this point in the history
Signed-off-by: Trae Yelovich <[email protected]>
  • Loading branch information
traeok committed Nov 21, 2024
2 parents cbfdbb7 + 66f4592 commit 2797547
Show file tree
Hide file tree
Showing 33 changed files with 422 additions and 61 deletions.
8 changes: 8 additions & 0 deletions packages/zowe-explorer-api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ All notable changes to the "zowe-explorer-api" extension will be documented in t

### New features and enhancements

- Added optional `getLocalStorage` function to the `IApiExplorerExtender` interface to expose local storage access to Zowe Explorer extenders. [#3180](https://github.com/zowe/zowe-explorer-vscode/issues/3180)

### Bug fixes

## `3.0.3`

### New features and enhancements

- Update Zowe SDKs to `8.8.2` to get the latest enhancements from Imperative. [#3296](https://github.com/zowe/zowe-explorer-vscode/pull/3296)

### Bug fixes
Expand Down
8 changes: 8 additions & 0 deletions packages/zowe-explorer-api/src/extend/IApiExplorerExtender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import * as imperative from "@zowe/imperative";
import { ProfilesCache } from "../profiles/ProfilesCache";
import { ErrorCorrelator } from "../utils/ErrorCorrelator";
import { ILocalStorageAccess } from "./ILocalStorageAccess";

/**
* This interface can be used by other VS Code Extensions to access an alternative
Expand Down Expand Up @@ -53,4 +54,11 @@ export interface IApiExplorerExtender {
* provide tips or additional resources for errors.
*/
getErrorCorrelator?(): ErrorCorrelator;

/**
* Allows extenders to access Zowe Explorer's local storage values. Retrieve a list of
* readable and writable keys by calling the `getReadableKeys, getWritableKeys` functions
* on the returned instance.
*/
getLocalStorage?(): ILocalStorageAccess;
}
38 changes: 38 additions & 0 deletions packages/zowe-explorer-api/src/extend/ILocalStorageAccess.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*
*/

export interface ILocalStorageAccess {
/**
* @returns The list of readable keys from the access facility
*/
getReadableKeys(): string[];

/**
* @returns The list of writable keys from the access facility
*/
getWritableKeys(): string[];

/**
* Retrieve the value from local storage for the given key.
* @param key A readable key
* @returns The value if it exists in local storage, or `undefined` otherwise
* @throws If the extender does not have appropriate read permissions for the given key
*/
getValue<T>(key: string): T;

/**
* Set a value in local storage for the given key.
* @param key A writable key
* @param value The new value for the given key to set in local storage
* @throws If the extender does not have appropriate write permissions for the given key
*/
setValue<T>(key: string, value: T): Thenable<void>;
}
1 change: 1 addition & 0 deletions packages/zowe-explorer-api/src/extend/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
*/

export * from "./IApiExplorerExtender";
export * from "./ILocalStorageAccess";
export * from "./IRegisterClient";
export * from "./MainframeInteraction";
8 changes: 5 additions & 3 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
- Use the "Troubleshoot" option for certain errors to obtain additional context, tips, and resources for how to resolve the errors. [#3243](https://github.com/zowe/zowe-explorer-vscode/pull/3243)
- Allow extenders to add context menu actions to a top level node, i.e. data sets, USS, Jobs, by encoding the profile type in the context value. [#3309](https://github.com/zowe/zowe-explorer-vscode/pull/3309)
- You can now add multiple partitioned data sets or USS directories to your workspace at once using the "Add to Workspace" feature. [#3324](https://github.com/zowe/zowe-explorer-vscode/issues/3324)
- Fixed an issue where a migrated data set is unusable after it is recalled through Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294)
- Fixed an issue where a recalled PDS is expandable after it is migrated through Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294)
- Fixed an issue where data set nodes did not update if migrated or recalled outside of Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294)
- Exposed read and write access to local storage keys for Zowe Explorer extenders. [#3180](https://github.com/zowe/zowe-explorer-vscode/issues/3180)

### Bug fixes

- Fixed an issue during initialization where the error dialog shown for a broken team configuration file was missing the "Show Config" action. [#3322](https://github.com/zowe/zowe-explorer-vscode/pull/3322)
- Fixed an issue where editing a team config file or updating secrets in the OS credential vault could trigger multiple events for a single action. [#3296](https://github.com/zowe/zowe-explorer-vscode/pull/3296)
- Fixed an issue where opening a PDS member after renaming an expanded PDS resulted in an error. [#3314](https://github.com/zowe/zowe-explorer-vscode/issues/3314)
- Fixed issue where persistent settings defined at the workspace level were migrated into global storage rather than workspace-specific storage. [#3180](https://github.com/zowe/zowe-explorer-vscode/issues/3180)
- Fixed an issue where a migrated data set is unusable after it is recalled through Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294)
- Fixed an issue where a recalled PDS is expandable after it is migrated through Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294)
- Fixed an issue where data set nodes did not update if migrated or recalled outside of Zowe Explorer. [#3294](https://github.com/zowe/zowe-explorer-vscode/issues/3294)

## `3.0.3`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe("mvsCommandActions unit testing", () => {
};
}),
});
Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({ persistence: true, favorites: [], history: [], sessions: ["zosmf"], searchHistory: [], fileHistory: [] }),
update: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe("TsoCommandHandler unit testing", () => {
};
}),
});
Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({ persistence: true, favorites: [], history: [], sessions: ["zosmf"], searchHistory: [], fileHistory: [] }),
update: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe("UnixCommand Actions Unit Testing", () => {
}),
});

Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({ persistence: true, favorites: [], history: [], sessions: ["zosmf"], searchHistory: [], fileHistory: [] }),
update: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function createGlobalMocks(): { [key: string]: any } {
configurable: true,
});

Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: jest.fn(() => ({ persistence: true })),
update: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { SettingsConfig } from "../../../src/configuration/SettingsConfig";
describe("SettingsConfig Unit Tests", () => {
beforeEach(() => {
Object.defineProperty(ZoweLogger, "trace", { value: jest.fn(), configurable: true });
Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({
persistence: true,
Expand Down Expand Up @@ -209,4 +209,58 @@ describe("SettingsConfig Unit Tests", () => {
expect(migrateToLocalStorageSpy).toHaveBeenCalledTimes(1);
});
});

describe("function migrateSettingsAtLevel", () => {
function getBlockMocks() {
const configurationsMock = jest.spyOn(SettingsConfig as any, "configurations", "get");
const setDirectValueMock = jest.spyOn(SettingsConfig, "setDirectValue").mockImplementation();
const setValueMock = jest.spyOn(ZoweLocalStorage, "setValue").mockImplementation();
jest.spyOn(SettingsConfig, "setMigratedDsTemplates").mockImplementation();

return {
configurationsMock,
setDirectValueMock,
setValueMock,
};
}

it("migrates workspace-level settings from settings config", async () => {
const blockMocks = getBlockMocks();
blockMocks.configurationsMock.mockReturnValue({
inspect: () => ({
globalValue: undefined,
workspaceValue: 123,
}),
});
await (SettingsConfig as any).migrateSettingsAtLevel(vscode.ConfigurationTarget.Workspace);
for (const [_, value, setInWorkspace] of blockMocks.setValueMock.mock.calls) {
expect(value).toBe(123);
expect(setInWorkspace).toBe(true);
}
for (const [_, value, target] of blockMocks.setDirectValueMock.mock.calls) {
expect(value).toEqual(undefined);
expect(target).toBe(vscode.ConfigurationTarget.Workspace);
}
});

it("migrates global-level settings from settings config", async () => {
const blockMocks = getBlockMocks();
blockMocks.configurationsMock.mockReturnValue({
inspect: () => ({
globalValue: 123,
workspaceValue: undefined,
}),
});
await (SettingsConfig as any).migrateSettingsAtLevel(vscode.ConfigurationTarget.Global);

for (const [_, value, setInWorkspace] of blockMocks.setValueMock.mock.calls) {
expect(value).toBe(123);
expect(setInWorkspace).toBe(false);
}
for (const [_, value, target] of blockMocks.setDirectValueMock.mock.calls) {
expect(value).toEqual(undefined);
expect(target).toBe(vscode.ConfigurationTarget.Global);
}
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { createUSSSessionNode, createUSSTree } from "../../__mocks__/mockCreator
import { createJobsTree, createIJobObject } from "../../__mocks__/mockCreators/jobs";
import { SettingsConfig } from "../../../src/configuration/SettingsConfig";
import { ZoweExplorerExtender } from "../../../src/extending/ZoweExplorerExtender";
import { ZoweLocalStorage } from "../../../src/tools/ZoweLocalStorage";
import { LocalStorageAccess, ZoweLocalStorage } from "../../../src/tools/ZoweLocalStorage";
import { ZoweLogger } from "../../../src/tools/ZoweLogger";
import { UssFSProvider } from "../../../src/trees/uss/UssFSProvider";
import { ProfilesUtils } from "../../../src/utils/ProfilesUtils";
Expand Down Expand Up @@ -71,7 +71,7 @@ describe("ZoweExplorerExtender unit tests", () => {
value: newMocks.mockTextDocument,
configurable: true,
});
Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({ persistence: true, favorites: [], history: [], sessions: ["zosmf"], searchHistory: [], fileHistory: [] }),
update: jest.fn(),
Expand Down Expand Up @@ -324,4 +324,11 @@ describe("ZoweExplorerExtender unit tests", () => {
expect(blockMocks.instTest.getErrorCorrelator()).toBe(ErrorCorrelator.getInstance());
});
});

describe("getLocalStorage", () => {
it("returns the singleton instance of LocalStorageAccess", () => {
const blockMocks = createBlockMocks();
expect(blockMocks.instTest.getLocalStorage()).toBe(LocalStorageAccess);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ async function createGlobalMocks() {
value: jest.fn(),
configurable: true,
});
Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({ persistence: true, favorites: [], history: [], sessions: ["zosmf"], searchHistory: [], fileHistory: [] }),
update: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe("Checking icon generator's basics", () => {
const createTreeView = jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() });

Object.defineProperty(vscode.window, "createTreeView", { value: createTreeView });
Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({ persistence: true, favorites: [], history: [], sessions: ["zosmf"], searchHistory: [], fileHistory: [] }),
update: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
*/

import * as vscode from "vscode";
import { ZoweLocalStorage } from "../../../src/tools/ZoweLocalStorage";
import { LocalStorageAccess, StorageKeys, ZoweLocalStorage } from "../../../src/tools/ZoweLocalStorage";
import { PersistenceSchemaEnum } from "@zowe/zowe-explorer-api";
import { Definitions } from "../../../src/configuration/Definitions";

describe("ZoweLocalStorage Unit Tests", () => {
it("should initialize successfully", () => {
const mockGlobalState = { get: jest.fn(), update: jest.fn(), keys: () => [] } as vscode.Memento;
ZoweLocalStorage.initializeZoweLocalStorage(mockGlobalState);
expect((ZoweLocalStorage as any).storage).toEqual(mockGlobalState);
expect((ZoweLocalStorage as any).globalState).toEqual(mockGlobalState);
});

it("should get and set values successfully", () => {
Expand All @@ -31,4 +32,102 @@ describe("ZoweLocalStorage Unit Tests", () => {
ZoweLocalStorage.setValue("fruit" as PersistenceSchemaEnum, "banana");
expect(ZoweLocalStorage.getValue("fruit" as PersistenceSchemaEnum)).toEqual("banana");
});

it("should get workspace values with no default and fallback to global if not found", () => {
const globalStorage = {};
const workspaceStorage = {};
const mockGlobalState = {
get: jest.fn().mockImplementation((key, defaultValue) => globalStorage[key] ?? defaultValue),
update: jest.fn().mockImplementation((key, value) => (globalStorage[key] = value)),
keys: () => [],
};
const mockWorkspaceState = {
get: jest.fn().mockImplementation((key, defaultValue) => workspaceStorage[key] ?? defaultValue),
update: jest.fn().mockImplementation((key, value) => (workspaceStorage[key] = value)),
keys: () => [],
};
ZoweLocalStorage.initializeZoweLocalStorage(mockGlobalState, mockWorkspaceState);
// add value into local storage
ZoweLocalStorage.setValue("fruit" as PersistenceSchemaEnum, "banana");

// assert that it can still be retrieved from global storage
expect(ZoweLocalStorage.getValue("fruit" as PersistenceSchemaEnum)).toEqual("banana");

// workspace state access should have default value of undefined
// covers `ZoweLocalStorage.workspaceState?.get<T>(key, undefined) ?? ZoweLocalStorage.globalState.get<T>(key, defaultValue);`
expect(mockWorkspaceState.get).toHaveBeenCalledWith("fruit" as PersistenceSchemaEnum, undefined);
// expect global state to be accessed since key in workspace state was undefined
expect(mockGlobalState.get).toHaveBeenCalledWith("fruit" as PersistenceSchemaEnum, undefined);
});

it("should set workspace values successfully when setInWorkspace is true", () => {
const globalState = { get: jest.fn(), update: jest.fn(), keys: () => [] } as vscode.Memento;
const workspaceState = { get: jest.fn(), update: jest.fn(), keys: () => [] } as vscode.Memento;
ZoweLocalStorage.initializeZoweLocalStorage(globalState, workspaceState);
ZoweLocalStorage.setValue("fruit" as PersistenceSchemaEnum, "banana", true);
expect(workspaceState.update).toHaveBeenCalled();
expect(globalState.update).not.toHaveBeenCalled();
});
});

describe("LocalStorageAccess", () => {
// Read: 1, Write: 2, Read | Write: 3
function omitKeysWithPermission(permBits: number): StorageKeys[] {
return Object.values({ ...Definitions.LocalStorageKey, ...PersistenceSchemaEnum }).filter(
(k) => !(((LocalStorageAccess as any).accessControl[k] & permBits) > 0)
);
}
function keysWithPerm(permBits: number): StorageKeys[] {
return Object.values({ ...Definitions.LocalStorageKey, ...PersistenceSchemaEnum }).filter(
(k) => (LocalStorageAccess as any).accessControl[k] === permBits
);
}

describe("getReadableKeys", () => {
it("returns a list of readable keys to the user", () => {
expect(LocalStorageAccess.getReadableKeys()).toStrictEqual([...keysWithPerm(1), ...keysWithPerm(3)]);
});
});

describe("getWritableKeys", () => {
it("returns a list of writable keys to the user", () => {
expect(LocalStorageAccess.getWritableKeys()).toStrictEqual([...keysWithPerm(2), ...keysWithPerm(3)]);
});
});

describe("getValue", () => {
it("calls ZoweLocalStorage.getValue for all readable keys", () => {
const getValueMock = jest.spyOn(ZoweLocalStorage, "getValue").mockReturnValue(123);
for (const key of keysWithPerm(1)) {
expect(LocalStorageAccess.getValue(key)).toBe(123);
expect(getValueMock).toHaveBeenCalledWith(key);
}
});

it("throws error for all keys that are not readable", () => {
for (const key of omitKeysWithPermission(1)) {
expect(() => LocalStorageAccess.getValue(key)).toThrow(`Insufficient read permissions for ${key as string} in local storage.`);
}
});
});

describe("setValue", () => {
it("calls ZoweLocalStorage.setValue for all writable keys", async () => {
const setValueMock = jest.spyOn(ZoweLocalStorage, "setValue").mockImplementation();
const expectWritableSpy = jest.spyOn(LocalStorageAccess as any, "expectWritable");
for (const key of keysWithPerm(2)) {
await LocalStorageAccess.setValue(key, 123);
expect(setValueMock).toHaveBeenCalledWith(key, 123);
expect(expectWritableSpy).not.toThrow();
}
});

it("throws error for all keys that are not writable", () => {
for (const key of omitKeysWithPermission(2)) {
expect(() => LocalStorageAccess.setValue(key, undefined)).toThrow(
`Insufficient write permissions for ${key as string} in local storage.`
);
}
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { ZowePersistentFilters } from "../../../src/tools/ZowePersistentFilters"

describe("PersistentFilters Unit Test", () => {
Object.defineProperty(ZoweLogger, "trace", { value: jest.fn(), configurable: true });
Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({
persistence: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jest.mock("../../../src/tools/ZoweLogger");

describe("ZoweSaveQueue - unit tests", () => {
const createGlobalMocks = () => {
Object.defineProperty(ZoweLocalStorage, "storage", {
Object.defineProperty(ZoweLocalStorage, "globalState", {
value: {
get: () => ({ persistence: true, favorites: [], history: [], sessions: ["zosmf"], searchHistory: [], fileHistory: [] }),
update: jest.fn(),
Expand Down
Loading

0 comments on commit 2797547

Please sign in to comment.