diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 67fde5552..e53ea6dbf 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -14,6 +14,9 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Fixed an issue where the `Create Directory` and `Create File` features would continue processing when the first prompt was dismissed, causing an incorrect path to be generated. [#3183](https://github.com/zowe/zowe-explorer-vscode/pull/3183) - Fixed an issue where the `Create Directory` and `Create File` features would incorrectly handle user-specified locations with trailing slashes. [#3183](https://github.com/zowe/zowe-explorer-vscode/pull/3183) - Fixed an issue where renaming a data set with unsaved changes did not cancel the rename operation. Now, when renaming a data set with unsaved changes, you are prompted to resolve them before continuing. [#3328](https://github.com/zowe/zowe-explorer-vscode/pull/3328) +- 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) ## `2.18.0` diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/ZoweDatasetNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/ZoweDatasetNode.unit.test.ts index 2e1bd4915..cd76da246 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/ZoweDatasetNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/ZoweDatasetNode.unit.test.ts @@ -29,6 +29,7 @@ import * as sharedUtils from "../../../src/shared/utils"; import { Profiles } from "../../../src/Profiles"; import { ZoweLogger } from "../../../src/utils/LoggerUtils"; import { LocalFileManagement } from "../../../src/utils/LocalFileManagement"; +import { getIconById, IconId } from "../../../src/generators/icons"; // Missing the definition of path module, because I need the original logic for tests jest.mock("fs"); @@ -435,3 +436,86 @@ describe("ZoweDatasetNode Unit Tests - Function node.setIcon()", () => { expect(mocked(vscode.commands.executeCommand)).toHaveBeenCalledWith("zowe.ds.refreshDataset", node); }); }); + +describe("ZoweDatasetNode Unit Tests - function datasetRecalled", () => { + it("changes the collapsible state", async () => { + const dsNode = new ZoweDatasetNode({ + label: "MIGRATED.PDS", + collapsibleState: vscode.TreeItemCollapsibleState.None, + contextOverride: globals.DS_MIGRATED_FILE_CONTEXT, + profile: createIProfile(), + }); + await (dsNode as any).datasetRecalled(true); + expect(dsNode.collapsibleState).toBe(vscode.TreeItemCollapsibleState.Collapsed); + }); + + it("adds the open command to the node - PS", async () => { + const dsNode = new ZoweDatasetNode({ + label: "MIGRATED.PS", + collapsibleState: vscode.TreeItemCollapsibleState.None, + contextOverride: globals.DS_MIGRATED_FILE_CONTEXT, + profile: createIProfile(), + }); + await (dsNode as any).datasetRecalled(false); + expect(dsNode.command).toStrictEqual({ command: "zowe.ds.ZoweNode.openPS", title: "", arguments: [dsNode] }); + }); + + it("updates the icon to folder - PDS", async () => { + const dsNode = new ZoweDatasetNode({ + label: "MIGRATED.PDS", + collapsibleState: vscode.TreeItemCollapsibleState.None, + contextOverride: globals.DS_MIGRATED_FILE_CONTEXT, + profile: createIProfile(), + }); + await (dsNode as any).datasetRecalled(true); + expect(dsNode.iconPath).toBe(getIconById(IconId.folder).path); + }); + + it("updates the icon to file - PS", async () => { + const dsNode = new ZoweDatasetNode({ + label: "MIGRATED.PS", + collapsibleState: vscode.TreeItemCollapsibleState.None, + contextOverride: globals.DS_MIGRATED_FILE_CONTEXT, + profile: createIProfile(), + }); + await (dsNode as any).datasetRecalled(false); + expect(dsNode.iconPath).toBe(getIconById(IconId.document).path); + }); +}); + +describe("ZoweDatasetNode Unit Tests - function datasetMigrated", () => { + it("changes the collapsible state", () => { + const dsNode = new ZoweDatasetNode({ + label: "SOME.PDS", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + contextOverride: globals.DS_PDS_CONTEXT, + profile: createIProfile(), + }); + dsNode.datasetMigrated(); + expect(dsNode.collapsibleState).toBe(vscode.TreeItemCollapsibleState.None); + }); + + it("removes the node command", () => { + const dsNode = new ZoweDatasetNode({ + label: "SOME.PDS", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + contextOverride: globals.DS_PDS_CONTEXT, + parentNode: createDatasetSessionNode(createISession(), createIProfile()), + profile: createIProfile(), + }); + dsNode.datasetMigrated(); + expect(dsNode.resourceUri).toBeUndefined(); + expect(dsNode.command).toBeUndefined(); + }); + + it("changes the icon to the migrated icon", () => { + const dsNode = new ZoweDatasetNode({ + label: "MIGRATED.PDS", + collapsibleState: vscode.TreeItemCollapsibleState.None, + contextOverride: globals.DS_MIGRATED_FILE_CONTEXT, + profile: createIProfile(), + }); + dsNode.datasetMigrated(); + expect(dsNode.iconPath).toBe(getIconById(IconId.migrated).path); + }); +}); diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index fdc020e50..21ff92cd1 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -131,6 +131,53 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } } + /** + * Updates an existing data set node that was recalled so it can be interacted with. + * @param isPds Whether the data set is a PDS + */ + private datasetRecalled(isPds: boolean): void { + // Change context value to match dsorg, update collapsible state + // Preserve favorite context and any additional context values + this.contextValue = this.contextValue.replace(globals.DS_MIGRATED_FILE_CONTEXT, isPds ? globals.DS_PDS_CONTEXT : globals.DS_DS_CONTEXT); + this.collapsibleState = isPds ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None; + + // For sequential data sets, re-apply the command so that they can be opened + if (!isPds) { + this.command = { command: "zowe.ds.ZoweNode.openPS", title: "", arguments: [this] }; + } + + // Replace icon on existing node with new one + const icon = getIconByNode(this); + if (icon) { + this.setIcon(icon.path); + } + } + + /** + * Updates a data set node so it is marked as migrated. + */ + public datasetMigrated(): void { + // Change the context value and collapsible state to represent a migrated data set + // Preserve favorite context and any additional context values + const isBinary = contextually.isBinary(this); + const isPds = this.collapsibleState !== vscode.TreeItemCollapsibleState.None; + let previousContext = isBinary ? globals.DS_DS_BINARY_CONTEXT : globals.DS_DS_CONTEXT; + if (isPds) { + previousContext = globals.DS_PDS_CONTEXT; + } + this.contextValue = this.contextValue.replace(previousContext, globals.DS_MIGRATED_FILE_CONTEXT); + this.collapsibleState = vscode.TreeItemCollapsibleState.None; + + // Remove the node's command + this.command = undefined; + + // Assign migrated icon to the data set node + const icon = getIconByNode(this); + if (icon) { + this.setIcon(icon.path); + } + } + /** * Retrieves child nodes of this ZoweDatasetNode * @@ -182,41 +229,46 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod const dsEntry = item.dsname ?? item.member; const existing = this.children.find((element) => element.label.toString() === dsEntry); if (existing) { + if (contextually.isMigrated(existing) && item.migr?.toUpperCase() !== "YES") { + existing.datasetRecalled(item.dsorg === "PO" || item.dsorg === "PO-E"); + } else if (!contextually.isMigrated(existing) && item.migr?.toUpperCase() === "YES") { + existing.datasetMigrated(); + } existing.updateStats(item); elementChildren[existing.label.toString()] = existing; - // Creates a ZoweDatasetNode for a PDS - } else if (item.dsorg === "PO" || item.dsorg === "PO-E") { + } else if (item.migr && item.migr.toUpperCase() === "YES") { + // Creates a ZoweDatasetNode for a migrated dataset const temp = new ZoweDatasetNode({ label: item.dsname, - collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: this, + contextOverride: globals.DS_MIGRATED_FILE_CONTEXT, profile: this.getProfile(), }); elementChildren[temp.label.toString()] = temp; - // Creates a ZoweDatasetNode for a dataset with imperative errors - } else if (item.error instanceof zowe.imperative.ImperativeError) { + } else if (item.dsorg === "PO" || item.dsorg === "PO-E") { + // Creates a ZoweDatasetNode for a PDS const temp = new ZoweDatasetNode({ label: item.dsname, - collapsibleState: vscode.TreeItemCollapsibleState.None, + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, parentNode: this, - contextOverride: globals.DS_FILE_ERROR_CONTEXT, profile: this.getProfile(), }); - temp.command = { command: "zowe.placeholderCommand", title: "" }; - temp.errorDetails = item.error; // Save imperative error to avoid extra z/OS requests elementChildren[temp.label.toString()] = temp; - // Creates a ZoweDatasetNode for a migrated dataset - } else if (item.migr && item.migr.toUpperCase() === "YES") { + } else if (item.error instanceof zowe.imperative.ImperativeError) { + // Creates a ZoweDatasetNode for a dataset with imperative errors const temp = new ZoweDatasetNode({ label: item.dsname, collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: this, - contextOverride: globals.DS_MIGRATED_FILE_CONTEXT, + contextOverride: globals.DS_FILE_ERROR_CONTEXT, profile: this.getProfile(), }); + temp.command = { command: "zowe.placeholderCommand", title: "" }; + temp.errorDetails = item.error; // Save imperative error to avoid extra z/OS requests elementChildren[temp.label.toString()] = temp; - // Creates a ZoweDatasetNode for a VSAM file } else if (item.dsorg === "VS") { + // Creates a ZoweDatasetNode for a VSAM file let altLabel = item.dsname; let endPoint = altLabel.indexOf(".DATA"); if (endPoint === -1) { diff --git a/packages/zowe-explorer/src/dataset/actions.ts b/packages/zowe-explorer/src/dataset/actions.ts index fe49b921c..86fc74d87 100644 --- a/packages/zowe-explorer/src/dataset/actions.ts +++ b/packages/zowe-explorer/src/dataset/actions.ts @@ -1357,11 +1357,9 @@ export async function hMigrateDataSet( if (Profiles.getInstance().validProfile !== api.ValidProfileEnum.INVALID) { const { dataSetName } = dsUtils.getNodeLabels(node); try { - const response = await ZoweExplorerApiRegister.getMvsApi(node.getProfile()).hMigrateDataSet(dataSetName); api.Gui.showMessage(localize("hMigrateDataSet.requestSent", "Migration of data set {0} requested.", dataSetName)); - node.contextValue = globals.DS_MIGRATED_FILE_CONTEXT; - node.setIcon(getIconByNode(node).path); - datasetProvider.refresh(); + const response = await ZoweExplorerApiRegister.getMvsApi(node.getProfile()).hMigrateDataSet(dataSetName); + datasetProvider.refreshElement(node.getParent()); return response; } catch (err) { ZoweLogger.error(err); @@ -1388,15 +1386,9 @@ export async function hRecallDataSet( if (Profiles.getInstance().validProfile !== api.ValidProfileEnum.INVALID) { const { dataSetName } = dsUtils.getNodeLabels(node); try { - const response = await ZoweExplorerApiRegister.getMvsApi(node.getProfile()).hRecallDataSet(dataSetName); api.Gui.showMessage(localize("hRecallDataSet.requestSent", "Recall of data set {0} requested.", dataSetName)); - if (node.collapsibleState !== vscode.TreeItemCollapsibleState.None) { - node.contextValue = globals.DS_PDS_CONTEXT; - } else { - node.contextValue = node.binary ? globals.DS_DS_BINARY_CONTEXT : globals.DS_DS_CONTEXT; - } - node.setIcon(getIconByNode(node).path); - datasetProvider.refresh(); + const response = await ZoweExplorerApiRegister.getMvsApi(node.getProfile()).hRecallDataSet(dataSetName); + datasetProvider.refreshElement(node.getSessionNode()); return response; } catch (err) { ZoweLogger.error(err);