diff --git a/.github/workflows/merge-by/build-table-and-notify.js b/.github/workflows/merge-by/build-table-and-notify.js index 9e0989eb2a..1e23e9fd53 100644 --- a/.github/workflows/merge-by/build-table-and-notify.js +++ b/.github/workflows/merge-by/build-table-and-notify.js @@ -127,6 +127,15 @@ const notifyUsers = async ({ dayJs, github, owner, pullRequests, repo, today }) }); for (const pr of prsCloseToMergeDate) { + const comments = (await github.rest.issues.listComments({ owner, repo, issue_number: pr.number })).data; + // Try to find the reminder comment + const existingComment = comments?.find((comment) => + comment.user.login === "github-actions[bot]" && comment.body.includes("**Reminder:** This pull request has a merge-by date coming up within the next 24 hours. Please review this PR as soon as possible.")); + + if (existingComment != null) { + continue; + } + // Make a comment on the PR and tag reviewers const body = `**Reminder:** This pull request has a merge-by date coming up within the next 24 hours. Please review this PR as soon as possible.\n\n${pr.reviewers.map((r) => `@${r.login}`).join(" ")}` await github.rest.issues.createComment({ @@ -202,5 +211,7 @@ module.exports = async ({ github, context }) => { // Look over existing PRs, grab all PRs with a merge-by date <= 1w from now, and update the issue with the new table await scanPRsAndUpdateTable({ github, owner, pullRequests, repo }); // Notify users for PRs with merge-by dates coming up within 24hrs from now - await notifyUsers({ dayJs, github, owner, pullRequests, repo, today }); + if (context.eventName === "schedule") { + await notifyUsers({ dayJs, github, owner, pullRequests, repo, today }); + } } \ No newline at end of file diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index b9d7c4c73c..9467ab09d9 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -12,6 +12,9 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen ### Bug fixes +- `DatasetFSProvider.stat()` will now throw a `FileNotFound` error for extenders trying to fetch an MVS resource that does not exist. [#3252](https://github.com/zowe/zowe-explorer-vscode/issues/3252) +- Fixed an issue where renaming or deleting a USS file or data set did not update the opened editor. [#3260](https://github.com/zowe/zowe-explorer-vscode/issues/3260) + ## `3.0.2` ### New features and enhancements diff --git a/packages/zowe-explorer/__tests__/__mocks__/vscode.ts b/packages/zowe-explorer/__tests__/__mocks__/vscode.ts index 8ab63f761c..5c32ad9803 100644 --- a/packages/zowe-explorer/__tests__/__mocks__/vscode.ts +++ b/packages/zowe-explorer/__tests__/__mocks__/vscode.ts @@ -1498,6 +1498,11 @@ export namespace workspace { } } +// We need to do this since "delete" is a reserved keyword and cannot be defined as a function name. +Object.defineProperty(workspace.fs, "delete", { + value: jest.fn(), +}); + export interface InputBoxOptions { placeholder?: string; } diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetActions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetActions.unit.test.ts index dcfa23e152..9352efd1fc 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetActions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetActions.unit.test.ts @@ -66,7 +66,7 @@ function createGlobalMocks() { testFavoritesNode: createDatasetFavoritesNode(), testDatasetTree: null, getContentsSpy: null, - fspDelete: jest.spyOn(DatasetFSProvider.prototype, "delete").mockImplementation(), + fspDelete: jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(), statusBarMsgSpy: null, mvsApi: null, mockShowWarningMessage: jest.fn(), @@ -96,7 +96,6 @@ function createGlobalMocks() { value: newMocks.mockShowWarningMessage, configurable: true, }); - Object.defineProperty(vscode.workspace.fs, "delete", { value: jest.fn(), configurable: true }); Object.defineProperty(vscode.window, "showInputBox", { value: jest.fn(), configurable: true }); Object.defineProperty(vscode.workspace, "openTextDocument", { value: jest.fn(), configurable: true }); Object.defineProperty(vscode.workspace, "getConfiguration", { value: jest.fn(), configurable: true }); @@ -487,7 +486,7 @@ describe("Dataset Actions Unit Tests - Function deleteDatasetPrompt", () => { blockMocks.testDatasetTree.getTreeView.mockReturnValueOnce(treeView); globalMocks.mockShowWarningMessage.mockResolvedValueOnce("Delete"); - jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation(); + jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(); await DatasetActions.deleteDatasetPrompt(blockMocks.testDatasetTree); expect(mocked(Gui.showMessage)).toHaveBeenCalledWith( @@ -721,7 +720,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { }); mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any); - const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation(); + const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(); await DatasetActions.deleteDataset(node, blockMocks.testDatasetTree); expect(deleteSpy).toHaveBeenCalledWith(node.resourceUri, { recursive: false }); }); @@ -737,7 +736,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { }); mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any); - const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation(); + const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(); await DatasetActions.deleteDataset(node, blockMocks.testDatasetTree); expect(deleteSpy).toHaveBeenCalledWith(node.resourceUri, { recursive: false }); }); @@ -753,7 +752,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { }); mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any); - jest.spyOn(DatasetFSProvider.instance, "delete").mockRejectedValueOnce(Error("not found")); + jest.spyOn(vscode.workspace.fs, "delete").mockRejectedValueOnce(Error("not found")); await expect(DatasetActions.deleteDataset(node, blockMocks.testDatasetTree)).rejects.toThrow("not found"); expect(mocked(Gui.showMessage)).toHaveBeenCalledWith("Unable to find file " + node.label?.toString()); }); @@ -769,7 +768,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { }); mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any); - jest.spyOn(DatasetFSProvider.instance, "delete").mockRejectedValueOnce(Error("")); + jest.spyOn(vscode.workspace.fs, "delete").mockRejectedValueOnce(Error("")); await expect(DatasetActions.deleteDataset(node, blockMocks.testDatasetTree)).rejects.toThrow(""); expect(mocked(Gui.errorMessage)).toHaveBeenCalledWith("Error"); }); @@ -792,7 +791,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { node.contextValue = Constants.DS_PDS_CONTEXT + Constants.FAV_SUFFIX; mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any); - const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete"); + const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete"); await DatasetActions.deleteDataset(node, blockMocks.testDatasetTree); @@ -812,7 +811,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { const child = new ZoweDatasetNode({ label: "child", collapsibleState: vscode.TreeItemCollapsibleState.None, parentNode: parent }); mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any); - const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation(); + const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(); await DatasetActions.deleteDataset(child, blockMocks.testDatasetTree); expect(deleteSpy).toHaveBeenCalledWith(child.resourceUri, { recursive: false }); @@ -841,7 +840,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { blockMocks.testDatasetTree.mFavorites[0].children.push(child); mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any); - const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation(); + const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(); await DatasetActions.deleteDataset(child, blockMocks.testDatasetTree); expect(deleteSpy).toHaveBeenCalledWith(child.resourceUri, { recursive: false }); expect(blockMocks.testDatasetTree.removeFavorite).toHaveBeenCalledWith(child); @@ -864,7 +863,7 @@ describe("Dataset Actions Unit Tests - Function deleteDataset", () => { }); mocked(vscode.window.showQuickPick).mockResolvedValueOnce("Delete" as any); - const deleteSpy = jest.spyOn(DatasetFSProvider.instance, "delete").mockImplementation(); + const deleteSpy = jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(); deleteSpy.mockClear(); await expect(DatasetActions.deleteDataset(child, blockMocks.testDatasetTree)).rejects.toThrow("Cannot delete, item invalid."); expect(deleteSpy).not.toHaveBeenCalled(); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetFSProvider.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetFSProvider.unit.test.ts index 2af16a7253..a48da93bb7 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetFSProvider.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/dataset/DatasetFSProvider.unit.test.ts @@ -734,6 +734,33 @@ describe("fetchEntriesForDataset", () => { describe("fetchDataset", () => { describe("calls dataSet to verify that the data set exists on the mainframe", () => { describe("PS", () => { + it("non-existent PS URI", async () => { + const dataSetMock = jest.fn().mockResolvedValue({ + success: true, + apiResponse: { + items: [], + }, + commandResponse: "", + }); + const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({ + dataSet: dataSetMock, + } as any); + try { + await (DatasetFSProvider.instance as any).fetchDataset(testUris.ps, { + isRoot: false, + slashAfterProfilePos: testUris.ps.path.indexOf("/", 1), + profileName: "sestest", + profile: testProfile, + }); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toBe(testUris.ps.toString(true)); + } + expect(dataSetMock).toHaveBeenCalled(); + mvsApiMock.mockRestore(); + }); + it("non-existent URI", async () => { const dataSetMock = jest.fn().mockResolvedValue({ success: true, @@ -747,7 +774,7 @@ describe("fetchDataset", () => { } as any); await (DatasetFSProvider.instance as any).fetchDataset(testUris.ps, { isRoot: false, - slashAfterProfilePos: testUris.pds.path.indexOf("/", 1), + slashAfterProfilePos: testUris.ps.path.indexOf("/", 1), profileName: "sestest", profile: testProfile, }); @@ -772,6 +799,36 @@ describe("fetchDataset", () => { }); describe("PDS", () => { + it("non-existent PDS URI", async () => { + const dataSetMock = jest.fn().mockResolvedValue({ + success: true, + apiResponse: { + items: [], + }, + commandResponse: "", + }); + const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({ + dataSet: dataSetMock, + } as any); + const lookupMock = jest.spyOn(DatasetFSProvider.instance as any, "lookup"); + lookupMock.mockImplementation(() => { + throw FileSystemError.FileNotFound(testUris.pds); + }); + try { + await (DatasetFSProvider.instance as any).fetchDataset(testUris.pds, { + isRoot: false, + slashAfterProfilePos: testUris.pds.path.indexOf("/", 1), + profileName: "sestest", + profile: testProfile, + }); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toBe(testUris.pds.toString(true)); + } + lookupMock.mockRestore(); + mvsApiMock.mockRestore(); + }); it("non-existent URI", async () => { const dataSetMock = jest.fn().mockResolvedValue({ success: true, @@ -807,6 +864,63 @@ describe("fetchDataset", () => { fetchEntriesForDatasetMock.mockRestore(); }); }); + + describe("PDS member", () => { + it("non-existent member URI", async () => { + const allMembersMockNoMatch = jest.fn().mockResolvedValue({ + success: true, + apiResponse: { + items: [ + { + member: "NOMATCH", + }, + ], + }, + commandResponse: "", + }); + const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({ + allMembers: allMembersMockNoMatch, + } as any); + try { + await (DatasetFSProvider.instance as any).fetchDataset(testUris.pdsMember, { + isRoot: false, + slashAfterProfilePos: testUris.pds.path.indexOf("/", 1), + profileName: "sestest", + profile: testProfile, + }); + // Fail test if above expression doesn't throw anything. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toBe(testUris.pdsMember.toString(true)); + } + expect(allMembersMockNoMatch).toHaveBeenCalledWith("USER.DATA.PDS"); + mvsApiMock.mockRestore(); + }); + it("existing member URI", async () => { + const allMembersMock = jest.fn().mockResolvedValue({ + success: true, + apiResponse: { + items: [ + { + member: "MEMBER1", + }, + ], + }, + commandResponse: "", + }); + const mvsApiMock = jest.spyOn(ZoweExplorerApiRegister, "getMvsApi").mockReturnValue({ + allMembers: allMembersMock, + } as any); + await (DatasetFSProvider.instance as any).fetchDataset(testUris.pdsMember, { + isRoot: false, + slashAfterProfilePos: testUris.pdsMember.path.indexOf("/", 1), + profileName: "sestest", + profile: testProfile, + }); + expect(allMembersMock).toHaveBeenCalledWith("USER.DATA.PDS"); + mvsApiMock.mockRestore(); + }); + }); }); }); 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 e217c18606..a9610200ee 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 @@ -2234,7 +2234,7 @@ describe("Dataset Tree Unit Tests - Function rename", () => { mvsApi, profileInstance, mockCheckCurrentProfile, - rename: jest.spyOn(DatasetFSProvider.instance, "rename").mockImplementation(), + rename: jest.spyOn(vscode.workspace.fs, "rename").mockImplementation(), }; } @@ -2481,7 +2481,7 @@ describe("Dataset Tree Unit Tests - Function rename", () => { favProfileNode.children.push(favParent); testTree.mFavorites.push(favProfileNode); const renameDataSetMemberSpy = jest.spyOn((DatasetTree as any).prototype, "renameDataSetMember"); - const renameMock = jest.spyOn(DatasetFSProvider.instance, "rename").mockImplementation(); + const renameMock = jest.spyOn(vscode.workspace.fs, "rename").mockImplementation(); await testTree.rename(child); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/job/JobFSProvider.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/job/JobFSProvider.unit.test.ts index 4a8a588976..641b38c44c 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/job/JobFSProvider.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/job/JobFSProvider.unit.test.ts @@ -321,7 +321,7 @@ describe("delete", () => { const lookupParentDirMock = jest .spyOn(JobFSProvider.instance as any, "_lookupParentDirectory") .mockReturnValueOnce({ ...testEntries.session }); - await JobFSProvider.instance.delete(testUris.job, { recursive: true, deleteRemote: true }); + await JobFSProvider.instance.delete(testUris.job, { recursive: true }); const jobInfo = testEntries.job.job; expect(jobInfo).not.toBeUndefined(); expect(mockUssApi.deleteJob).toHaveBeenCalledWith(jobInfo?.jobname || "TESTJOB", jobInfo?.jobid || "JOB12345"); @@ -341,7 +341,7 @@ describe("delete", () => { fakeJob.job = testEntries.job.job; const lookupMock = jest.spyOn(JobFSProvider.instance as any, "lookup").mockReturnValueOnce(fakeSpool); const lookupParentDirMock = jest.spyOn(JobFSProvider.instance as any, "_lookupParentDirectory").mockReturnValueOnce(fakeJob); - await JobFSProvider.instance.delete(testUris.spool, { recursive: true, deleteRemote: true }); + await JobFSProvider.instance.delete(testUris.spool, { recursive: true }); expect(mockUssApi.deleteJob).not.toHaveBeenCalled(); expect(lookupParentDirMock).not.toHaveBeenCalled(); ussApiMock.mockRestore(); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/job/JobTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/job/JobTree.unit.test.ts index 5e573cdbec..3cbd4eba23 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/job/JobTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/job/JobTree.unit.test.ts @@ -135,7 +135,7 @@ async function createGlobalMocks() { }; jest.spyOn(JobFSProvider.instance, "createDirectory").mockImplementation(globalMocks.FileSystemProvider.createDirectory); - jest.spyOn(JobFSProvider.instance, "delete").mockImplementation(globalMocks.FileSystemProvider.delete); + jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(globalMocks.FileSystemProvider.delete); jest.spyOn(Gui, "createTreeView").mockImplementation(globalMocks.createTreeView); Object.defineProperty(ProfilesCache, "getConfigInstance", { value: jest.fn(() => { diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/uss/USSTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/uss/USSTree.unit.test.ts index 1f03687097..e3709ba0aa 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/uss/USSTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/uss/USSTree.unit.test.ts @@ -104,7 +104,7 @@ function createGlobalMocks() { }; jest.spyOn(UssFSProvider.instance, "createDirectory").mockImplementation(globalMocks.FileSystemProvider.createDirectory); - jest.spyOn(UssFSProvider.instance, "rename").mockImplementation(globalMocks.FileSystemProvider.rename); + jest.spyOn(vscode.workspace.fs, "rename").mockImplementation(globalMocks.FileSystemProvider.rename); globalMocks.mockTextDocuments.push(globalMocks.mockTextDocumentDirty); globalMocks.mockTextDocuments.push(globalMocks.mockTextDocumentClean); @@ -1690,7 +1690,7 @@ describe("USSTree Unit Tests - Function crossLparMove", () => { ]; ussDirNode.dirty = false; - const deleteMock = jest.spyOn(UssFSProvider.instance, "delete").mockResolvedValue(undefined); + const deleteMock = jest.spyOn(vscode.workspace.fs, "delete").mockResolvedValue(undefined); const readFileMock = jest.spyOn(UssFSProvider.instance, "readFile").mockResolvedValue(new Uint8Array([1, 2, 3])); const writeFileMock = jest.spyOn(UssFSProvider.instance, "writeFile").mockResolvedValue(undefined); const existsMock = jest.spyOn(UssFSProvider.instance, "exists").mockReturnValueOnce(false); diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts index 5d00e1ac6e..56f052c5b2 100644 --- a/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/trees/uss/ZoweUSSNode.unit.test.ts @@ -430,7 +430,7 @@ describe("ZoweUSSNode Unit Tests - Function node.rename()", () => { uss: { addSingleSession: jest.fn(), mSessionNodes: [], refresh: jest.fn() } as any, job: { addSingleSession: jest.fn(), mSessionNodes: [], refresh: jest.fn() } as any, }), - renameSpy: jest.spyOn(UssFSProvider.instance, "rename").mockImplementation(), + renameSpy: jest.spyOn(vscode.workspace.fs, "rename").mockImplementation(), getEncodingForFile: jest.spyOn(UssFSProvider.instance as any, "getEncodingForFile").mockReturnValue(undefined), }; newMocks.ussDir.contextValue = Constants.USS_DIR_CONTEXT; @@ -443,7 +443,7 @@ describe("ZoweUSSNode Unit Tests - Function node.rename()", () => { const newFullPath = "/u/user/newName"; const errMessageMock = jest.spyOn(Gui, "errorMessage").mockImplementation(); - const renameMock = jest.spyOn(UssFSProvider.instance, "rename").mockRejectedValueOnce(new Error("Rename error: file is busy")); + const renameMock = jest.spyOn(vscode.workspace.fs, "rename").mockRejectedValueOnce(new Error("Rename error: file is busy")); await blockMocks.ussDir.rename(newFullPath); expect(errMessageMock).toHaveBeenCalledWith("Rename error: file is busy"); @@ -631,7 +631,7 @@ describe("ZoweUSSNode Unit Tests - Function node.deleteUSSNode()", () => { session: globalMocks.session, profile: globalMocks.profileOne, }), - fspDelete: jest.spyOn(UssFSProvider.instance, "delete").mockImplementation(), + fspDelete: jest.spyOn(vscode.workspace.fs, "delete").mockImplementation(), }; newMocks.ussNode = new ZoweUSSNode({ @@ -679,7 +679,7 @@ describe("ZoweUSSNode Unit Tests - Function node.deleteUSSNode()", () => { const globalMocks = createGlobalMocks(); const blockMocks = createBlockMocks(globalMocks); globalMocks.mockShowWarningMessage.mockResolvedValueOnce("Delete"); - jest.spyOn(UssFSProvider.instance, "delete").mockImplementationOnce(() => { + jest.spyOn(vscode.workspace.fs, "delete").mockImplementationOnce(() => { throw Error("testError"); }); diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index 2819f84d0f..ed530c1f8b 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -1774,7 +1774,7 @@ ] }, "scripts": { - "build": "pnpm copy-secrets && pnpm clean:bundle && pnpm license && pnpm build:nls && webpack --mode development && pnpm madge", + "build": "pnpm copy-secrets && pnpm clean:bundle && pnpm license && pnpm build:nls && webpack --mode development && pnpm madge", "test": "pnpm test:unit", "test:e2e": "cd __tests__/__e2e__/ && wdio run ./wdio.conf.ts", "test:integration": "cd __tests__/__integration__/bdd && wdio run ./wdio.conf.ts", @@ -1801,9 +1801,8 @@ "pretty": "prettier --write .", "generateLocalization": "pnpm dlx @vscode/l10n-dev export --o ./l10n ./src && node ./scripts/generatePoeditorJson.js", "copy-secrets": "node ./scripts/getSecretsPrebuilds.js", - "build:nlsExtract": "pnpm dlx @vscode/l10n-dev export --outDir ./l10n ./src", "build:nlsPseudo": "pnpm dlx @vscode/l10n-dev generate-pseudo -o ./l10n/ ./l10n/bundle.l10n.json ./package.nls.json", - "build:nls": "pnpm run build:nlsExtract && pnpm run build:nlsPseudo" + "build:nls": "pnpm run build:nlsPseudo" }, "engines": { "vscode": "^1.79.0" diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetActions.ts b/packages/zowe-explorer/src/trees/dataset/DatasetActions.ts index 6b469cd61e..647ea46ce8 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetActions.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetActions.ts @@ -1171,7 +1171,7 @@ export class DatasetActions { } await datasetProvider.checkCurrentProfile(node); if (Profiles.getInstance().validProfile !== Validation.ValidationType.INVALID) { - await DatasetFSProvider.instance.delete(node.resourceUri, { recursive: false }); + await vscode.workspace.fs.delete(node.resourceUri, { recursive: false }); } else { return; } diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts b/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts index fc9512b336..a853471adf 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetFSProvider.ts @@ -203,17 +203,28 @@ export class DatasetFSProvider extends BaseProvider implements vscode.FileSystem const uriPath = uri.path.substring(uriInfo.slashAfterProfilePos + 1).split("/"); const pdsMember = uriPath.length === 2; if (!entryExists) { - const resp = await ZoweExplorerApiRegister.getMvsApi(uriInfo.profile).dataSet(entryIsDir ? uriPath[0] : path.parse(uriPath[0]).name, { - attributes: true, - }); - if (resp.success) { - const dsorg: string = resp.apiResponse?.items?.[0]?.dsorg; - entryIsDir = pdsMember ? false : dsorg?.startsWith("PO") ?? false; + if (pdsMember) { + const resp = await ZoweExplorerApiRegister.getMvsApi(uriInfo.profile).allMembers(uriPath[0]); + entryIsDir = false; + const memberName = path.parse(uriPath[1]).name; + if ( + !resp.success || + resp.apiResponse?.items?.length < 1 || + !resp.apiResponse.items.find((respItem) => respItem.member === memberName) + ) { + throw vscode.FileSystemError.FileNotFound(uri); + } } else { - throw vscode.FileSystemError.FileNotFound(uri); + const resp = await ZoweExplorerApiRegister.getMvsApi(uriInfo.profile).dataSet(uriPath[0], { + attributes: true, + }); + if (resp.success && resp.apiResponse?.items?.length > 0) { + entryIsDir = resp.apiResponse.items[0].dsorg?.startsWith("PO"); + } else { + throw vscode.FileSystemError.FileNotFound(uri); + } } } - if (entryIsDir) { if (!entryExists) { this.createDirectory(uri); diff --git a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts index 2369002022..5e18da8f31 100644 --- a/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/trees/dataset/DatasetTree.ts @@ -1167,7 +1167,7 @@ export class DatasetTree extends ZoweTreeProvider implemen const newUri = node.resourceUri.with({ path: path.posix.join(path.posix.dirname(node.resourceUri.path), afterMemberName), }); - await DatasetFSProvider.instance.rename(node.resourceUri, newUri, { overwrite: false }); + await vscode.workspace.fs.rename(node.resourceUri, newUri, { overwrite: false }); node.resourceUri = newUri; node.label = afterMemberName; node.tooltip = afterMemberName; @@ -1222,7 +1222,7 @@ export class DatasetTree extends ZoweTreeProvider implemen const newUri = node.resourceUri.with({ path: path.posix.join(path.posix.dirname(node.resourceUri.path), afterDataSetName), }); - await DatasetFSProvider.instance.rename(node.resourceUri, newUri, { overwrite: false }); + await vscode.workspace.fs.rename(node.resourceUri, newUri, { overwrite: false }); // Rename corresponding node in Sessions or Favorites section (whichever one Rename wasn't called from) if (SharedContext.isFavorite(node)) { diff --git a/packages/zowe-explorer/src/trees/job/JobFSProvider.ts b/packages/zowe-explorer/src/trees/job/JobFSProvider.ts index cb998c0924..fc5dfaf2d6 100644 --- a/packages/zowe-explorer/src/trees/job/JobFSProvider.ts +++ b/packages/zowe-explorer/src/trees/job/JobFSProvider.ts @@ -281,7 +281,7 @@ export class JobFSProvider extends BaseProvider implements vscode.FileSystemProv * @param options Options for deleting the spool file or job * - `deleteRemote` - Deletes the job from the remote system if set to true. */ - public async delete(uri: vscode.Uri, options: { readonly recursive: boolean; readonly deleteRemote: boolean }): Promise { + public async delete(uri: vscode.Uri, options: { readonly recursive: boolean }): Promise { const entry = this.lookup(uri, false); const isJob = FsJobsUtils.isJobEntry(entry); if (!isJob) { @@ -291,10 +291,7 @@ export class JobFSProvider extends BaseProvider implements vscode.FileSystemProv const parent = this._lookupParentDirectory(uri, false); const profInfo = FsAbstractUtils.getInfoForUri(uri, Profiles.getInstance()); - - if (options.deleteRemote) { - await ZoweExplorerApiRegister.getJesApi(profInfo.profile).deleteJob(entry.job.jobname, entry.job.jobid); - } + await ZoweExplorerApiRegister.getJesApi(profInfo.profile).deleteJob(entry.job.jobname, entry.job.jobid); parent.entries.delete(entry.name); this._fireSoon({ type: vscode.FileChangeType.Deleted, uri }); } diff --git a/packages/zowe-explorer/src/trees/job/JobTree.ts b/packages/zowe-explorer/src/trees/job/JobTree.ts index 3fa17e221f..8a950c2ffa 100644 --- a/packages/zowe-explorer/src/trees/job/JobTree.ts +++ b/packages/zowe-explorer/src/trees/job/JobTree.ts @@ -218,7 +218,7 @@ export class JobTree extends ZoweTreeProvider implements Types public async delete(node: IZoweJobTreeNode): Promise { ZoweLogger.trace("JobTree.delete called."); - await JobFSProvider.instance.delete(node.resourceUri, { recursive: false, deleteRemote: true }); + await vscode.workspace.fs.delete(node.resourceUri, { recursive: false }); const favNode = this.relabelFavoritedJob(node); favNode.contextValue = SharedContext.asFavorite(favNode); await this.removeFavorite(favNode); diff --git a/packages/zowe-explorer/src/trees/uss/USSTree.ts b/packages/zowe-explorer/src/trees/uss/USSTree.ts index 3e574829fc..cc741e35a1 100644 --- a/packages/zowe-explorer/src/trees/uss/USSTree.ts +++ b/packages/zowe-explorer/src/trees/uss/USSTree.ts @@ -126,7 +126,7 @@ export class USSTree extends ZoweTreeProvider implements Types true ); } - await UssFSProvider.instance.delete(sourceUri, { recursive: true }); + await vscode.workspace.fs.delete(sourceUri, { recursive: true }); } else { // create a file on the remote system for writing try { @@ -160,7 +160,7 @@ export class USSTree extends ZoweTreeProvider implements Types if (!recursiveCall) { // Delete any files from the selection on the source LPAR - await UssFSProvider.instance.delete(sourceNode.resourceUri, { recursive: false }); + await vscode.workspace.fs.delete(sourceNode.resourceUri, { recursive: false }); } } } diff --git a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts index f7f7951131..364532e9a5 100644 --- a/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts +++ b/packages/zowe-explorer/src/trees/uss/ZoweUSSNode.ts @@ -364,7 +364,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { }); try { - await UssFSProvider.instance.rename(oldUri, newUri, { overwrite: false }); + await vscode.workspace.fs.rename(oldUri, newUri, { overwrite: false }); } catch (err) { Gui.errorMessage(err.message); return; @@ -416,7 +416,7 @@ export class ZoweUSSNode extends ZoweTreeNode implements IZoweUSSTreeNode { return; } try { - await UssFSProvider.instance.delete(this.resourceUri, { recursive: this.isFolder }); + await vscode.workspace.fs.delete(this.resourceUri, { recursive: this.isFolder }); } catch (err) { ZoweLogger.error(err); if (err instanceof Error) { diff --git a/packages/zowe-explorer/src/webviews/src/edit-history/components/PersistentUtils.ts b/packages/zowe-explorer/src/webviews/src/edit-history/components/PersistentUtils.ts index 4e2daa69f9..7e5880c439 100644 --- a/packages/zowe-explorer/src/webviews/src/edit-history/components/PersistentUtils.ts +++ b/packages/zowe-explorer/src/webviews/src/edit-history/components/PersistentUtils.ts @@ -12,14 +12,13 @@ import { createContext } from "preact"; import { DataPanelContextType } from "../types"; import { useContext } from "preact/hooks"; -import * as l10n from "@vscode/l10n"; export const DataPanelContext = createContext(null); export function useDataPanelContext(): DataPanelContextType { const dataPanelContext = useContext(DataPanelContext); if (!dataPanelContext) { - throw new Error(l10n.t("DataPanelContext has to be used within ")); + throw new Error("DataPanelContext has to be used within "); } return dataPanelContext; } diff --git a/packages/zowe-explorer/src/webviews/src/table-view/ActionsBar.tsx b/packages/zowe-explorer/src/webviews/src/table-view/ActionsBar.tsx index 6edcf8c6a0..dcf604dc61 100644 --- a/packages/zowe-explorer/src/webviews/src/table-view/ActionsBar.tsx +++ b/packages/zowe-explorer/src/webviews/src/table-view/ActionsBar.tsx @@ -81,8 +81,8 @@ export const ActionsBar = (props: ActionsProps) => {

- {props.selectionCount === 0 ? l10n.t("No") : props.selectionCount} {l10n.t("item")} - {props.selectionCount > 1 || props.selectionCount === 0 ? l10n.t("s") : ""} {l10n.t("selected")} + {props.selectionCount === 0 ? l10n.t("No") : props.selectionCount} + {props.selectionCount > 1 || props.selectionCount === 0 ? l10n.t("items") : l10n.t("item")} {l10n.t("selected")}

{props.actions .filter((action) => (props.itemCount > 1 ? action.callback.typ === "multi-row" : action.callback.typ.endsWith("row")))