From 9ae62e2ce8d96bc378097078df866941aaf7d9f7 Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:47:32 +0530 Subject: [PATCH 01/10] Drag and drop of MVS Seq Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- .../src/trees/dataset/DatasetTree.ts | 136 +++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index ee4cd7960f..224dff05eb 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -25,6 +25,7 @@ import { FsAbstractUtils, DatasetMatch, ZoweExplorerApiType, + ZoweScheme, } from "@zowe/zowe-explorer-api"; import { ZoweDatasetNode } from "./ZoweDatasetNode"; import { DatasetFSProvider } from "./DatasetFSProvider"; @@ -43,6 +44,7 @@ import { FilterDescriptor, FilterItem } from "../../management/FilterManagement" import { IconUtils } from "../../icons/IconUtils"; import { AuthUtils } from "../../utils/AuthUtils"; import { DataSetTemplates } from "./DatasetTemplates"; +import * as zosfiles from "@zowe/zos-files-for-zowe-sdk"; /** * A tree that contains nodes of sessions and data sets @@ -64,9 +66,11 @@ export class DatasetTree extends ZoweTreeProvider implemen // public memberPattern: IZoweDatasetTreeNode[] = []; private treeView: vscode.TreeView; - public dragMimeTypes: string[] = ["application/vnd.code.tree.zowe.ds.explorer"]; + public dragMimeTypes: string[] = []; public dropMimeTypes: string[] = ["application/vnd.code.tree.zowe.ds.explorer"]; + private draggedNodes: Record = {}; + public constructor() { super( DatasetTree.persistenceSchema, @@ -83,12 +87,142 @@ export class DatasetTree extends ZoweTreeProvider implemen this.mSessionNodes = [this.mFavoriteSession]; this.treeView = Gui.createTreeView("zowe.ds.explorer", { treeDataProvider: this, + dragAndDropController: this, canSelectMany: true, }); // eslint-disable-next-line @typescript-eslint/unbound-method this.treeView.onDidCollapseElement(TreeViewUtils.refreshIconOnCollapse([SharedContext.isPds, SharedContext.isDsSession], this)); } + public handleDrag(source: IZoweDatasetTreeNode[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): void { + const items = []; + for (const srcItem of source) { + this.draggedNodes[srcItem.resourceUri.path] = srcItem; + items.push({ + label: srcItem.label, + uri: srcItem.resourceUri, + }); + } + dataTransfer.set("application/vnd.code.tree.zowe.ds.explorer", new vscode.DataTransferItem(items)); + } + + private async crossLparMove( + sourceNode: IZoweDatasetTreeNode, + sourceUri: vscode.Uri, + destUri: vscode.Uri, + recursiveCall?: boolean + ): Promise { + const destinationInfo = FsAbstractUtils.getInfoForUri(destUri, Profiles.getInstance()); + // this is like when the dragged item is a file like sequential + try { + await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSet( + zosfiles.CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, + sourceNode.getLabel() as string, + {} + ); + } catch (err) { + //file might already exist. Ignore the error and try to write it to lpar + } + // read the contents from the source LPAR + // const contents = await ZoweExplorerApiRegister.getMvsApi(sourceNode.getProfile()).getContents(sourceNode.getLabel() as string); + const contents = await DatasetFSProvider.instance.readFile(sourceNode.resourceUri); + //write the contents to the destination LPAR + try { + await DatasetFSProvider.instance.writeFile( + destUri.with({ + query: "forceUplaod=true", + }), + contents, + { create: true, overwrite: true } + ); + } catch (err) { + // If the write fails, we cannot move to the next file + if (err instanceof Error) { + Gui.errorMessage( + vscode.l10n.t("Failed to move file {0}: {1}", destUri.path.substring(destinationInfo.slashAfterProfilePos), err.message) + ); + } + return; + } + } + + public async handleDrop( + targetNode: IZoweDatasetTreeNode | undefined, + dataTransfer: vscode.DataTransfer, + _token: vscode.CancellationToken + ): Promise { + const droppedItems = dataTransfer.get("application/vnd.code.tree.zowe.ds.explorer"); + if (!droppedItems) { + return; + } + + //get the closest parent folder if the target is a + let target = targetNode; + if (!SharedContext.isPds(target)) { + target = target.getParent() as IZoweDatasetTreeNode; + } + + //should see y this condition is present here + // If the target path fully contains the path of the dragged node, + // the user is trying to move a parent node into its child - invalid operation + const movedIntoChild = Object.values(this.draggedNodes).some((n) => target.resourceUri.path.startsWith(n.resourceUri.path)); + if (movedIntoChild) { + this.draggedNodes = {}; + return; + } + + // determine if any overwrites may occur + const willOverwrite = Object.values(this.draggedNodes).some((n) => target.children?.find((tc) => tc.label === n.label) != null); + if (willOverwrite) { + const userOpts = [vscode.l10n.t("Confirm")]; + const resp = await Gui.warningMessage( + vscode.l10n.t("One or more items may be overwritten from this drop operation. Confirm or cancel?"), + { + items: userOpts, + vsCodeOpts: { + modal: true, + }, + } + ); + if (resp == null || resp !== userOpts[0]) { + return; + } + } + + const movingMsg = Gui.setStatusBarMessage(`$(sync~spin) ${vscode.l10n.t("Moving MVS files...")}`); + const parentsToUpdate = new Set(); + + for (const item of droppedItems.value) { + const node = this.draggedNodes[item.uri.path]; + if (node.getParent() === target) { + //skip nodes that are direct children of the target node + continue; + } + + const newUriForNode = vscode.Uri.from({ + scheme: ZoweScheme.DS, + path: path.posix.join("/", target.getProfile().name, target.fullPath, item.label as string), + }); + const prof = node.getProfile(); + // const hasMoveApi = ZoweExplorerApiRegister.getMvsApi(prof). + + await this.crossLparMove(node, node.resourceUri, newUriForNode); + + //remove node from old parent and relocate to new parent + const oldParent = node.getParent() as IZoweDatasetTreeNode; + oldParent.children = oldParent.children.filter((c) => c !== node); + node.resourceUri = newUriForNode; + + parentsToUpdate.add(node.getParent() as IZoweDatasetTreeNode); + } + for (const parent of parentsToUpdate) { + this.refreshElement(parent); + } + this.refreshElement(target); + movingMsg.dispose(); + this.draggedNodes = {}; + } + /** * Rename data set * From 9d0466d564752aa6071b13409c0d9f610eca32a3 Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:23:56 +0530 Subject: [PATCH 02/10] Support for drag and drop of PDS and members Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- .../src/trees/dataset/DatasetTree.ts | 101 ++++++++++++------ 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index 224dff05eb..9cd21821e8 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -113,36 +113,77 @@ export class DatasetTree extends ZoweTreeProvider implemen recursiveCall?: boolean ): Promise { const destinationInfo = FsAbstractUtils.getInfoForUri(destUri, Profiles.getInstance()); - // this is like when the dragged item is a file like sequential - try { - await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSet( - zosfiles.CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, - sourceNode.getLabel() as string, - {} - ); - } catch (err) { - //file might already exist. Ignore the error and try to write it to lpar - } - // read the contents from the source LPAR - // const contents = await ZoweExplorerApiRegister.getMvsApi(sourceNode.getProfile()).getContents(sourceNode.getLabel() as string); - const contents = await DatasetFSProvider.instance.readFile(sourceNode.resourceUri); - //write the contents to the destination LPAR - try { - await DatasetFSProvider.instance.writeFile( - destUri.with({ - query: "forceUplaod=true", - }), - contents, - { create: true, overwrite: true } - ); - } catch (err) { - // If the write fails, we cannot move to the next file - if (err instanceof Error) { - Gui.errorMessage( - vscode.l10n.t("Failed to move file {0}: {1}", destUri.path.substring(destinationInfo.slashAfterProfilePos), err.message) + if (SharedContext.isPds(sourceNode)) { + if (!DatasetFSProvider.instance.exists(destUri)) { + // create a PDS on remote + try { + await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSet( + zosfiles.CreateDataSetTypeEnum.DATA_SET_PARTITIONED, + sourceNode.getLabel() as string, + {} + ); + } catch (err) { + //error + } + // create directory entry in local + DatasetFSProvider.instance.createDirectory(destUri); + } + const children = await sourceNode.getChildren(); + for (const childNode of children) { + // move members within the folder to the destination + await this.crossLparMove( + childNode, + sourceUri.with({ + path: path.posix.join(sourceUri.path, childNode.getLabel() as string), + }), + destUri.with({ + path: path.posix.join(destUri.path, childNode.getLabel() as string), + }), + true ); } - return; + await vscode.workspace.fs.delete(sourceUri, { recursive: true }); + } else { + // this is like when the dragged item is a file like sequential + try { + if (sourceNode.contextValue === Constants.DS_MEMBER_CONTEXT) { + const dsname: string = destUri.path.match(/^\/[^/]+\/(.*?)\/[^/]+$/)[1] + "(" + (sourceNode.getLabel() as string) + ")"; + await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSetMember(dsname, {}); + } else { + await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSet( + zosfiles.CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, + sourceNode.getLabel() as string, + {} + ); + } + } catch (err) { + //file might already exist. Ignore the error and try to write it to lpar + } + // read the contents from the source LPAR + const contents = await DatasetFSProvider.instance.readFile(sourceNode.resourceUri); + //write the contents to the destination LPAR + try { + await DatasetFSProvider.instance.writeFile( + destUri.with({ + query: "forceUplaod=true", + }), + contents, + { create: true, overwrite: true } + ); + } catch (err) { + // If the write fails, we cannot move to the next file + if (err instanceof Error) { + Gui.errorMessage( + vscode.l10n.t("Failed to move file {0}: {1}", destUri.path.substring(destinationInfo.slashAfterProfilePos), err.message) + ); + } + return; + } + + if (!recursiveCall) { + // Delete any files from the selection on the source LPAR + await vscode.workspace.fs.delete(sourceNode.resourceUri, { recursive: false }); + } } } @@ -201,10 +242,8 @@ export class DatasetTree extends ZoweTreeProvider implemen const newUriForNode = vscode.Uri.from({ scheme: ZoweScheme.DS, - path: path.posix.join("/", target.getProfile().name, target.fullPath, item.label as string), + path: path.posix.join("/", target.resourceUri.path, item.label as string), }); - const prof = node.getProfile(); - // const hasMoveApi = ZoweExplorerApiRegister.getMvsApi(prof). await this.crossLparMove(node, node.resourceUri, newUriForNode); From 2bc5e5d6aaf6a6d9854c15e022c857141be8ba55 Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Wed, 22 Jan 2025 14:34:38 +0530 Subject: [PATCH 03/10] Support replacing if already existing Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- .../src/trees/dataset/DatasetFSProvider.ts | 1 - .../src/trees/dataset/DatasetTree.ts | 30 ++++++++----------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts b/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts index 36fb164dd3..2bf4c1b239 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts @@ -608,7 +608,6 @@ export class DatasetFSProvider extends BaseProvider implements vscode.FileSystem }); throw err; } - entry.data = content; // Prompt the user with the conflict dialog await this._handleConflict(uri, entry); diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index 9cd21821e8..252a256ae1 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -94,7 +94,7 @@ export class DatasetTree extends ZoweTreeProvider implemen this.treeView.onDidCollapseElement(TreeViewUtils.refreshIconOnCollapse([SharedContext.isPds, SharedContext.isDsSession], this)); } - public handleDrag(source: IZoweDatasetTreeNode[], dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): void { + public handleDrag(source: IZoweDatasetTreeNode[], dataTransfer: vscode.DataTransfer, _token: vscode.CancellationToken): void { const items = []; for (const srcItem of source) { this.draggedNodes[srcItem.resourceUri.path] = srcItem; @@ -144,17 +144,19 @@ export class DatasetTree extends ZoweTreeProvider implemen } await vscode.workspace.fs.delete(sourceUri, { recursive: true }); } else { - // this is like when the dragged item is a file like sequential try { - if (sourceNode.contextValue === Constants.DS_MEMBER_CONTEXT) { - const dsname: string = destUri.path.match(/^\/[^/]+\/(.*?)\/[^/]+$/)[1] + "(" + (sourceNode.getLabel() as string) + ")"; - await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSetMember(dsname, {}); - } else { - await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSet( - zosfiles.CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, - sourceNode.getLabel() as string, - {} - ); + const entry = await DatasetFSProvider.instance.fetchDatasetAtUri(destUri); + if (entry == null) { + if (sourceNode.contextValue === Constants.DS_MEMBER_CONTEXT) { + const dsname: string = destUri.path.match(/^\/[^/]+\/(.*?)\/[^/]+$/)[1] + "(" + (sourceNode.getLabel() as string) + ")"; + await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSetMember(dsname, {}); + } else { + await ZoweExplorerApiRegister.getMvsApi(destinationInfo.profile).createDataSet( + zosfiles.CreateDataSetTypeEnum.DATA_SET_SEQUENTIAL, + sourceNode.getLabel() as string, + {} + ); + } } } catch (err) { //file might already exist. Ignore the error and try to write it to lpar @@ -203,7 +205,6 @@ export class DatasetTree extends ZoweTreeProvider implemen target = target.getParent() as IZoweDatasetTreeNode; } - //should see y this condition is present here // If the target path fully contains the path of the dragged node, // the user is trying to move a parent node into its child - invalid operation const movedIntoChild = Object.values(this.draggedNodes).some((n) => target.resourceUri.path.startsWith(n.resourceUri.path)); @@ -247,11 +248,6 @@ export class DatasetTree extends ZoweTreeProvider implemen await this.crossLparMove(node, node.resourceUri, newUriForNode); - //remove node from old parent and relocate to new parent - const oldParent = node.getParent() as IZoweDatasetTreeNode; - oldParent.children = oldParent.children.filter((c) => c !== node); - node.resourceUri = newUriForNode; - parentsToUpdate.add(node.getParent() as IZoweDatasetTreeNode); } for (const parent of parentsToUpdate) { From 57a40e3d0c5d141a9e5fb265ac2049edbede27fa Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:46:10 +0530 Subject: [PATCH 04/10] Add edge cases and unit test Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- .../trees/dataset/DatasetTree.unit.test.ts | 131 ++++++++++++++++++ .../src/trees/dataset/DatasetTree.ts | 16 ++- 2 files changed, 146 insertions(+), 1 deletion(-) 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 cb1aab8f98..62507d6d09 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 @@ -3462,3 +3462,134 @@ describe("Dataset Tree Unit Tests - Function applyPatternsToChildren", () => { withProfileMock.mockRestore(); }); }); + +describe("DataSetTree Unit Tests - Function handleDrag", () => { + function createBlockMocks() { + const session = createISession(); + const imperativeProfile = createIProfile(); + const datasetSessionNode = createDatasetSessionNode(session, imperativeProfile); + return { + session, + imperativeProfile, + datasetSessionNode, + }; + } + it("adds a DataTransferItem containing info about the dragged Data node", async () => { + createGlobalMocks(); + const blockMocks = createBlockMocks(); + const datasetNode = new ZoweDatasetNode({ + label: "draggedNode", + collapsibleState: vscode.TreeItemCollapsibleState.None, + parentNode: blockMocks.datasetSessionNode, + session: blockMocks.session, + }); + const dataTransferSetMock = jest.fn(); + const testTree = new DatasetTree(); + testTree.handleDrag([datasetNode], { set: dataTransferSetMock } as any, undefined); + expect(dataTransferSetMock).toHaveBeenCalledWith( + "application/vnd.code.tree.zowe.ds.explorer", + new vscode.DataTransferItem([ + { + label: datasetNode.label, + uri: datasetNode.resourceUri, + }, + ]) + ); + }); +}); + +describe("DataSetTree Unit Tests - Function handleDrop", () => { + function createBlockMocks() { + const session = createISession(); + const imperativeProfile = createIProfile(); + const datasetSessionNode = createDatasetSessionNode(session, imperativeProfile); + datasetSessionNode.dirty = false; + const datasetNode = new ZoweDatasetNode({ + label: "draggedNode", + collapsibleState: vscode.TreeItemCollapsibleState.None, + parentNode: datasetSessionNode, + session: session, + }); + datasetNode.dirty = false; + return { + session, + imperativeProfile, + datasetSessionNode, + datasetNode, + }; + } + it("returns early if there are no items in the dataTransfer object", async () => { + createGlobalMocks(); + const blockMocks = createBlockMocks(); + const statusBarMsgSpy = jest.spyOn(Gui, "setStatusBarMessage"); + const getDataTransferMock = jest.spyOn(vscode.DataTransfer.prototype, "get").mockReturnValueOnce(undefined); + const testTree = new DatasetTree(); + await testTree.handleDrop(blockMocks.datasetNode, new vscode.DataTransfer(), undefined); + expect(statusBarMsgSpy).not.toHaveBeenCalled(); + getDataTransferMock.mockRestore(); + }); + + it("handle moving of seq and pds to different profiles dropping on seq", async () => { + createGlobalMocks(); + const testTree = new DatasetTree(); + const statusBarMsgSpy = jest.spyOn(Gui, "setStatusBarMessage"); + const blockMocks = createBlockMocks(); + const datasetSession = blockMocks.datasetSessionNode; + const datasetPdsNode = new ZoweDatasetNode({ + label: "pdsnode", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + profile: datasetSession.getProfile(), + parentNode: blockMocks.datasetSessionNode, + }); + datasetPdsNode.dirty = false; + const memberNode = new ZoweDatasetNode({ + label: "mem1", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + profile: datasetSession.getProfile(), + }); + datasetPdsNode.children = [memberNode]; + datasetPdsNode.contextValue = Constants.DS_PDS_CONTEXT; + memberNode.contextValue = Constants.DS_MEMBER_CONTEXT; + memberNode.dirty = false; + const datasetSeqNode = new ZoweDatasetNode({ + label: "seqnode", + collapsibleState: vscode.TreeItemCollapsibleState.None, + profile: datasetPdsNode.getProfile(), + parentNode: blockMocks.datasetNode, + }); + datasetSeqNode.contextValue = Constants.DS_DS_CONTEXT; + datasetSession.children = [datasetPdsNode, datasetSeqNode]; + const dataTransfer = new vscode.DataTransfer(); + const getDataTransferMock = jest.spyOn(dataTransfer, "get").mockReturnValueOnce({ + value: [ + { + label: datasetPdsNode.label as string, + uri: datasetPdsNode.resourceUri, + }, + ], + } as any); + const draggedNodeMock = new MockedProperty(testTree, "draggedNodes", undefined, { + [datasetPdsNode.resourceUri.path]: datasetPdsNode, + }); + const createDataSetMock = jest.fn(); + const createDataSetMemberMock = jest.fn(); + const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({ + createDataSet: createDataSetMock, + createDataSetMember: createDataSetMemberMock, + } as any); + + const createDirMock = jest.spyOn(DatasetFSProvider.instance as any, "createDirectory").mockResolvedValueOnce(undefined); + const deleteMock = jest.spyOn(vscode.workspace.fs, "delete").mockResolvedValue(undefined); + const readFileMock = jest.spyOn(DatasetFSProvider.instance, "readFile").mockResolvedValue(new Uint8Array([1, 2, 3])); + const writeFileMock = jest.spyOn(DatasetFSProvider.instance, "writeFile").mockResolvedValue(undefined); + await testTree.handleDrop(datasetSeqNode, dataTransfer, undefined); + expect(deleteMock).toHaveBeenCalledWith(datasetPdsNode.resourceUri, { recursive: true }); + expect(statusBarMsgSpy).toHaveBeenCalledWith("$(sync~spin) Moving MVS files..."); + draggedNodeMock[Symbol.dispose](); + getDataTransferMock.mockRestore(); + writeFileMock.mockRestore(); + readFileMock.mockRestore(); + createDirMock.mockRestore(); + mvsApiMock.mockRestore(); + }); +}); diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index 252a256ae1..ad59a2da3d 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -199,8 +199,22 @@ export class DatasetTree extends ZoweTreeProvider implemen return; } - //get the closest parent folder if the target is a let target = targetNode; + for (const item of droppedItems.value) { + const node = this.draggedNodes[item.uri.path]; + if (SharedContext.isPds(target) || SharedContext.isDsMember(target)) { + if (SharedContext.isPds(node) || SharedContext.isDs(node)) { + Gui.errorMessage(vscode.l10n.t("Cannot drop a seq or pds onto a pds")); + return; + } + } + if (SharedContext.isDsMember(node) && SharedContext.isDs(target)) { + Gui.errorMessage(vscode.l10n.t("Cannot drop a member onto a seq dataset")); + return; + } + } + + //get the closest parent folder if the target is not a pds if (!SharedContext.isPds(target)) { target = target.getParent() as IZoweDatasetTreeNode; } From dabdde6854e027adfb326125d2082a2b5a87b65b Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Mon, 27 Jan 2025 21:55:43 +0530 Subject: [PATCH 05/10] Increase test coverage Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- packages/zowe-explorer/CHANGELOG.md | 1 + .../trees/dataset/DatasetTree.unit.test.ts | 131 ++++++++++++++---- .../src/trees/dataset/DatasetFSProvider.ts | 1 + .../src/trees/dataset/DatasetTree.ts | 4 +- 4 files changed, 106 insertions(+), 31 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 9c51f67e25..b66a205777 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -21,6 +21,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added `Open with Encoding` to the context menu of Job Spool files. [#1941](https://github.com/zowe/zowe-explorer-vscode/issues/1941) - Added Time Started, Time Ended, and Time Submitted job properties to the Jobs table view. [#3055](https://github.com/zowe/zowe-explorer-vscode/issues/3055) - Implemented copy/paste functionality of data sets within and across LPARs. [#3012](https://github.com/zowe/zowe-explorer-vscode/issues/3012) +- Implemented drag and drop functionality of data sets within and across LPARs. [#3413](https://github.com/zowe/zowe-explorer-vscode/pull/3413) ### Bug fixes 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 b5b71f4f6b..a6de0aeae5 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 @@ -3530,11 +3530,39 @@ describe("DataSetTree Unit Tests - Function handleDrop", () => { session: session, }); datasetNode.dirty = false; + + const datasetPdsNode = new ZoweDatasetNode({ + label: "pdsnode", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + profile: datasetSessionNode.getProfile(), + parentNode: datasetSessionNode, + }); + datasetPdsNode.dirty = false; + const memberNode = new ZoweDatasetNode({ + label: "mem1", + collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, + profile: datasetSessionNode.getProfile(), + }); + datasetPdsNode.children = [memberNode]; + datasetPdsNode.contextValue = Constants.DS_PDS_CONTEXT; + memberNode.contextValue = Constants.DS_MEMBER_CONTEXT; + memberNode.dirty = false; + const datasetSeqNode = new ZoweDatasetNode({ + label: "seqnode", + collapsibleState: vscode.TreeItemCollapsibleState.None, + profile: datasetPdsNode.getProfile(), + parentNode: datasetNode, + }); + datasetSeqNode.contextValue = Constants.DS_DS_CONTEXT; + return { session, imperativeProfile, datasetSessionNode, datasetNode, + datasetPdsNode, + datasetSeqNode, + memberNode, }; } it("returns early if there are no items in the dataTransfer object", async () => { @@ -3554,41 +3582,19 @@ describe("DataSetTree Unit Tests - Function handleDrop", () => { const statusBarMsgSpy = jest.spyOn(Gui, "setStatusBarMessage"); const blockMocks = createBlockMocks(); const datasetSession = blockMocks.datasetSessionNode; - const datasetPdsNode = new ZoweDatasetNode({ - label: "pdsnode", - collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, - profile: datasetSession.getProfile(), - parentNode: blockMocks.datasetSessionNode, - }); - datasetPdsNode.dirty = false; - const memberNode = new ZoweDatasetNode({ - label: "mem1", - collapsibleState: vscode.TreeItemCollapsibleState.Collapsed, - profile: datasetSession.getProfile(), - }); - datasetPdsNode.children = [memberNode]; - datasetPdsNode.contextValue = Constants.DS_PDS_CONTEXT; - memberNode.contextValue = Constants.DS_MEMBER_CONTEXT; - memberNode.dirty = false; - const datasetSeqNode = new ZoweDatasetNode({ - label: "seqnode", - collapsibleState: vscode.TreeItemCollapsibleState.None, - profile: datasetPdsNode.getProfile(), - parentNode: blockMocks.datasetNode, - }); - datasetSeqNode.contextValue = Constants.DS_DS_CONTEXT; - datasetSession.children = [datasetPdsNode, datasetSeqNode]; + + datasetSession.children = [blockMocks.datasetPdsNode, blockMocks.datasetSeqNode]; const dataTransfer = new vscode.DataTransfer(); const getDataTransferMock = jest.spyOn(dataTransfer, "get").mockReturnValueOnce({ value: [ { - label: datasetPdsNode.label as string, - uri: datasetPdsNode.resourceUri, + label: blockMocks.datasetPdsNode.label as string, + uri: blockMocks.datasetPdsNode.resourceUri, }, ], } as any); const draggedNodeMock = new MockedProperty(testTree, "draggedNodes", undefined, { - [datasetPdsNode.resourceUri.path]: datasetPdsNode, + [blockMocks.datasetPdsNode.resourceUri.path]: blockMocks.datasetPdsNode, }); const createDataSetMock = jest.fn(); const createDataSetMemberMock = jest.fn(); @@ -3601,8 +3607,8 @@ describe("DataSetTree Unit Tests - Function handleDrop", () => { const deleteMock = jest.spyOn(vscode.workspace.fs, "delete").mockResolvedValue(undefined); const readFileMock = jest.spyOn(DatasetFSProvider.instance, "readFile").mockResolvedValue(new Uint8Array([1, 2, 3])); const writeFileMock = jest.spyOn(DatasetFSProvider.instance, "writeFile").mockResolvedValue(undefined); - await testTree.handleDrop(datasetSeqNode, dataTransfer, undefined); - expect(deleteMock).toHaveBeenCalledWith(datasetPdsNode.resourceUri, { recursive: true }); + await testTree.handleDrop(blockMocks.datasetSeqNode, dataTransfer, undefined); + expect(deleteMock).toHaveBeenCalledWith(blockMocks.datasetPdsNode.resourceUri, { recursive: true }); expect(statusBarMsgSpy).toHaveBeenCalledWith("$(sync~spin) Moving MVS files..."); draggedNodeMock[Symbol.dispose](); getDataTransferMock.mockRestore(); @@ -3611,4 +3617,71 @@ describe("DataSetTree Unit Tests - Function handleDrop", () => { createDirMock.mockRestore(); mvsApiMock.mockRestore(); }); + + it("Dragging a pds onto another pds on different LPAR should throw error", async () => { + createGlobalMocks(); + const testTree = new DatasetTree(); + const blockMocks = createBlockMocks(); + const dataTransfer = new vscode.DataTransfer(); + const getDataTransferMock = jest.spyOn(dataTransfer, "get").mockReturnValueOnce({ + value: [ + { + label: blockMocks.datasetPdsNode.label as string, + uri: blockMocks.datasetPdsNode.resourceUri, + }, + ], + } as any); + const draggedNodeMock = new MockedProperty(testTree, "draggedNodes", undefined, { + [blockMocks.datasetPdsNode.resourceUri.path]: blockMocks.datasetPdsNode, + }); + await testTree.handleDrop(blockMocks.datasetPdsNode, dataTransfer, undefined); + expect(Gui.errorMessage).toHaveBeenCalledWith("Cannot drop a sequential dataset or a partitioned dataset onto another PDS."); + getDataTransferMock.mockRestore(); + draggedNodeMock[Symbol.dispose](); + }); + + it("If a member is dropped on a sequential ds, should throw error", async () => { + createGlobalMocks(); + const testTree = new DatasetTree(); + const blockMocks = createBlockMocks(); + const dataTransfer = new vscode.DataTransfer(); + const getDataTransferMock = jest.spyOn(dataTransfer, "get").mockReturnValueOnce({ + value: [ + { + label: blockMocks.memberNode.label as string, + uri: blockMocks.memberNode.resourceUri, + }, + ], + } as any); + const draggedNodeMock = new MockedProperty(testTree, "draggedNodes", undefined, { + [blockMocks.memberNode.resourceUri.path]: blockMocks.memberNode, + }); + await testTree.handleDrop(blockMocks.datasetSeqNode, dataTransfer, undefined); + expect(Gui.errorMessage).toHaveBeenCalledWith("Cannot drop a member onto a sequential dataset."); + getDataTransferMock.mockRestore(); + draggedNodeMock[Symbol.dispose](); + }); + + it("Member being dropped on pds", async () => { + createGlobalMocks(); + const testTree = new DatasetTree(); + const blockMocks = createBlockMocks(); + const dataTransfer = new vscode.DataTransfer(); + const getDataTransferMock = jest.spyOn(dataTransfer, "get").mockReturnValueOnce({ + value: [ + { + label: blockMocks.memberNode.label as string, + uri: blockMocks.memberNode.resourceUri, + }, + ], + } as any); + const draggedNodeMock = new MockedProperty(testTree, "draggedNodes", undefined, { + [blockMocks.memberNode.resourceUri.path]: blockMocks.memberNode, + }); + jest.spyOn(Gui, "warningMessage").mockResolvedValueOnce(null as any); + await testTree.handleDrop(blockMocks.datasetPdsNode, dataTransfer, undefined); + expect(Gui.warningMessage).toHaveBeenCalledTimes(1); + getDataTransferMock.mockRestore(); + draggedNodeMock[Symbol.dispose](); + }); }); diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts b/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts index a804250d64..d9bb0a6cdd 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts @@ -609,6 +609,7 @@ export class DatasetFSProvider extends BaseProvider implements vscode.FileSystem }); throw err; } + entry.data = content; // Prompt the user with the conflict dialog await this._handleConflict(uri, entry); diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index dfae25ae83..dea709775a 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -204,12 +204,12 @@ export class DatasetTree extends ZoweTreeProvider implemen const node = this.draggedNodes[item.uri.path]; if (SharedContext.isPds(target) || SharedContext.isDsMember(target)) { if (SharedContext.isPds(node) || SharedContext.isDs(node)) { - Gui.errorMessage(vscode.l10n.t("Cannot drop a seq or pds onto a pds")); + Gui.errorMessage(vscode.l10n.t("Cannot drop a sequential dataset or a partitioned dataset onto another PDS.")); return; } } if (SharedContext.isDsMember(node) && SharedContext.isDs(target)) { - Gui.errorMessage(vscode.l10n.t("Cannot drop a member onto a seq dataset")); + Gui.errorMessage(vscode.l10n.t("Cannot drop a member onto a sequential dataset.")); return; } } From 430662092910a3f2a98b73f62dd95102ca869740 Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Mon, 27 Jan 2025 22:41:45 +0530 Subject: [PATCH 06/10] Increase unit test coverage Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- .../trees/dataset/DatasetTree.unit.test.ts | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 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 a6de0aeae5..e811fd495c 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 @@ -55,6 +55,7 @@ import { IconUtils } from "../../../../src/icons/IconUtils"; import { SharedContext } from "../../../../src/trees/shared/SharedContext"; import { ZoweTreeProvider } from "../../../../src/trees/ZoweTreeProvider"; import { TreeViewUtils } from "../../../../src/utils/TreeViewUtils"; +import { error } from "console"; jest.mock("fs"); jest.mock("util"); @@ -3554,7 +3555,12 @@ describe("DataSetTree Unit Tests - Function handleDrop", () => { parentNode: datasetNode, }); datasetSeqNode.contextValue = Constants.DS_DS_CONTEXT; - + const draggedNode = new ZoweDatasetNode({ + label: "seqnode1", + collapsibleState: vscode.TreeItemCollapsibleState.None, + parentNode: datasetSessionNode, + }); + draggedNode.contextValue = Constants.DS_DS_CONTEXT; return { session, imperativeProfile, @@ -3563,6 +3569,7 @@ describe("DataSetTree Unit Tests - Function handleDrop", () => { datasetPdsNode, datasetSeqNode, memberNode, + draggedNode, }; } it("returns early if there are no items in the dataTransfer object", async () => { @@ -3612,10 +3619,10 @@ describe("DataSetTree Unit Tests - Function handleDrop", () => { expect(statusBarMsgSpy).toHaveBeenCalledWith("$(sync~spin) Moving MVS files..."); draggedNodeMock[Symbol.dispose](); getDataTransferMock.mockRestore(); + mvsApiMock.mockRestore(); writeFileMock.mockRestore(); readFileMock.mockRestore(); createDirMock.mockRestore(); - mvsApiMock.mockRestore(); }); it("Dragging a pds onto another pds on different LPAR should throw error", async () => { @@ -3684,4 +3691,44 @@ describe("DataSetTree Unit Tests - Function handleDrop", () => { getDataTransferMock.mockRestore(); draggedNodeMock[Symbol.dispose](); }); + + it("Write File throwing error", async () => { + createGlobalMocks(); + const testTree = new DatasetTree(); + const statusBarMsgSpy = jest.spyOn(Gui, "setStatusBarMessage"); + const blockMocks = createBlockMocks(); + const datasetSession = blockMocks.datasetSessionNode; + datasetSession.children = [blockMocks.datasetPdsNode, blockMocks.datasetSeqNode]; + const dataTransfer = new vscode.DataTransfer(); + const getDataTransferMock = jest.spyOn(dataTransfer, "get").mockReturnValueOnce({ + value: [ + { + label: blockMocks.draggedNode.label as string, + uri: blockMocks.draggedNode.resourceUri, + }, + ], + } as any); + const draggedNodeMock = new MockedProperty(testTree, "draggedNodes", undefined, { + [blockMocks.draggedNode.resourceUri.path]: blockMocks.draggedNode, + }); + const createDataSetMock = jest.fn(); + const createDataSetMemberMock = jest.fn(); + const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({ + createDataSet: createDataSetMock, + createDataSetMember: createDataSetMemberMock, + } as any); + + const createDirMock = jest.spyOn(DatasetFSProvider.instance as any, "createDirectory").mockResolvedValueOnce(undefined); + const readFileMock = jest.spyOn(DatasetFSProvider.instance, "readFile").mockResolvedValue(new Uint8Array([1, 2, 3])); + jest.spyOn(DatasetFSProvider.instance, "writeFile").mockImplementationOnce(() => { + throw Error("Write file error"); + }); + await testTree.handleDrop(blockMocks.datasetSeqNode, dataTransfer, undefined); + expect(statusBarMsgSpy).toHaveBeenCalledWith("$(sync~spin) Moving MVS files..."); + draggedNodeMock[Symbol.dispose](); + getDataTransferMock.mockRestore(); + mvsApiMock.mockRestore(); + readFileMock.mockRestore(); + createDirMock.mockRestore(); + }); }); From 37b8ec65d0ab9f50c00020bfcdaed0b20e7cebf1 Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Mon, 27 Jan 2025 22:48:06 +0530 Subject: [PATCH 07/10] Remove unused import Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- .../__tests__/__unit__/trees/dataset/DatasetTree.unit.test.ts | 1 - 1 file changed, 1 deletion(-) 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 e811fd495c..c897d92797 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 @@ -55,7 +55,6 @@ import { IconUtils } from "../../../../src/icons/IconUtils"; import { SharedContext } from "../../../../src/trees/shared/SharedContext"; import { ZoweTreeProvider } from "../../../../src/trees/ZoweTreeProvider"; import { TreeViewUtils } from "../../../../src/utils/TreeViewUtils"; -import { error } from "console"; jest.mock("fs"); jest.mock("util"); From 26b801b15a18df2e7de9e29d74888468acc64045 Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:42:38 +0530 Subject: [PATCH 08/10] Create shared function Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- .../src/trees/dataset/DatasetTree.ts | 25 ++------------- .../src/trees/shared/SharedUtils.ts | 32 +++++++++++++++++++ .../zowe-explorer/src/trees/uss/USSTree.ts | 25 ++------------- 3 files changed, 36 insertions(+), 46 deletions(-) diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index dea709775a..b4495ce737 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -219,32 +219,11 @@ export class DatasetTree extends ZoweTreeProvider implemen target = target.getParent() as IZoweDatasetTreeNode; } - // If the target path fully contains the path of the dragged node, - // the user is trying to move a parent node into its child - invalid operation - const movedIntoChild = Object.values(this.draggedNodes).some((n) => target.resourceUri.path.startsWith(n.resourceUri.path)); - if (movedIntoChild) { - this.draggedNodes = {}; + const overwrite = await SharedUtils.handleDragAndDropOverwrite(target, this.draggedNodes); + if (overwrite === false) { return; } - // determine if any overwrites may occur - const willOverwrite = Object.values(this.draggedNodes).some((n) => target.children?.find((tc) => tc.label === n.label) != null); - if (willOverwrite) { - const userOpts = [vscode.l10n.t("Confirm")]; - const resp = await Gui.warningMessage( - vscode.l10n.t("One or more items may be overwritten from this drop operation. Confirm or cancel?"), - { - items: userOpts, - vsCodeOpts: { - modal: true, - }, - } - ); - if (resp == null || resp !== userOpts[0]) { - return; - } - } - const movingMsg = Gui.setStatusBarMessage(`$(sync~spin) ${vscode.l10n.t("Moving MVS files...")}`); const parentsToUpdate = new Set(); diff --git a/packages/zowe-explorer/src/trees/shared/SharedUtils.ts b/packages/zowe-explorer/src/trees/shared/SharedUtils.ts index 63c8378870..55270c4f07 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedUtils.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedUtils.ts @@ -357,4 +357,36 @@ export class SharedUtils { timeoutId = setTimeout(() => callback(...args), delay); }; } + + public static async handleDragAndDropOverwrite( + target: IZoweDatasetTreeNode | IZoweUSSTreeNode | undefined, + draggedNodes: Record + ): Promise { + const movedIntoChild = Object.values(draggedNodes).some((n) => target.resourceUri.path.startsWith(n.resourceUri.path)); + if (movedIntoChild) { + draggedNodes = {}; + return false; + } + + // determine if any overwrites may occur + const willOverwrite = Object.values(draggedNodes).some((n) => target.children?.find((tc) => tc.label === n.label) != null); + if (willOverwrite) { + const userOpts = [vscode.l10n.t("Confirm")]; + const resp = await Gui.warningMessage( + vscode.l10n.t("One or more items may be overwritten from this drop operation. Confirm or cancel?"), + { + items: userOpts, + vsCodeOpts: { + modal: true, + }, + } + ); + if (resp == null || resp !== userOpts[0]) { + return false; + } else { + return true; + } + } + return true; + } } diff --git a/packages/zowe-explorer/src/trees/uss/USSTree.ts b/packages/zowe-explorer/src/trees/uss/USSTree.ts index 067c4e6c16..288be69b91 100644 --- a/packages/zowe-explorer/src/trees/uss/USSTree.ts +++ b/packages/zowe-explorer/src/trees/uss/USSTree.ts @@ -182,32 +182,11 @@ export class USSTree extends ZoweTreeProvider implements Types target = target.getParent() as IZoweUSSTreeNode; } - // If the target path fully contains the path of the dragged node, - // the user is trying to move a parent node into its child - invalid operation - const movedIntoChild = Object.values(this.draggedNodes).some((n) => target.resourceUri.path.startsWith(n.resourceUri.path)); - if (movedIntoChild) { - this.draggedNodes = {}; + const overwrite = await SharedUtils.handleDragAndDropOverwrite(target, this.draggedNodes); + if (overwrite === false) { return; } - // determine if any overwrites may occur - const willOverwrite = Object.values(this.draggedNodes).some((n) => target.children?.find((tc) => tc.label === n.label) != null); - if (willOverwrite) { - const userOpts = [vscode.l10n.t("Confirm")]; - const resp = await Gui.warningMessage( - vscode.l10n.t("One or more items may be overwritten from this drop operation. Confirm or cancel?"), - { - items: userOpts, - vsCodeOpts: { - modal: true, - }, - } - ); - if (resp == null || resp !== userOpts[0]) { - return; - } - } - const movingMsg = Gui.setStatusBarMessage(`$(sync~spin) ${vscode.l10n.t("Moving USS files...")}`); const parentsToUpdate = new Set(); From 45fba33e0000ca8883086aa9a3d8d0883622dab8 Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:47:30 +0530 Subject: [PATCH 09/10] Remove unused assignment to variable Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- packages/zowe-explorer/src/trees/shared/SharedUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/zowe-explorer/src/trees/shared/SharedUtils.ts b/packages/zowe-explorer/src/trees/shared/SharedUtils.ts index 55270c4f07..08cfe4faca 100644 --- a/packages/zowe-explorer/src/trees/shared/SharedUtils.ts +++ b/packages/zowe-explorer/src/trees/shared/SharedUtils.ts @@ -364,7 +364,6 @@ export class SharedUtils { ): Promise { const movedIntoChild = Object.values(draggedNodes).some((n) => target.resourceUri.path.startsWith(n.resourceUri.path)); if (movedIntoChild) { - draggedNodes = {}; return false; } From d6718dd00470f267d54f21de659dfb26f7774772 Mon Sep 17 00:00:00 2001 From: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> Date: Tue, 4 Feb 2025 10:07:12 +0530 Subject: [PATCH 10/10] Remove duplicate changelog Signed-off-by: likhithanimma1 <142219673+likhithanimma1@users.noreply.github.com> --- packages/zowe-explorer/CHANGELOG.md | 1 - packages/zowe-explorer/src/trees/dataset/DatasetTree.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 7241efba28..7e341a7352 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -9,7 +9,6 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added Time Started, Time Ended, and Time Submitted job properties to the Jobs table view. [#3055](https://github.com/zowe/zowe-explorer-vscode/issues/3055) - Implemented copy/paste functionality of data sets within and across LPARs. [#3012](https://github.com/zowe/zowe-explorer-vscode/issues/3012) - Added Time Started, Time Ended, and Time Submitted job properties to the Jobs table view. [#3055](https://github.com/zowe/zowe-explorer-vscode/issues/3055) -- Implemented copy/paste functionality of data sets within and across LPARs. [#3012](https://github.com/zowe/zowe-explorer-vscode/issues/3012) - Implemented drag and drop functionality of data sets within and across LPARs. [#3413](https://github.com/zowe/zowe-explorer-vscode/pull/3413) ### Bug fixes diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index b4495ce737..4625be2e25 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -167,7 +167,7 @@ export class DatasetTree extends ZoweTreeProvider implemen try { await DatasetFSProvider.instance.writeFile( destUri.with({ - query: "forceUplaod=true", + query: "forceUpload=true", }), contents, { create: true, overwrite: true }