Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(v2): Update migrated and recalled data sets in tree #3333

Open
wants to merge 15 commits into
base: v2-lts
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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);
});
});
78 changes: 65 additions & 13 deletions packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,59 @@
}
}

/**
* 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
*
* @returns {Promise<ZoweDatasetNode[]>}
*/
public async getChildren(): Promise<ZoweDatasetNode[]> {

Check warning on line 186 in packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts

View workflow job for this annotation

GitHub Actions / lint

Async method 'getChildren' has a complexity of 41. Maximum allowed is 15
ZoweLogger.trace("ZoweDatasetNode.getChildren called.");
if (!this.pattern && contextually.isSessionNotFav(this)) {
return [
Expand Down Expand Up @@ -182,41 +229,46 @@
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();

Check warning on line 235 in packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts

View check run for this annotation

Codecov / codecov/patch

packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts#L235

Added line #L235 was not covered by tests
}
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) {
Expand Down Expand Up @@ -516,7 +568,7 @@
return responses;
}

public async openDs(forceDownload: boolean, previewMember: boolean, datasetProvider: IZoweTree<IZoweDatasetTreeNode>): Promise<void> {

Check warning on line 571 in packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts

View workflow job for this annotation

GitHub Actions / lint

Async method 'openDs' has a complexity of 21. Maximum allowed is 15
ZoweLogger.trace("ZoweDatasetNode.openDs called.");
await datasetProvider.checkCurrentProfile(this);

Expand Down
16 changes: 4 additions & 12 deletions packages/zowe-explorer/src/dataset/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@
* @param {IZoweDatasetTreeNode} node - The node selected for deletion
* @param {DatasetTree} datasetProvider - the tree which contains the nodes
*/
export async function deleteDatasetPrompt(datasetProvider: api.IZoweTree<api.IZoweDatasetTreeNode>, node?: api.IZoweDatasetTreeNode): Promise<void> {

Check warning on line 249 in packages/zowe-explorer/src/dataset/actions.ts

View workflow job for this annotation

GitHub Actions / lint

Async function 'deleteDatasetPrompt' has a complexity of 25. Maximum allowed is 15
ZoweLogger.trace("dataset.actions.deleteDatasetPrompt called.");
let nodes: api.IZoweDatasetTreeNode[];
const treeView = datasetProvider.getTreeView();
Expand Down Expand Up @@ -643,7 +643,7 @@
const templates = datasetProvider.getDsTemplates();
const templateNames: string[] = [];
templates?.forEach((template) => {
Object.entries(template).forEach(([key, value]) => {

Check warning on line 646 in packages/zowe-explorer/src/dataset/actions.ts

View workflow job for this annotation

GitHub Actions / lint

'value' is defined but never used. Allowed unused args must match /^_/u
templateNames.push(key);
});
});
Expand Down Expand Up @@ -688,7 +688,7 @@
if (template[key].dsorg === "PS") {
typeEnum = 4;
} else {
typeEnum = 3;

Check warning on line 691 in packages/zowe-explorer/src/dataset/actions.ts

View workflow job for this annotation

GitHub Actions / lint

No magic number: 3
}
propertiesFromDsType = value;
}
Expand Down Expand Up @@ -1357,11 +1357,9 @@
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);
Expand All @@ -1388,15 +1386,9 @@
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);
Expand Down Expand Up @@ -1518,7 +1510,7 @@
* @export
* @param {vscode.TextDocument} doc - TextDocument that is being saved
*/
export async function saveFile(doc: vscode.TextDocument, datasetProvider: api.IZoweTree<api.IZoweDatasetTreeNode>): Promise<void> {

Check warning on line 1513 in packages/zowe-explorer/src/dataset/actions.ts

View workflow job for this annotation

GitHub Actions / lint

Async function 'saveFile' has a complexity of 17. Maximum allowed is 15
ZoweLogger.trace("dataset.actions.saveFile called.");
// Check if file is a data set, instead of some other file
const docPath = path.join(doc.fileName, "..");
Expand Down
Loading