From e7da1d0e5d404db6c5bfedf667c60198cc1c6330 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Sat, 23 Sep 2023 00:25:50 +0530 Subject: [PATCH 01/47] updated with local filtering of jobs Signed-off-by: SanthoshiBoyina --- .../__tests__/__unit__/extension.unit.test.ts | 2 + .../__unit__/job/actions.unit.test.ts | 197 ++++++++++++++++++ packages/zowe-explorer/package.json | 24 +++ .../resources/dark/filter-dark.svg | 12 ++ .../resources/light/filter-light.svg | 12 ++ packages/zowe-explorer/src/globals.ts | 2 +- packages/zowe-explorer/src/job/actions.ts | 74 ++++++- packages/zowe-explorer/src/job/init.ts | 18 +- 8 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 packages/zowe-explorer/resources/dark/filter-dark.svg create mode 100644 packages/zowe-explorer/resources/light/filter-light.svg diff --git a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts index 5f111c1d64..0037c3e2d7 100644 --- a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts @@ -241,6 +241,8 @@ async function createGlobalMocks() { "zowe.jobs.sortbyname", "zowe.jobs.sortbyid", "zowe.jobs.sortbyreturncode", + "zowe.jobs.filterJobs", + "zowe.jobs.filterSpools", "zowe.manualPoll", "zowe.updateSecureCredentials", "zowe.promptCredentials", diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 85737a5c89..d49923cca7 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -45,6 +45,49 @@ import { ZosJobsProvider } from "../../../src/job/ZosJobsProvider"; const activeTextEditorDocument = jest.fn(); +jest.mock("vscode"); + +const showMock = jest.fn(); + +const onDidChangeValueMock = { + event: (callback: (value: string) => void): vscode.Disposable => { + const disposable = { + dispose: jest.fn(), + }; + callback(""); + return disposable; + }, +}; + +const mockInputBox: vscode.InputBox = { + title: "", + value: "", + placeholder: "", + password: false, + onDidChangeValue: onDidChangeValueMock.event, + onDidAccept: jest.fn(), + show: showMock, + hide: jest.fn(), + dispose: jest.fn(), + buttons: [], + onDidTriggerButton: jest.fn(), + prompt: "", + validationMessage: "", + step: 1, + totalSteps: 100, + enabled: true, + busy: false, + ignoreFocusOut: false, + onDidHide: jest.fn(), +}; + +function setJobObjects(job: zowe.IJob, newJobName: string, newJobId: string, newRetCode: string) { + job.jobname = newJobName; + job.jobid = newJobId; + job.retcode = newRetCode; + return job; +} + function createGlobalMocks() { Object.defineProperty(vscode.workspace, "getConfiguration", { value: jest.fn().mockImplementation(() => new Map([["zowe.jobs.confirmSubmission", false]])), @@ -86,6 +129,12 @@ function createGlobalMocks() { Object.defineProperty(ZoweLogger, "error", { value: jest.fn(), configurable: true }); Object.defineProperty(ZoweLogger, "debug", { value: jest.fn(), configurable: true }); Object.defineProperty(ZoweLogger, "trace", { value: jest.fn(), configurable: true }); + Object.defineProperty(vscode.window, "createInputBox", { + value: jest.fn(() => mockInputBox), + configurable: true, + }); + + Object.defineProperty(vscode.window, "showInformationMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(vscode.window, "showInformationMessage", { value: jest.fn(), configurable: true }); function settingJobObjects(job: zowe.IJob, setjobname: string, setjobid: string, setjobreturncode: string): zowe.IJob { job.jobname = setjobname; @@ -1401,3 +1450,151 @@ describe("sortjobsby function", () => { expect(sortbyretcodespy.mock.calls[0][0].children).toStrictEqual(expected.mSessionNodes[0].children); }); }); + +describe("Job Actions Unit Tests - Filter Jobs", () => { + const node1 = new Job( + "jobnew", + vscode.TreeItemCollapsibleState.None, + null, + null, + setJobObjects(createIJobObject(), "BOYINAA", "JOB04945", "CC 0000"), + null + ); + const node2 = new Job( + "jobnew", + vscode.TreeItemCollapsibleState.None, + null, + null, + setJobObjects(createIJobObject(), "BOYINAA", "JOB05037", "CC 0000"), + null + ); + + it("To show showInformationMessage", async () => { + const testTree = new ZosJobsProvider(); + testTree.mSessionNodes[0].label = "zosmf"; + testTree.mSessionNodes[0].collapsibleState = 1; + + await jobActions.filterJobs(testTree); + + expect(mocked(vscode.window.showInformationMessage)).toHaveBeenCalled(); + }); + + it("To filter jobs based on a combination of JobName, JobId and Return code", async () => { + const testTree = new ZosJobsProvider(); + testTree.mSessionNodes[0].label = "zosmf"; + testTree.mSessionNodes[0].collapsibleState = 2; + testTree.mSessionNodes[0].children = [node1, node2]; + + const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); + mockInputBox.value = "BOYINAA(JOB04945) - CC 0000"; + createInputBoxSpy.mockReturnValue(mockInputBox); + const filterJobsSpy = jest.spyOn(jobActions, "filterJobs"); + await jobActions.filterJobs(testTree); + testTree.mSessionNodes[0].children = [node1]; + + expect(createInputBoxSpy).toHaveBeenCalled(); + expect(filterJobsSpy).toHaveBeenCalled(); + expect(filterJobsSpy).toBeCalledWith(testTree); + expect(filterJobsSpy.mock.calls[0][0].mSessionNodes[0].children).toBe(testTree.mSessionNodes[0].children); + }); +}); + +describe("Job Actions Unit Tests - Filter Spools", () => { + function createBlockMocks() { + const session = createISession(); + const treeView = createTreeView(); + const iJob = createIJobObject(); + const imperativeProfile = createIProfile(); + const testJobTree = createJobsTree(session, iJob, imperativeProfile, treeView); + + return { + session, + treeView, + iJob, + imperativeProfile, + testJobTree, + testJobsTree: createJobsTree(session, iJob, imperativeProfile, treeView), + }; + } + const blockMocks = createBlockMocks(); + + function setSpoolObjects(spool: zowe.IJobFile, newStepName: string, newDdName: string, newRecordCount: number) { + spool.stepname = newStepName; + spool.ddname = newDdName; + spool["record-count"] = newRecordCount; + return spool; + } + + const node2 = new Job( + "jobnew", + vscode.TreeItemCollapsibleState.None, + null, + null, + setJobObjects(createIJobObject(), "BOYINAA", "JOB05037", "CC 0000"), + null + ); + + const node1 = new Job( + "jobnew", + vscode.TreeItemCollapsibleState.None, + node2, + blockMocks.session, + setJobObjects(createIJobObject(), "BOYINAA", "JOB04945", "CC 0000"), + createIProfile() + ); + + const spoolNode1 = new Spool( + "JES2:JESMSGLG - 21", + vscode.TreeItemCollapsibleState.None, + node1, + null, + setSpoolObjects(createIJobFile(), "JES2", "JESMSGLG", 21), + createIJobObject(), + node1 + ); + const spoolNode2 = new Spool( + "DELETE:SYSPRINT - 7", + vscode.TreeItemCollapsibleState.None, + node1, + null, + setSpoolObjects(createIJobFile(), "DELETE", "SYSPRINT", 7), + createIJobObject(), + node1 + ); + + it("To check a job is expanded or not when filter spools option is choosen", async () => { + const testTree = new ZosJobsProvider(); + node1.collapsibleState = 1; + const filterSpoolsSpy = jest.spyOn(jobActions, "filterSpools"); + const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); + mockInputBox.value = "DELETE:SYSPRINT - 7"; + createInputBoxSpy.mockReturnValue(mockInputBox); + + await jobActions.filterSpools(testTree, node1, blockMocks.testJobTree); + + expect(createInputBoxSpy).toHaveBeenCalled(); + expect(filterSpoolsSpy).toHaveBeenCalled(); + expect(filterSpoolsSpy).toBeCalledWith(testTree, node1, blockMocks.testJobTree); + expect(filterSpoolsSpy.mock.calls[0][1].collapsibleState).toBe(vscode.TreeItemCollapsibleState.Expanded); + }); + + it("To filter spools based on a combination of stepname, ddname and record-count", async () => { + const testTree = new ZosJobsProvider(); + node1.collapsibleState = 1; + node1.children = [spoolNode1, spoolNode2]; + testTree.mSessionNodes[0].children = [node1]; + const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); + mockInputBox.value = "JES2:JESMSGLG - 21"; + createInputBoxSpy.mockReturnValue(mockInputBox); + const filterSpoolsSpy = jest.spyOn(jobActions, "filterSpools"); + + await jobActions.filterSpools(testTree, node1, blockMocks.testJobTree); + node1.children = [spoolNode1]; + testTree.mSessionNodes[0].children = [node1]; + + expect(createInputBoxSpy).toHaveBeenCalled(); + expect(filterSpoolsSpy).toHaveBeenCalled(); + expect(filterSpoolsSpy).toBeCalledWith(testTree, node1, blockMocks.testJobTree); + expect(filterSpoolsSpy.mock.calls[0][0].mSessionNodes[0].children).toBe(testTree.mSessionNodes[0].children); + }); +}); diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index c602c3c2d8..f16908824a 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -749,6 +749,20 @@ "title": "%cancelJobs%", "category": "Zowe Explorer" }, + { + "command": "zowe.jobs.filterJobs", + "title": "Filter Jobs", + "category": "Zowe Explorer", + "icon": { + "light": "./resources/light/filter-light.svg", + "dark": "./resources/dark/filter-dark.svg" + } + }, + { + "command": "zowe.jobs.filterSpools", + "title": "Filter Spools", + "category": "Zowe Explorer" + }, { "command": "zowe.jobs.search", "title": "%jobs.search%", @@ -1163,6 +1177,11 @@ "command": "zowe.jobs.removeSearchFavorite", "group": "inline" }, + { + "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", + "command": "zowe.jobs.filterJobs", + "group": "inline" + }, { "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", "command": "zowe.jobs.search", @@ -1218,6 +1237,11 @@ "command": "zowe.jobs.addFavorite", "group": "002_zowe_jobsWorkspace@0" }, + { + "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", + "command": "zowe.jobs.filterJobs", + "group": "002_zowe_jobsProfileModification@99" + }, { "when": "view == zowe.jobs.explorer && viewItem =~ /^job.*_fav.*/", "command": "zowe.jobs.removeFavorite", diff --git a/packages/zowe-explorer/resources/dark/filter-dark.svg b/packages/zowe-explorer/resources/dark/filter-dark.svg new file mode 100644 index 0000000000..cdba9f4963 --- /dev/null +++ b/packages/zowe-explorer/resources/dark/filter-dark.svg @@ -0,0 +1,12 @@ + + + diff --git a/packages/zowe-explorer/resources/light/filter-light.svg b/packages/zowe-explorer/resources/light/filter-light.svg new file mode 100644 index 0000000000..3013b2a0fd --- /dev/null +++ b/packages/zowe-explorer/resources/light/filter-light.svg @@ -0,0 +1,12 @@ + + + diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 3d3d338c62..43dedc7c98 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 114; +export const COMMAND_COUNT = 115; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000; diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index b7dd81ef9e..3e0b36070d 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -14,12 +14,13 @@ import * as zowe from "@zowe/cli"; import { errorHandling } from "../utils/ProfilesUtils"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; -import { Gui, IZoweTree, IZoweJobTreeNode } from "@zowe/zowe-explorer-api"; +import { Gui, IZoweTree, IZoweJobTreeNode, IZoweNodeType } from "@zowe/zowe-explorer-api"; import { Job, Spool } from "./ZoweJobNode"; import * as nls from "vscode-nls"; import SpoolProvider, { encodeJobFile, getSpoolFiles, matchSpool } from "../SpoolProvider"; import { ZoweLogger } from "../utils/LoggerUtils"; import { getDefaultUri } from "../shared/utils"; +import { TreeViewUtils } from "../utils/TreeViewUtils"; // Set up localization nls.config({ @@ -541,3 +542,74 @@ export async function sortJobsBy(jobs: IZoweJobTreeNode, jobsProvider: IZoweTree }); jobsProvider.refresh(); } + +export async function filterJobs(jobsProvider: IZoweTree): Promise { + let acutal_jobs; + let flag = false; + for (const level of jobsProvider.mSessionNodes) { + if (level.label === "zosmf") { + acutal_jobs = level.children; + if (level.collapsibleState === 1) { + vscode.window.showInformationMessage("Inorder to filter jobs,first populate them using search icon"); + flag = true; + } + } + } + if (flag) return; + + const inputBox = await vscode.window.createInputBox(); + inputBox.placeholder = "Type here..."; + inputBox.onDidChangeValue((query) => { + query = query.toUpperCase(); + for (const level of jobsProvider.mSessionNodes) { + if (level.label === "zosmf") { + level.children = acutal_jobs.filter((item) => + `${item["job"].jobname as string}(${item["job"].jobid as string}) - ${item["job"].retcode as string}`.includes(query) + ); + } + } + jobsProvider.refresh(); + }); + inputBox.show(); + return inputBox; +} + +export async function filterSpools( + jobsProvider: IZoweTree, + job: IZoweJobTreeNode, + zoweFileProvider: IZoweTree +): Promise { + if (job["collapsibleState"] == 1) { + const spools = await getSpoolFiles(job); + const Spools = spools.map((spool) => { + const spoolNode = new Spool( + `${spool.stepname}:${spool.ddname} - ${spool["record-count"]}`, + vscode.TreeItemCollapsibleState.None, + job.getParent(), + job.getSession(), + spool, + job.job, + job.getParent() + ); + return spoolNode; + }); + job.children = Spools; + + await TreeViewUtils.expandNode(job, zoweFileProvider); + job.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + jobsProvider.refresh(); + } + + const actual_spools = job.children; + const inputBox = vscode.window.createInputBox(); + inputBox.placeholder = "Type here..."; + inputBox.onDidChangeValue((query) => { + query = query.toUpperCase(); + job["children"] = actual_spools.filter((item) => + `${item["spool"].stepname as string}:${item["spool"].ddname as string} - ${item["spool"]["record-count"] as string}`.includes(query) + ); + jobsProvider.refresh(); + }); + inputBox.show(); + return inputBox; +} diff --git a/packages/zowe-explorer/src/job/init.ts b/packages/zowe-explorer/src/job/init.ts index 55cb416fa2..d6239db317 100644 --- a/packages/zowe-explorer/src/job/init.ts +++ b/packages/zowe-explorer/src/job/init.ts @@ -13,7 +13,7 @@ import * as globals from "../globals"; import * as vscode from "vscode"; import * as jobActions from "./actions"; import * as refreshActions from "../shared/refresh"; -import { IZoweJobTreeNode, IZoweTreeNode, IZoweTree } from "@zowe/zowe-explorer-api"; +import { IZoweJobTreeNode, IZoweTreeNode, IZoweTree, IZoweNodeType } from "@zowe/zowe-explorer-api"; import { Profiles } from "../Profiles"; import { createJobsTree } from "./ZosJobsProvider"; import * as contextuals from "../shared/context"; @@ -24,7 +24,9 @@ import { ZoweLogger } from "../utils/LoggerUtils"; export async function initJobsProvider(context: vscode.ExtensionContext): Promise> { ZoweLogger.trace("job.init.initJobsProvider called."); - const jobsProvider: IZoweTree = await createJobsTree(globals.LOG); + const data = await createJobsTree(globals.LOG); + const jobsProvider: IZoweTree = data; + const zoweFileProvider: IZoweTree = data; if (jobsProvider == null) { return null; } @@ -174,6 +176,18 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis context.subscriptions.push( vscode.commands.registerCommand("zowe.jobs.sortbyreturncode", (job) => jobActions.sortJobsBy(job, jobsProvider, "retcode")) ); + context.subscriptions.push( + vscode.commands.registerCommand("zowe.jobs.filterJobs", async () => { + await jobActions.filterJobs(jobsProvider); + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand("zowe.jobs.filterSpools", async (job) => { + await jobActions.filterSpools(jobsProvider, job, zoweFileProvider); + }) + ); + initSubscribers(context, jobsProvider); return jobsProvider; } From f780957178ac76881449de2f6a41afa83a311982 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Sat, 23 Sep 2023 08:18:34 +0530 Subject: [PATCH 02/47] updated changelog file Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index b4815bcd01..ba0c5462a1 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Introduce a new user interface for managing profiles via right-click action "Manage Profile". - Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) - Added new API {ZE Extender MetaData} to allow extenders to have the metadata of registered extenders to aid in team configuration file creation from a view that isn't Zowe Explorer's. [#2394](https://github.com/zowe/vscode-extension-for-zowe/issues/2394) +- Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) ### Bug fixes From 59737e9159ad0dbc7b736c07dbc336e93e2fe4a9 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Sat, 23 Sep 2023 08:45:12 +0530 Subject: [PATCH 03/47] Updated sample data in actions.unit.test.ts file Signed-off-by: SanthoshiBoyina --- .../__tests__/__unit__/job/actions.unit.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index d49923cca7..85b6931677 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -1457,7 +1457,7 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { vscode.TreeItemCollapsibleState.None, null, null, - setJobObjects(createIJobObject(), "BOYINAA", "JOB04945", "CC 0000"), + setJobObjects(createIJobObject(), "ZOWEUSR1", "JOB04945", "CC 0000"), null ); const node2 = new Job( @@ -1465,7 +1465,7 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { vscode.TreeItemCollapsibleState.None, null, null, - setJobObjects(createIJobObject(), "BOYINAA", "JOB05037", "CC 0000"), + setJobObjects(createIJobObject(), "ZOWEUSR2", "JOB05037", "CC 0000"), null ); @@ -1486,7 +1486,7 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { testTree.mSessionNodes[0].children = [node1, node2]; const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); - mockInputBox.value = "BOYINAA(JOB04945) - CC 0000"; + mockInputBox.value = "ZOWEUSR1(JOB04945) - CC 0000"; createInputBoxSpy.mockReturnValue(mockInputBox); const filterJobsSpy = jest.spyOn(jobActions, "filterJobs"); await jobActions.filterJobs(testTree); @@ -1530,7 +1530,7 @@ describe("Job Actions Unit Tests - Filter Spools", () => { vscode.TreeItemCollapsibleState.None, null, null, - setJobObjects(createIJobObject(), "BOYINAA", "JOB05037", "CC 0000"), + setJobObjects(createIJobObject(), "ZOWEUSR2", "JOB05037", "CC 0000"), null ); @@ -1539,7 +1539,7 @@ describe("Job Actions Unit Tests - Filter Spools", () => { vscode.TreeItemCollapsibleState.None, node2, blockMocks.session, - setJobObjects(createIJobObject(), "BOYINAA", "JOB04945", "CC 0000"), + setJobObjects(createIJobObject(), "ZOWEUSR1", "JOB04945", "CC 0000"), createIProfile() ); From 88024ccf0cdba333ced47d32ace2a62858c67fcc Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Thu, 28 Sep 2023 17:15:56 +0530 Subject: [PATCH 04/47] Updated with error handling Signed-off-by: SanthoshiBoyina --- .../i18n/sample/src/job/actions.i18n.json | 4 +- packages/zowe-explorer/src/job/actions.ts | 69 ++++++++++--------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json index f1c4dad3d1..2b7ff94a1b 100644 --- a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json @@ -21,5 +21,7 @@ "cancelJobs.notImplemented": "The cancel function is not implemented in this API.", "cancelJobs.notCancelled": "The job was not cancelled.", "cancelJobs.failed": "One or more jobs failed to cancel: {0}", - "cancelJobs.succeeded": "Cancelled selected jobs successfully." + "cancelJobs.succeeded": "Cancelled selected jobs successfully.", + "filterJobs.message": "Inorder to filter jobs,first populate them using search icon", + "filterJobs.prompt.message": "Type here..." } diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 3e0b36070d..20b0de79e3 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -549,8 +549,8 @@ export async function filterJobs(jobsProvider: IZoweTree): Pro for (const level of jobsProvider.mSessionNodes) { if (level.label === "zosmf") { acutal_jobs = level.children; - if (level.collapsibleState === 1) { - vscode.window.showInformationMessage("Inorder to filter jobs,first populate them using search icon"); + if (level.collapsibleState === vscode.TreeItemCollapsibleState.Collapsed) { + Gui.infoMessage(localize("filterJobs.message", "Inorder to filter jobs,first populate them using search icon")); flag = true; } } @@ -558,7 +558,7 @@ export async function filterJobs(jobsProvider: IZoweTree): Pro if (flag) return; const inputBox = await vscode.window.createInputBox(); - inputBox.placeholder = "Type here..."; + inputBox.placeholder = localize("filterJobs.prompt.message", "Type here..."); inputBox.onDidChangeValue((query) => { query = query.toUpperCase(); for (const level of jobsProvider.mSessionNodes) { @@ -579,37 +579,40 @@ export async function filterSpools( job: IZoweJobTreeNode, zoweFileProvider: IZoweTree ): Promise { - if (job["collapsibleState"] == 1) { - const spools = await getSpoolFiles(job); - const Spools = spools.map((spool) => { - const spoolNode = new Spool( - `${spool.stepname}:${spool.ddname} - ${spool["record-count"]}`, - vscode.TreeItemCollapsibleState.None, - job.getParent(), - job.getSession(), - spool, - job.job, - job.getParent() + try { + if (job["collapsibleState"] == vscode.TreeItemCollapsibleState.Collapsed) { + const Spools = (await getSpoolFiles(job)).map((spool) => { + const spoolNode = new Spool( + `${spool.stepname}:${spool.ddname} - ${spool["record-count"]}`, + vscode.TreeItemCollapsibleState.None, + job.getParent(), + job.getSession(), + spool, + job.job, + job.getParent() + ); + return spoolNode; + }); + job.children = Spools; + + await TreeViewUtils.expandNode(job, zoweFileProvider); + job.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + jobsProvider.refresh(); + } + + const actual_spools = job.children; + const inputBox = vscode.window.createInputBox(); + inputBox.placeholder = localize("filterJobs.prompt.message", "Type here..."); + inputBox.onDidChangeValue((query) => { + query = query.toUpperCase(); + job["children"] = actual_spools.filter((item) => + `${item["spool"].stepname as string}:${item["spool"].ddname as string} - ${item["spool"]["record-count"] as string}`.includes(query) ); - return spoolNode; + jobsProvider.refresh(); }); - job.children = Spools; - - await TreeViewUtils.expandNode(job, zoweFileProvider); - job.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; - jobsProvider.refresh(); + inputBox.show(); + return inputBox; + } catch (error) { + await errorHandling(error); } - - const actual_spools = job.children; - const inputBox = vscode.window.createInputBox(); - inputBox.placeholder = "Type here..."; - inputBox.onDidChangeValue((query) => { - query = query.toUpperCase(); - job["children"] = actual_spools.filter((item) => - `${item["spool"].stepname as string}:${item["spool"].ddname as string} - ${item["spool"]["record-count"] as string}`.includes(query) - ); - jobsProvider.refresh(); - }); - inputBox.show(); - return inputBox; } From 936c9095bc1c15c2897a9cd000aa3825d3a902ef Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Thu, 28 Sep 2023 17:27:39 +0530 Subject: [PATCH 05/47] resolved conflicts Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index ba0c5462a1..6dc1dadb4c 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) - Added new API {ZE Extender MetaData} to allow extenders to have the metadata of registered extenders to aid in team configuration file creation from a view that isn't Zowe Explorer's. [#2394](https://github.com/zowe/vscode-extension-for-zowe/issues/2394) - Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) +- Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) ### Bug fixes From 2df66b6512d8d5ea8d5a970669e621461de556d5 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Sat, 23 Sep 2023 00:25:50 +0530 Subject: [PATCH 06/47] updated with local filtering of jobs Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/src/job/actions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 20b0de79e3..9b89a85383 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -21,6 +21,7 @@ import SpoolProvider, { encodeJobFile, getSpoolFiles, matchSpool } from "../Spoo import { ZoweLogger } from "../utils/LoggerUtils"; import { getDefaultUri } from "../shared/utils"; import { TreeViewUtils } from "../utils/TreeViewUtils"; +import { TreeViewUtils } from "../utils/TreeViewUtils"; // Set up localization nls.config({ From ada826bff9811948a80e646b8a4e731d88d373fa Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Sat, 23 Sep 2023 08:18:34 +0530 Subject: [PATCH 07/47] updated changelog file Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 6dc1dadb4c..300590b659 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added new API {ZE Extender MetaData} to allow extenders to have the metadata of registered extenders to aid in team configuration file creation from a view that isn't Zowe Explorer's. [#2394](https://github.com/zowe/vscode-extension-for-zowe/issues/2394) - Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) - Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) +- Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) ### Bug fixes From b78a4f266ed365603b35638a2eec6fbd596f13e4 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Thu, 28 Sep 2023 17:27:39 +0530 Subject: [PATCH 08/47] resolved conflicts Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 300590b659..ffd35a490e 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) - Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) - Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) +- Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) ### Bug fixes From 000c87f6609d4f9de047cfc84782f9313c1c8485 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Fri, 29 Sep 2023 13:59:33 +0530 Subject: [PATCH 09/47] updated use of tree provider Signed-off-by: SanthoshiBoyina --- .../__tests__/__unit__/job/actions.unit.test.ts | 15 ++++++++------- .../i18n/sample/src/job/actions.i18n.json | 4 ++-- packages/zowe-explorer/src/job/actions.ts | 14 +++++--------- packages/zowe-explorer/src/job/init.ts | 7 ++----- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 85b6931677..0c84a019e8 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -94,6 +94,7 @@ function createGlobalMocks() { configurable: true, }); Object.defineProperty(Gui, "showMessage", { value: jest.fn(), configurable: true }); + Object.defineProperty(Gui, "infoMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(Gui, "warningMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(Gui, "errorMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(Gui, "showOpenDialog", { value: jest.fn(), configurable: true }); @@ -1476,7 +1477,7 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { await jobActions.filterJobs(testTree); - expect(mocked(vscode.window.showInformationMessage)).toHaveBeenCalled(); + expect(mocked(Gui.infoMessage)).toHaveBeenCalled(); }); it("To filter jobs based on a combination of JobName, JobId and Return code", async () => { @@ -1563,23 +1564,23 @@ describe("Job Actions Unit Tests - Filter Spools", () => { ); it("To check a job is expanded or not when filter spools option is choosen", async () => { - const testTree = new ZosJobsProvider(); + const testTree = blockMocks.testJobTree; node1.collapsibleState = 1; const filterSpoolsSpy = jest.spyOn(jobActions, "filterSpools"); const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); mockInputBox.value = "DELETE:SYSPRINT - 7"; createInputBoxSpy.mockReturnValue(mockInputBox); - await jobActions.filterSpools(testTree, node1, blockMocks.testJobTree); + await jobActions.filterSpools(testTree, node1); expect(createInputBoxSpy).toHaveBeenCalled(); expect(filterSpoolsSpy).toHaveBeenCalled(); - expect(filterSpoolsSpy).toBeCalledWith(testTree, node1, blockMocks.testJobTree); + expect(filterSpoolsSpy).toBeCalledWith(testTree, node1); expect(filterSpoolsSpy.mock.calls[0][1].collapsibleState).toBe(vscode.TreeItemCollapsibleState.Expanded); }); it("To filter spools based on a combination of stepname, ddname and record-count", async () => { - const testTree = new ZosJobsProvider(); + const testTree = blockMocks.testJobTree; node1.collapsibleState = 1; node1.children = [spoolNode1, spoolNode2]; testTree.mSessionNodes[0].children = [node1]; @@ -1588,13 +1589,13 @@ describe("Job Actions Unit Tests - Filter Spools", () => { createInputBoxSpy.mockReturnValue(mockInputBox); const filterSpoolsSpy = jest.spyOn(jobActions, "filterSpools"); - await jobActions.filterSpools(testTree, node1, blockMocks.testJobTree); + await jobActions.filterSpools(testTree, node1); node1.children = [spoolNode1]; testTree.mSessionNodes[0].children = [node1]; expect(createInputBoxSpy).toHaveBeenCalled(); expect(filterSpoolsSpy).toHaveBeenCalled(); - expect(filterSpoolsSpy).toBeCalledWith(testTree, node1, blockMocks.testJobTree); + expect(filterSpoolsSpy).toBeCalledWith(testTree, node1); expect(filterSpoolsSpy.mock.calls[0][0].mSessionNodes[0].children).toBe(testTree.mSessionNodes[0].children); }); }); diff --git a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json index 2b7ff94a1b..816a92caba 100644 --- a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json @@ -22,6 +22,6 @@ "cancelJobs.notCancelled": "The job was not cancelled.", "cancelJobs.failed": "One or more jobs failed to cancel: {0}", "cancelJobs.succeeded": "Cancelled selected jobs successfully.", - "filterJobs.message": "Inorder to filter jobs,first populate them using search icon", - "filterJobs.prompt.message": "Type here..." + "filterJobs.message": "Use the search button to display jobs", + "filterJobs.prompt.message": "Enter local filter..." } diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 9b89a85383..892fb5d92d 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -551,7 +551,7 @@ export async function filterJobs(jobsProvider: IZoweTree): Pro if (level.label === "zosmf") { acutal_jobs = level.children; if (level.collapsibleState === vscode.TreeItemCollapsibleState.Collapsed) { - Gui.infoMessage(localize("filterJobs.message", "Inorder to filter jobs,first populate them using search icon")); + Gui.infoMessage(localize("filterJobs.message", "Use the search button to display jobs")); flag = true; } } @@ -559,7 +559,7 @@ export async function filterJobs(jobsProvider: IZoweTree): Pro if (flag) return; const inputBox = await vscode.window.createInputBox(); - inputBox.placeholder = localize("filterJobs.prompt.message", "Type here..."); + inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); inputBox.onDidChangeValue((query) => { query = query.toUpperCase(); for (const level of jobsProvider.mSessionNodes) { @@ -575,11 +575,7 @@ export async function filterJobs(jobsProvider: IZoweTree): Pro return inputBox; } -export async function filterSpools( - jobsProvider: IZoweTree, - job: IZoweJobTreeNode, - zoweFileProvider: IZoweTree -): Promise { +export async function filterSpools(jobsProvider: IZoweTree, job: IZoweJobTreeNode): Promise { try { if (job["collapsibleState"] == vscode.TreeItemCollapsibleState.Collapsed) { const Spools = (await getSpoolFiles(job)).map((spool) => { @@ -596,14 +592,14 @@ export async function filterSpools( }); job.children = Spools; - await TreeViewUtils.expandNode(job, zoweFileProvider); + await TreeViewUtils.expandNode(job, jobsProvider); job.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; jobsProvider.refresh(); } const actual_spools = job.children; const inputBox = vscode.window.createInputBox(); - inputBox.placeholder = localize("filterJobs.prompt.message", "Type here..."); + inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); inputBox.onDidChangeValue((query) => { query = query.toUpperCase(); job["children"] = actual_spools.filter((item) => diff --git a/packages/zowe-explorer/src/job/init.ts b/packages/zowe-explorer/src/job/init.ts index d6239db317..bf2903e0e4 100644 --- a/packages/zowe-explorer/src/job/init.ts +++ b/packages/zowe-explorer/src/job/init.ts @@ -24,9 +24,7 @@ import { ZoweLogger } from "../utils/LoggerUtils"; export async function initJobsProvider(context: vscode.ExtensionContext): Promise> { ZoweLogger.trace("job.init.initJobsProvider called."); - const data = await createJobsTree(globals.LOG); - const jobsProvider: IZoweTree = data; - const zoweFileProvider: IZoweTree = data; + const jobsProvider: IZoweTree = await createJobsTree(globals.LOG); if (jobsProvider == null) { return null; } @@ -181,10 +179,9 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis await jobActions.filterJobs(jobsProvider); }) ); - context.subscriptions.push( vscode.commands.registerCommand("zowe.jobs.filterSpools", async (job) => { - await jobActions.filterSpools(jobsProvider, job, zoweFileProvider); + await jobActions.filterSpools(jobsProvider, job); }) ); From e88cbee89923014d1c58a156439e9b4a8f8a802e Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Fri, 29 Sep 2023 15:20:50 +0530 Subject: [PATCH 10/47] resolved latest conflicts Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 2 -- packages/zowe-explorer/src/job/actions.ts | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index ffd35a490e..6dc1dadb4c 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -12,8 +12,6 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added new API {ZE Extender MetaData} to allow extenders to have the metadata of registered extenders to aid in team configuration file creation from a view that isn't Zowe Explorer's. [#2394](https://github.com/zowe/vscode-extension-for-zowe/issues/2394) - Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) - Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) -- Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) -- Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) ### Bug fixes diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 892fb5d92d..78ccaa90a1 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -14,14 +14,13 @@ import * as zowe from "@zowe/cli"; import { errorHandling } from "../utils/ProfilesUtils"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; -import { Gui, IZoweTree, IZoweJobTreeNode, IZoweNodeType } from "@zowe/zowe-explorer-api"; +import { Gui, IZoweTree, IZoweJobTreeNode } from "@zowe/zowe-explorer-api"; import { Job, Spool } from "./ZoweJobNode"; import * as nls from "vscode-nls"; import SpoolProvider, { encodeJobFile, getSpoolFiles, matchSpool } from "../SpoolProvider"; import { ZoweLogger } from "../utils/LoggerUtils"; import { getDefaultUri } from "../shared/utils"; import { TreeViewUtils } from "../utils/TreeViewUtils"; -import { TreeViewUtils } from "../utils/TreeViewUtils"; // Set up localization nls.config({ From 01f14b52dbd82b914b1cf7b66dfbf95621a07524 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Fri, 29 Sep 2023 15:26:25 +0530 Subject: [PATCH 11/47] addressed the code smell Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/src/job/init.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zowe-explorer/src/job/init.ts b/packages/zowe-explorer/src/job/init.ts index bf2903e0e4..7090cdc449 100644 --- a/packages/zowe-explorer/src/job/init.ts +++ b/packages/zowe-explorer/src/job/init.ts @@ -13,7 +13,7 @@ import * as globals from "../globals"; import * as vscode from "vscode"; import * as jobActions from "./actions"; import * as refreshActions from "../shared/refresh"; -import { IZoweJobTreeNode, IZoweTreeNode, IZoweTree, IZoweNodeType } from "@zowe/zowe-explorer-api"; +import { IZoweJobTreeNode, IZoweTreeNode, IZoweTree } from "@zowe/zowe-explorer-api"; import { Profiles } from "../Profiles"; import { createJobsTree } from "./ZosJobsProvider"; import * as contextuals from "../shared/context"; From 5098ac97057250cabbad22fd613598912fdd3e72 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Fri, 29 Sep 2023 22:40:15 +0530 Subject: [PATCH 12/47] Improved code coverage in init.ts Signed-off-by: SanthoshiBoyina --- .../__tests__/__unit__/job/init.unit.test.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts index 9a9f9fdd85..e2fe07acc2 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts @@ -54,6 +54,8 @@ describe("Test src/jobs/extension", () => { onDidChangeConfiguration: jest.fn(), pollData: jest.fn(), refreshElement: jest.fn(), + filterJobs: jest.fn(), + filterSpools: jest.fn(), }; const commands: IJestIt[] = [ { @@ -219,6 +221,14 @@ describe("Test src/jobs/extension", () => { mock: [{ spy: jest.spyOn(jobActions, "cancelJobs"), arg: [jobsProvider, [exampleData.job]] }], parm: [exampleData.job], }, + { + name: "zowe.jobs.filterJobs", + mock: [{ spy: jest.spyOn(jobActions, "filterJobs"), arg: [jobsProvider] }], + }, + { + name: "zowe.jobs.filterSpools", + mock: [{ spy: jest.spyOn(jobActions, "filterSpools"), arg: [jobsProvider, test.value] }], + }, ]; beforeAll(async () => { From 0e0d4a32da0403a3f55f4bf9966eebdcd5fefbf5 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Tue, 10 Oct 2023 21:22:18 +0530 Subject: [PATCH 13/47] updated with filter jobs Signed-off-by: SanthoshiBoyina --- .../__tests__/__unit__/extension.unit.test.ts | 1 - .../__unit__/job/actions.unit.test.ts | 124 +++--------------- .../__tests__/__unit__/job/init.unit.test.ts | 6 +- packages/zowe-explorer/package.json | 5 - packages/zowe-explorer/src/globals.ts | 2 +- packages/zowe-explorer/src/job/actions.ts | 69 ++-------- packages/zowe-explorer/src/job/init.ts | 9 +- 7 files changed, 29 insertions(+), 187 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts index 0037c3e2d7..3fa766a107 100644 --- a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts @@ -242,7 +242,6 @@ async function createGlobalMocks() { "zowe.jobs.sortbyid", "zowe.jobs.sortbyreturncode", "zowe.jobs.filterJobs", - "zowe.jobs.filterSpools", "zowe.manualPoll", "zowe.updateSecureCredentials", "zowe.promptCredentials", diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 0c84a019e8..1b1dcc178f 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -1469,13 +1469,21 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { setJobObjects(createIJobObject(), "ZOWEUSR2", "JOB05037", "CC 0000"), null ); + const node3 = new Job( + "jobnew", + vscode.TreeItemCollapsibleState.None, + null, + null, + setJobObjects(createIJobObject(), "ZOWEUSR3", "TSU07707", "ABEND S222"), + null + ); it("To show showInformationMessage", async () => { const testTree = new ZosJobsProvider(); testTree.mSessionNodes[0].label = "zosmf"; - testTree.mSessionNodes[0].collapsibleState = 1; + node1.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; - await jobActions.filterJobs(testTree); + await jobActions.filterJobs(testTree, node1); expect(mocked(Gui.infoMessage)).toHaveBeenCalled(); }); @@ -1483,119 +1491,19 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { it("To filter jobs based on a combination of JobName, JobId and Return code", async () => { const testTree = new ZosJobsProvider(); testTree.mSessionNodes[0].label = "zosmf"; - testTree.mSessionNodes[0].collapsibleState = 2; - testTree.mSessionNodes[0].children = [node1, node2]; + node1.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; + node1.children = [node2, node3]; const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); - mockInputBox.value = "ZOWEUSR1(JOB04945) - CC 0000"; + mockInputBox.value = "ZOWEUSR2(JOB05037) - CC 0000"; createInputBoxSpy.mockReturnValue(mockInputBox); const filterJobsSpy = jest.spyOn(jobActions, "filterJobs"); - await jobActions.filterJobs(testTree); - testTree.mSessionNodes[0].children = [node1]; + await jobActions.filterJobs(testTree, node1); + testTree.mSessionNodes[0].children = [node2]; expect(createInputBoxSpy).toHaveBeenCalled(); expect(filterJobsSpy).toHaveBeenCalled(); - expect(filterJobsSpy).toBeCalledWith(testTree); + expect(filterJobsSpy).toBeCalledWith(testTree, node1); expect(filterJobsSpy.mock.calls[0][0].mSessionNodes[0].children).toBe(testTree.mSessionNodes[0].children); }); }); - -describe("Job Actions Unit Tests - Filter Spools", () => { - function createBlockMocks() { - const session = createISession(); - const treeView = createTreeView(); - const iJob = createIJobObject(); - const imperativeProfile = createIProfile(); - const testJobTree = createJobsTree(session, iJob, imperativeProfile, treeView); - - return { - session, - treeView, - iJob, - imperativeProfile, - testJobTree, - testJobsTree: createJobsTree(session, iJob, imperativeProfile, treeView), - }; - } - const blockMocks = createBlockMocks(); - - function setSpoolObjects(spool: zowe.IJobFile, newStepName: string, newDdName: string, newRecordCount: number) { - spool.stepname = newStepName; - spool.ddname = newDdName; - spool["record-count"] = newRecordCount; - return spool; - } - - const node2 = new Job( - "jobnew", - vscode.TreeItemCollapsibleState.None, - null, - null, - setJobObjects(createIJobObject(), "ZOWEUSR2", "JOB05037", "CC 0000"), - null - ); - - const node1 = new Job( - "jobnew", - vscode.TreeItemCollapsibleState.None, - node2, - blockMocks.session, - setJobObjects(createIJobObject(), "ZOWEUSR1", "JOB04945", "CC 0000"), - createIProfile() - ); - - const spoolNode1 = new Spool( - "JES2:JESMSGLG - 21", - vscode.TreeItemCollapsibleState.None, - node1, - null, - setSpoolObjects(createIJobFile(), "JES2", "JESMSGLG", 21), - createIJobObject(), - node1 - ); - const spoolNode2 = new Spool( - "DELETE:SYSPRINT - 7", - vscode.TreeItemCollapsibleState.None, - node1, - null, - setSpoolObjects(createIJobFile(), "DELETE", "SYSPRINT", 7), - createIJobObject(), - node1 - ); - - it("To check a job is expanded or not when filter spools option is choosen", async () => { - const testTree = blockMocks.testJobTree; - node1.collapsibleState = 1; - const filterSpoolsSpy = jest.spyOn(jobActions, "filterSpools"); - const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); - mockInputBox.value = "DELETE:SYSPRINT - 7"; - createInputBoxSpy.mockReturnValue(mockInputBox); - - await jobActions.filterSpools(testTree, node1); - - expect(createInputBoxSpy).toHaveBeenCalled(); - expect(filterSpoolsSpy).toHaveBeenCalled(); - expect(filterSpoolsSpy).toBeCalledWith(testTree, node1); - expect(filterSpoolsSpy.mock.calls[0][1].collapsibleState).toBe(vscode.TreeItemCollapsibleState.Expanded); - }); - - it("To filter spools based on a combination of stepname, ddname and record-count", async () => { - const testTree = blockMocks.testJobTree; - node1.collapsibleState = 1; - node1.children = [spoolNode1, spoolNode2]; - testTree.mSessionNodes[0].children = [node1]; - const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); - mockInputBox.value = "JES2:JESMSGLG - 21"; - createInputBoxSpy.mockReturnValue(mockInputBox); - const filterSpoolsSpy = jest.spyOn(jobActions, "filterSpools"); - - await jobActions.filterSpools(testTree, node1); - node1.children = [spoolNode1]; - testTree.mSessionNodes[0].children = [node1]; - - expect(createInputBoxSpy).toHaveBeenCalled(); - expect(filterSpoolsSpy).toHaveBeenCalled(); - expect(filterSpoolsSpy).toBeCalledWith(testTree, node1); - expect(filterSpoolsSpy.mock.calls[0][0].mSessionNodes[0].children).toBe(testTree.mSessionNodes[0].children); - }); -}); diff --git a/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts index e2fe07acc2..3ba9e119db 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts @@ -223,11 +223,7 @@ describe("Test src/jobs/extension", () => { }, { name: "zowe.jobs.filterJobs", - mock: [{ spy: jest.spyOn(jobActions, "filterJobs"), arg: [jobsProvider] }], - }, - { - name: "zowe.jobs.filterSpools", - mock: [{ spy: jest.spyOn(jobActions, "filterSpools"), arg: [jobsProvider, test.value] }], + mock: [{ spy: jest.spyOn(jobActions, "filterJobs"), arg: [jobsProvider, test.value] }], }, ]; diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index f16908824a..7903a60127 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -758,11 +758,6 @@ "dark": "./resources/dark/filter-dark.svg" } }, - { - "command": "zowe.jobs.filterSpools", - "title": "Filter Spools", - "category": "Zowe Explorer" - }, { "command": "zowe.jobs.search", "title": "%jobs.search%", diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 43dedc7c98..3d3d338c62 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 115; +export const COMMAND_COUNT = 114; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000; diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 78ccaa90a1..2883bec901 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -20,7 +20,6 @@ import * as nls from "vscode-nls"; import SpoolProvider, { encodeJobFile, getSpoolFiles, matchSpool } from "../SpoolProvider"; import { ZoweLogger } from "../utils/LoggerUtils"; import { getDefaultUri } from "../shared/utils"; -import { TreeViewUtils } from "../utils/TreeViewUtils"; // Set up localization nls.config({ @@ -543,72 +542,22 @@ export async function sortJobsBy(jobs: IZoweJobTreeNode, jobsProvider: IZoweTree jobsProvider.refresh(); } -export async function filterJobs(jobsProvider: IZoweTree): Promise { - let acutal_jobs; - let flag = false; - for (const level of jobsProvider.mSessionNodes) { - if (level.label === "zosmf") { - acutal_jobs = level.children; - if (level.collapsibleState === vscode.TreeItemCollapsibleState.Collapsed) { - Gui.infoMessage(localize("filterJobs.message", "Use the search button to display jobs")); - flag = true; - } - } +export async function filterJobs(jobsProvider: IZoweTree, job: IZoweJobTreeNode): Promise { + if (job.collapsibleState === vscode.TreeItemCollapsibleState.Collapsed) { + Gui.infoMessage(localize("filterJobs.message", "Use the search button to display jobs")); + return; } - if (flag) return; - + const acutal_jobs = job["children"]; const inputBox = await vscode.window.createInputBox(); inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); inputBox.onDidChangeValue((query) => { query = query.toUpperCase(); - for (const level of jobsProvider.mSessionNodes) { - if (level.label === "zosmf") { - level.children = acutal_jobs.filter((item) => - `${item["job"].jobname as string}(${item["job"].jobid as string}) - ${item["job"].retcode as string}`.includes(query) - ); - } - } + job["children"] = acutal_jobs.filter((item) => `${item["job"].jobname}(${item["job"].jobid}) - ${item["job"].retcode}`.includes(query)); jobsProvider.refresh(); }); + inputBox.onDidAccept(() => { + inputBox.hide(); + }); inputBox.show(); return inputBox; } - -export async function filterSpools(jobsProvider: IZoweTree, job: IZoweJobTreeNode): Promise { - try { - if (job["collapsibleState"] == vscode.TreeItemCollapsibleState.Collapsed) { - const Spools = (await getSpoolFiles(job)).map((spool) => { - const spoolNode = new Spool( - `${spool.stepname}:${spool.ddname} - ${spool["record-count"]}`, - vscode.TreeItemCollapsibleState.None, - job.getParent(), - job.getSession(), - spool, - job.job, - job.getParent() - ); - return spoolNode; - }); - job.children = Spools; - - await TreeViewUtils.expandNode(job, jobsProvider); - job.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; - jobsProvider.refresh(); - } - - const actual_spools = job.children; - const inputBox = vscode.window.createInputBox(); - inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); - inputBox.onDidChangeValue((query) => { - query = query.toUpperCase(); - job["children"] = actual_spools.filter((item) => - `${item["spool"].stepname as string}:${item["spool"].ddname as string} - ${item["spool"]["record-count"] as string}`.includes(query) - ); - jobsProvider.refresh(); - }); - inputBox.show(); - return inputBox; - } catch (error) { - await errorHandling(error); - } -} diff --git a/packages/zowe-explorer/src/job/init.ts b/packages/zowe-explorer/src/job/init.ts index 7090cdc449..95edfeeabb 100644 --- a/packages/zowe-explorer/src/job/init.ts +++ b/packages/zowe-explorer/src/job/init.ts @@ -175,13 +175,8 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis vscode.commands.registerCommand("zowe.jobs.sortbyreturncode", (job) => jobActions.sortJobsBy(job, jobsProvider, "retcode")) ); context.subscriptions.push( - vscode.commands.registerCommand("zowe.jobs.filterJobs", async () => { - await jobActions.filterJobs(jobsProvider); - }) - ); - context.subscriptions.push( - vscode.commands.registerCommand("zowe.jobs.filterSpools", async (job) => { - await jobActions.filterSpools(jobsProvider, job); + vscode.commands.registerCommand("zowe.jobs.filterJobs", async (job) => { + await jobActions.filterJobs(jobsProvider, job); }) ); From 5da04b76814375fb3919329277634b9638a2d924 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Wed, 11 Oct 2023 19:50:28 +0530 Subject: [PATCH 14/47] updated global.ts file Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/src/globals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 3d3d338c62..43dedc7c98 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 114; +export const COMMAND_COUNT = 115; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000; From 11267590bc78cadcb8baf4506d878ff303c70c16 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Tue, 3 Oct 2023 15:37:20 -0400 Subject: [PATCH 15/47] feat(ds): Sort by date modified, name, or user ID Signed-off-by: Trae Yelovich --- .../src/tree/IZoweTreeNode.ts | 19 +++++++++ .../i18n/sample/package.i18n.json | 8 ++-- packages/zowe-explorer/package.json | 32 ++++++++++++++- packages/zowe-explorer/package.nls.json | 8 ++-- .../src/abstract/ZoweTreeProvider.ts | 10 +++++ .../zowe-explorer/src/dataset/DatasetTree.ts | 17 ++++++++ .../src/dataset/ZoweDatasetNode.ts | 34 ++++++++++++--- packages/zowe-explorer/src/dataset/init.ts | 41 ++++++++++++------- 8 files changed, 143 insertions(+), 26 deletions(-) diff --git a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts index 7bd2349f31..d2ff45e178 100644 --- a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts @@ -20,6 +20,17 @@ export enum NodeAction { Download = "download", } +export enum DatasetSort { + LastModified, + Name, + UserId, +} + +export type DatasetStats = { + user: string; + m4date: Date; +}; + /** * The base interface for Zowe tree nodes that are implemented by vscode.TreeItem. * @@ -135,6 +146,14 @@ export interface IZoweDatasetTreeNode extends IZoweTreeNode { * Search criteria for a Dataset member search */ memberPattern?: string; + /** + * Sort members of a node by the given sorting method + */ + sortMethod?: DatasetSort; + /** + * Additional statistics about this data set + */ + stats?: Partial; /** * Retrieves child nodes of this IZoweDatasetTreeNode * diff --git a/packages/zowe-explorer/i18n/sample/package.i18n.json b/packages/zowe-explorer/i18n/sample/package.i18n.json index a73e7985e5..b543b506a6 100644 --- a/packages/zowe-explorer/i18n/sample/package.i18n.json +++ b/packages/zowe-explorer/i18n/sample/package.i18n.json @@ -148,7 +148,9 @@ "createZoweSchema.reload.infoMessage": "Team Configuration file created. Location: {0}. \n Please reload your window.", "copyFile": "Copy", "pasteFile": "Paste", - "jobs.sortbyreturncode": "Sort by ReturnCode", - "jobs.sortbyname": "Sort by Name", - "jobs.sortbyid": "Sort by ID" + "jobs.sortbyreturncode": "Sort by Return Code", + "jobs.sortbyid": "Sort by ID", + "sortByName": "Sort by Name", + "ds.sortByUserId": "Sort by User ID", + "ds.sortByModified": "Sort by Date Modified" } diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index 7903a60127..3e586fa038 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -128,7 +128,7 @@ }, { "command": "zowe.jobs.sortbyname", - "title": "%jobs.sortbyname%", + "title": "%sortByName%", "category": "Zowe Explorer" }, { @@ -281,6 +281,21 @@ "title": "%deleteProfile%", "category": "Zowe Explorer" }, + { + "command": "zowe.ds.sortByName", + "title": "%sortByName%", + "category": "Zowe Explorer" + }, + { + "command": "zowe.ds.sortByModified", + "title": "%ds.sortByModified%", + "category": "Zowe Explorer" + }, + { + "command": "zowe.ds.sortByUserId", + "title": "%ds.sortByUserId%", + "category": "Zowe Explorer" + }, { "command": "zowe.cmd.deleteProfile", "title": "%cmd.deleteProfile%", @@ -1087,6 +1102,21 @@ "command": "zowe.ds.removeFavProfile", "group": "002_zowe_dsWorkspace@4" }, + { + "when": "view == zowe.ds.explorer && viewItem =~ /^(pds).*/ && !listMultiSelection", + "command": "zowe.ds.sortByModified", + "group": "003_zowe_dsSort@1" + }, + { + "when": "view == zowe.ds.explorer && viewItem =~ /^(pds).*/ && !listMultiSelection", + "command": "zowe.ds.sortByName", + "group": "003_zowe_dsSort@2" + }, + { + "when": "view == zowe.ds.explorer && viewItem =~ /^(pds).*/ && !listMultiSelection", + "command": "zowe.ds.sortByUserId", + "group": "003_zowe_dsSort@3" + }, { "when": "view == zowe.ds.explorer && viewItem =~ /^fileError.*/", "command": "zowe.ds.showFileErrorDetails", diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json index a73e7985e5..b543b506a6 100644 --- a/packages/zowe-explorer/package.nls.json +++ b/packages/zowe-explorer/package.nls.json @@ -148,7 +148,9 @@ "createZoweSchema.reload.infoMessage": "Team Configuration file created. Location: {0}. \n Please reload your window.", "copyFile": "Copy", "pasteFile": "Paste", - "jobs.sortbyreturncode": "Sort by ReturnCode", - "jobs.sortbyname": "Sort by Name", - "jobs.sortbyid": "Sort by ID" + "jobs.sortbyreturncode": "Sort by Return Code", + "jobs.sortbyid": "Sort by ID", + "sortByName": "Sort by Name", + "ds.sortByUserId": "Sort by User ID", + "ds.sortByModified": "Sort by Date Modified" } diff --git a/packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts b/packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts index 0321cad081..afbdfef2e2 100644 --- a/packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts +++ b/packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts @@ -85,6 +85,16 @@ export class ZoweTreeProvider { } } + /** + * Fire the "onDidChangeTreeData" event to signal that a node in the tree has changed. + * Unlike `refreshElement`, this function does NOT signal a refresh for the given node - + * it simply tells VS Code to repaint the node in the tree. + * @param node The node that should be repainted + */ + public nodeDataChanged(node: IZoweTreeNode): void { + this.mOnDidChangeTreeData.fire(node); + } + /** * Called whenever the tree needs to be refreshed, and fires the data change event * diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index 41e428768d..542587aef2 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -23,6 +23,7 @@ import { PersistenceSchemaEnum, NodeInteraction, IZoweTreeNode, + DatasetSort, } from "@zowe/zowe-explorer-api"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; @@ -1287,4 +1288,20 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { + // If children nodes already exist, sort now and avoid extra refresh + node.children.sort(ZoweDatasetNode.sortBy(method)); + this.nodeDataChanged(node); + } else { + this.refreshElement(node); + } + } } diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index 8d369eb00d..a72a95b934 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -13,7 +13,7 @@ import * as zowe from "@zowe/cli"; import * as vscode from "vscode"; import * as globals from "../globals"; import { errorHandling } from "../utils/ProfilesUtils"; -import { Gui, NodeAction, IZoweDatasetTreeNode, ZoweTreeNode } from "@zowe/zowe-explorer-api"; +import { DatasetSort, DatasetStats, Gui, NodeAction, IZoweDatasetTreeNode, ZoweTreeNode } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; import { getIconByNode } from "../generators/icons"; import * as contextually from "../shared/context"; @@ -43,6 +43,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod public errorDetails: zowe.imperative.ImperativeError; public ongoingActions: Record> = {}; public wasDoubleClicked: boolean = false; + public sortMethod: DatasetSort = DatasetSort.Name; + public stats: DatasetStats; /** * Creates an instance of ZoweDatasetNode @@ -77,8 +79,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod if (icon) { this.iconPath = icon.path; } - if (!globals.ISTHEIA && this.getParent() && contextually.isSession(this.getParent())) { - this.id = `${mParent?.id ?? mParent?.label?.toString() ?? ""}.${this.label as string}`; + if (!globals.ISTHEIA && contextually.isSession(this)) { + this.id = this.label as string; } } @@ -235,6 +237,12 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod msg: localize("getChildren.invalidMember", "Cannot access member with control characters in the name: {0}", item.member), }); } + if (item.m4date) { + temp.stats = { + user: item.user, + m4date: new Date(`${item.m4date.replace(/\//g, "-")}T${item.mtime as string}:${item.msec as string}`), + }; + } elementChildren[temp.label.toString()] = temp; } } @@ -253,16 +261,32 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod ]; } else { const newChildren = Object.keys(elementChildren) - .sort() .filter((label) => this.children.find((c) => (c.label as string) === label) == null) .map((label) => elementChildren[label]); - this.children = this.children.concat(newChildren).filter((c) => (c.label as string) in elementChildren); + this.children = this.children + .concat(newChildren) + .filter((c) => (c.label as string) in elementChildren) + .sort(ZoweDatasetNode.sortBy(this.sortMethod)); } return this.children; } + public static sortBy(method: DatasetSort): (a: IZoweDatasetTreeNode, b: IZoweDatasetTreeNode) => number { + return (a, b): number => { + switch (method) { + case DatasetSort.LastModified: + return a.stats?.m4date < b.stats?.m4date ? -1 : 1; + case DatasetSort.UserId: + return a.stats?.user < b.stats?.user ? -1 : 1; + case DatasetSort.Name: + default: + return (a.label as string) < (b.label as string) ? -1 : 1; + } + }; + } + public getSessionNode(): IZoweDatasetTreeNode { ZoweLogger.trace("ZoweDatasetNode.getSessionNode called."); return this.getParent() ? this.getParent().getSessionNode() : this; diff --git a/packages/zowe-explorer/src/dataset/init.ts b/packages/zowe-explorer/src/dataset/init.ts index 12e81d74eb..9668ee1040 100644 --- a/packages/zowe-explorer/src/dataset/init.ts +++ b/packages/zowe-explorer/src/dataset/init.ts @@ -13,7 +13,7 @@ import * as globals from "../globals"; import * as vscode from "vscode"; import * as dsActions from "./actions"; import * as refreshActions from "../shared/refresh"; -import { IZoweDatasetTreeNode, IZoweTreeNode, IZoweTree } from "@zowe/zowe-explorer-api"; +import { IZoweDatasetTreeNode, IZoweTreeNode, IZoweTree, DatasetSort } from "@zowe/zowe-explorer-api"; import { Profiles } from "../Profiles"; import { createDatasetTree } from "./DatasetTree"; import { ZoweDatasetNode } from "./ZoweDatasetNode"; @@ -25,7 +25,7 @@ import { TreeViewUtils } from "../utils/TreeViewUtils"; export async function initDatasetProvider(context: vscode.ExtensionContext): Promise> { ZoweLogger.trace("dataset.init.initDatasetProvider called."); - const datasetProvider: IZoweTree = await createDatasetTree(globals.LOG); + const datasetProvider = await createDatasetTree(globals.LOG); if (datasetProvider == null) { return null; } @@ -37,10 +37,10 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro ); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.addSession", async () => datasetProvider.createZoweSession(datasetProvider))); context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.addFavorite", (node, nodeList) => { + vscode.commands.registerCommand("zowe.ds.addFavorite", async (node, nodeList) => { const selectedNodes = getSelectedNodeList(node, nodeList); for (const item of selectedNodes) { - datasetProvider.addFavorite(item); + await datasetProvider.addFavorite(item); } }) ); @@ -67,7 +67,7 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro } }) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.pattern", (node): void => datasetProvider.filterPrompt(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.pattern", (node) => datasetProvider.filterPrompt(node))); context.subscriptions.push( vscode.commands.registerCommand("zowe.ds.editSession", async (node) => datasetProvider.editSession(node, datasetProvider)) ); @@ -126,10 +126,10 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro } }) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.saveSearch", (node): void => datasetProvider.addFavorite(node))); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.removeSavedSearch", (node): void => datasetProvider.removeFavorite(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.saveSearch", (node) => datasetProvider.addFavorite(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.removeSavedSearch", (node) => datasetProvider.removeFavorite(node))); context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.removeFavProfile", (node): void => datasetProvider.removeFavProfile(node.label, true)) + vscode.commands.registerCommand("zowe.ds.removeFavProfile", (node) => datasetProvider.removeFavProfile(node.label, true)) ); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.submitJcl", async () => dsActions.submitJcl(datasetProvider))); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.submitMember", async (node) => dsActions.submitMember(node))); @@ -143,7 +143,7 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro } }) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.renameDataSet", (node): void => datasetProvider.rename(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.renameDataSet", (node) => datasetProvider.rename(node))); context.subscriptions.push( vscode.commands.registerCommand("zowe.ds.copyDataSets", async (node, nodeList) => dsActions.copyDataSets(node, nodeList, datasetProvider)) ); @@ -156,7 +156,7 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro await dsActions.refreshDataset(node.getParent(), datasetProvider); }) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.renameDataSetMember", (node): void => datasetProvider.rename(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.renameDataSetMember", (node) => datasetProvider.rename(node))); context.subscriptions.push( vscode.commands.registerCommand("zowe.ds.hMigrateDataSet", async (node, nodeList) => { let selectedNodes = getSelectedNodeList(node, nodeList); @@ -196,11 +196,24 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro datasetProvider.refreshElement(node); }) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogin", (node: IZoweTreeNode): void => datasetProvider.ssoLogin(node))); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogout", (node: IZoweTreeNode): void => datasetProvider.ssoLogout(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogin", (node: IZoweTreeNode) => datasetProvider.ssoLogin(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogout", (node: IZoweTreeNode) => datasetProvider.ssoLogout(node))); context.subscriptions.push( - vscode.workspace.onDidChangeConfiguration((e) => { - datasetProvider.onDidChangeConfiguration(e); + vscode.commands.registerCommand("zowe.ds.sortByName", (node: IZoweDatasetTreeNode): void => datasetProvider.sortBy(DatasetSort.Name, node)) + ); + context.subscriptions.push( + vscode.commands.registerCommand("zowe.ds.sortByModified", (node: IZoweDatasetTreeNode): void => + datasetProvider.sortBy(DatasetSort.LastModified, node) + ) + ); + context.subscriptions.push( + vscode.commands.registerCommand("zowe.ds.sortByUserId", (node: IZoweDatasetTreeNode): void => + datasetProvider.sortBy(DatasetSort.UserId, node) + ) + ); + context.subscriptions.push( + vscode.workspace.onDidChangeConfiguration(async (e) => { + await datasetProvider.onDidChangeConfiguration(e); }) ); From cf7e333213958288bebef48ae88db1c1a236258b Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Tue, 3 Oct 2023 16:08:30 -0400 Subject: [PATCH 16/47] fix(ds): deconstruct date, time, seconds from API response Signed-off-by: Trae Yelovich --- packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts | 7 +++++-- packages/zowe-explorer/src/job/ZoweJobNode.ts | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index a72a95b934..26d12a7904 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -237,10 +237,13 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod msg: localize("getChildren.invalidMember", "Cannot access member with control characters in the name: {0}", item.member), }); } - if (item.m4date) { + + // get user and last modified date for sorting, if available + const { m4date, mtime, msec }: { m4date: string; mtime: string; msec: string } = item; + if (m4date) { temp.stats = { user: item.user, - m4date: new Date(`${item.m4date.replace(/\//g, "-")}T${item.mtime as string}:${item.msec as string}`), + m4date: new Date(`${m4date.replace(/\//g, "-")}T${mtime}:${msec}`), }; } elementChildren[temp.label.toString()] = temp; diff --git a/packages/zowe-explorer/src/job/ZoweJobNode.ts b/packages/zowe-explorer/src/job/ZoweJobNode.ts index 1bcc7f86f3..dc757c8b2d 100644 --- a/packages/zowe-explorer/src/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/job/ZoweJobNode.ts @@ -89,8 +89,8 @@ export class Job extends ZoweTreeNode implements IZoweJobTreeNode { this.iconPath = icon.path; } - if (!globals.ISTHEIA && !(this instanceof Spool)) { - this.id = `${mParent?.id ?? mParent?.label?.toString() ?? ""}.${this.label as string}`; + if (!globals.ISTHEIA && contextually.isSession(this)) { + this.id = this.label as string; } } From 378881cee04a93eedac926aa6a905e746fbeb88d Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Tue, 3 Oct 2023 16:46:49 -0400 Subject: [PATCH 17/47] fix(ds, jobs): update node children check; adjust sortJobsBy function for session persistence (wip) Signed-off-by: Trae Yelovich --- .../zowe-explorer/src/dataset/DatasetTree.ts | 2 +- packages/zowe-explorer/src/job/actions.ts | 24 ++++++++++--------- packages/zowe-explorer/src/job/init.ts | 20 +++++++++------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index 542587aef2..e4b691a3c3 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1296,7 +1296,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { + if (node.children != null) { // If children nodes already exist, sort now and avoid extra refresh node.children.sort(ZoweDatasetNode.sortBy(method)); this.nodeDataChanged(node); diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 2883bec901..06d36e3b35 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -20,6 +20,7 @@ import * as nls from "vscode-nls"; import SpoolProvider, { encodeJobFile, getSpoolFiles, matchSpool } from "../SpoolProvider"; import { ZoweLogger } from "../utils/LoggerUtils"; import { getDefaultUri } from "../shared/utils"; +import { ZosJobsProvider } from "./ZosJobsProvider"; // Set up localization nls.config({ @@ -528,18 +529,19 @@ export async function cancelJobs(jobsProvider: IZoweTree, node await Gui.showMessage(localize("cancelJobs.succeeded", "Cancelled selected jobs successfully.")); } } -export async function sortJobsBy(jobs: IZoweJobTreeNode, jobsProvider: IZoweTree, key: keyof zowe.IJob): Promise { - if (jobs["children"].length == 0) { - await vscode.window.showInformationMessage("No jobs are present in the profile."); +export function sortJobsBy(session: IZoweJobTreeNode, jobsProvider: ZosJobsProvider, key: keyof zowe.IJob): void { + if (session.children != null) { + session.children.sort((x, y) => { + if (key !== "jobid" && x["job"][key] == y["job"][key]) { + return x["job"]["jobid"] > y["job"]["jobid"] ? 1 : -1; + } else { + return x["job"][key] > y["job"][key] ? 1 : -1; + } + }); + jobsProvider.nodeDataChanged(session); + } else { + jobsProvider.refreshElement(session); } - jobs["children"].sort((x, y) => { - if (key !== "jobid" && x["job"][key] == y["job"][key]) { - return x["job"]["jobid"] > y["job"]["jobid"] ? 1 : -1; - } else { - return x["job"][key] > y["job"][key] ? 1 : -1; - } - }); - jobsProvider.refresh(); } export async function filterJobs(jobsProvider: IZoweTree, job: IZoweJobTreeNode): Promise { diff --git a/packages/zowe-explorer/src/job/init.ts b/packages/zowe-explorer/src/job/init.ts index 95edfeeabb..3842ff395b 100644 --- a/packages/zowe-explorer/src/job/init.ts +++ b/packages/zowe-explorer/src/job/init.ts @@ -24,7 +24,7 @@ import { ZoweLogger } from "../utils/LoggerUtils"; export async function initJobsProvider(context: vscode.ExtensionContext): Promise> { ZoweLogger.trace("job.init.initJobsProvider called."); - const jobsProvider: IZoweTree = await createJobsTree(globals.LOG); + const jobsProvider = await createJobsTree(globals.LOG); if (jobsProvider == null) { return null; } @@ -103,7 +103,7 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis context.subscriptions.push( vscode.commands.registerCommand("zowe.jobs.setJobSpool", async (session, jobId) => jobActions.focusOnJob(jobsProvider, session, jobId)) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.search", (node): void => jobsProvider.filterPrompt(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.search", async (node): Promise => jobsProvider.filterPrompt(node))); context.subscriptions.push( vscode.commands.registerCommand("zowe.jobs.editSession", async (node): Promise => jobsProvider.editSession(node, jobsProvider)) ); @@ -123,10 +123,12 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis } }) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.saveSearch", (node): void => jobsProvider.saveSearch(node))); - context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.removeSearchFavorite", (node): void => jobsProvider.removeFavorite(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.saveSearch", (node): void => void jobsProvider.saveSearch(node))); context.subscriptions.push( - vscode.commands.registerCommand("zowe.jobs.removeFavProfile", (node): void => jobsProvider.removeFavProfile(node.label, true)) + vscode.commands.registerCommand("zowe.jobs.removeSearchFavorite", async (node): Promise => jobsProvider.removeFavorite(node)) + ); + context.subscriptions.push( + vscode.commands.registerCommand("zowe.jobs.removeFavProfile", async (node): Promise => jobsProvider.removeFavProfile(node.label, true)) ); context.subscriptions.push( vscode.commands.registerCommand("zowe.jobs.disableValidation", (node) => { @@ -140,8 +142,8 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis jobsProvider.refreshElement(node); }) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.ssoLogin", (node: IZoweTreeNode): void => jobsProvider.ssoLogin(node))); - context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.ssoLogout", (node: IZoweTreeNode): void => jobsProvider.ssoLogout(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.ssoLogin", async (node): Promise => jobsProvider.ssoLogin(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.ssoLogout", async (node): Promise => jobsProvider.ssoLogout(node))); const spoolFileTogglePoll = (startPolling: boolean) => async (node: IZoweTreeNode, nodeList: IZoweTreeNode[]): Promise => { @@ -160,8 +162,8 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.startPolling", spoolFileTogglePoll(true))); context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.stopPolling", spoolFileTogglePoll(false))); context.subscriptions.push( - vscode.workspace.onDidChangeConfiguration((e) => { - jobsProvider.onDidChangeConfiguration(e); + vscode.workspace.onDidChangeConfiguration(async (e) => { + await jobsProvider.onDidChangeConfiguration(e); }) ); context.subscriptions.push( From 32b308cb59f153fdc8a2eb8b0a777dbc91ed7a8d Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Wed, 4 Oct 2023 13:46:58 -0400 Subject: [PATCH 18/47] tests(ds): Add test cases for DatasetTree.sortBy Signed-off-by: Trae Yelovich --- .../__tests__/__unit__/ZoweNode.unit.test.ts | 2 - .../__unit__/dataset/DatasetTree.unit.test.ts | 57 ++++++++++++++++++- .../__unit__/dataset/init.unit.test.ts | 14 +++++ .../__tests__/__unit__/extension.unit.test.ts | 3 + .../__unit__/job/actions.unit.test.ts | 10 ---- .../zowe-explorer/src/dataset/DatasetTree.ts | 2 +- packages/zowe-explorer/src/globals.ts | 2 +- 7 files changed, 75 insertions(+), 15 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts index 3bc557fb9f..72da57e2c7 100644 --- a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts @@ -238,7 +238,6 @@ describe("Unit Tests (Jest)", () => { undefined, profileOne ); - infoChild.id = "root.Use the search button to display data sets"; rootNode.contextValue = globals.DS_SESSION_CONTEXT; rootNode.dirty = false; await expect(await rootNode.getChildren()).toEqual([infoChild]); @@ -259,7 +258,6 @@ describe("Unit Tests (Jest)", () => { undefined, profileOne ); - infoChild.id = "root.Use the search button to display data sets"; rootNode.contextValue = globals.DS_SESSION_CONTEXT; await expect(await rootNode.getChildren()).toEqual([infoChild]); }); diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index 9cb5357619..e11409d50a 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -15,7 +15,7 @@ import * as fs from "fs"; import * as zowe from "@zowe/cli"; import { DatasetTree } from "../../../src/dataset/DatasetTree"; import { ZoweDatasetNode } from "../../../src/dataset/ZoweDatasetNode"; -import { Gui, IZoweDatasetTreeNode, ProfilesCache, ValidProfileEnum } from "@zowe/zowe-explorer-api"; +import { DatasetSort, Gui, IZoweDatasetTreeNode, ProfilesCache, ValidProfileEnum } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../../../src/ZoweExplorerApiRegister"; import { Profiles } from "../../../src/Profiles"; import * as utils from "../../../src/utils/ProfilesUtils"; @@ -2696,3 +2696,58 @@ describe("Dataset Tree Unit Tests - Function initializeFavorites", () => { expect(() => testTree.initializeFavorites(log)).not.toThrow(); }); }); + +describe("Dataset Tree Unit Tests - Function sortBy", () => { + const testTree = new DatasetTree(); + const mocks = { + nodeDataChanged: jest.spyOn(DatasetTree.prototype, "nodeDataChanged"), + refreshElement: jest.spyOn(DatasetTree.prototype, "refreshElement"), + }; + const testNode = new ZoweDatasetNode("test", vscode.TreeItemCollapsibleState.Collapsed, null, createISession()); + + beforeEach(() => { + testNode.children = [ + { label: "A", stats: { user: "someUser", m4date: Date.now() } } as unknown as ZoweDatasetNode, + { label: "B", stats: { user: "anotherUser", m4date: Date.parse("2022-01-01T12:00:00") } } as unknown as ZoweDatasetNode, + { label: "C", stats: { user: "someUser", m4date: Date.parse("2022-03-15T16:30:00") } } as unknown as ZoweDatasetNode, + ]; + }); + + afterEach(() => { + mocks.nodeDataChanged.mockClear(); + mocks.refreshElement.mockClear(); + }); + + afterAll(() => { + mocks.nodeDataChanged.mockRestore(); + mocks.refreshElement.mockRestore(); + }); + + it("calls refreshElement if no children exist", () => { + testNode.children = []; + testTree.sortBy(DatasetSort.Name, testNode); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + expect(mocks.refreshElement).toHaveBeenCalledWith(testNode); + }); + + it("sorts by name", () => { + testTree.sortBy(DatasetSort.Name, testNode); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(testNode.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["A", "B", "C"]); + }); + + it("sorts by last modified date", () => { + testTree.sortBy(DatasetSort.LastModified, testNode); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(testNode.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "C", "A"]); + }); + + it("sorts by user ID", () => { + testTree.sortBy(DatasetSort.UserId, testNode); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(testNode.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "A", "C"]); + }); +}); diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts index bacbe6f822..0fc7719101 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts @@ -19,6 +19,7 @@ import { initDatasetProvider } from "../../../src/dataset/init"; import { Profiles } from "../../../src/Profiles"; import { IJestIt, ITestContext, processSubscriptions, spyOnSubscriptions } from "../../__common__/testUtils"; import { ZoweLogger } from "../../../src/utils/LoggerUtils"; +import { DatasetSort } from "@zowe/zowe-explorer-api"; describe("Test src/dataset/extension", () => { describe("initDatasetProvider", () => { @@ -45,6 +46,7 @@ describe("Test src/dataset/extension", () => { onDidChangeConfiguration: jest.fn(), getTreeView: jest.fn(), refreshElement: jest.fn(), + sortBy: jest.fn(), }; const commands: IJestIt[] = [ { @@ -250,6 +252,18 @@ describe("Test src/dataset/extension", () => { name: "zowe.ds.ssoLogout", mock: [{ spy: jest.spyOn(dsProvider, "ssoLogout"), arg: [test.value] }], }, + { + name: "zowe.ds.sortByName", + mock: [{ spy: jest.spyOn(dsProvider, "sortBy"), arg: [DatasetSort.Name, test.value] }], + }, + { + name: "zowe.ds.sortByModified", + mock: [{ spy: jest.spyOn(dsProvider, "sortBy"), arg: [DatasetSort.LastModified, test.value] }], + }, + { + name: "zowe.ds.sortByUserId", + mock: [{ spy: jest.spyOn(dsProvider, "sortBy"), arg: [DatasetSort.UserId, test.value] }], + }, { name: "onDidChangeConfiguration", mock: [{ spy: jest.spyOn(dsProvider, "onDidChangeConfiguration"), arg: [test.value] }], diff --git a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts index 3fa766a107..8ef7f6536b 100644 --- a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts @@ -176,6 +176,9 @@ async function createGlobalMocks() { "zowe.ds.enableValidation", "zowe.ds.ssoLogin", "zowe.ds.ssoLogout", + "zowe.ds.sortByName", + "zowe.ds.sortByModified", + "zowe.ds.sortByUserId", "zowe.uss.addFavorite", "zowe.uss.removeFavorite", "zowe.uss.addSession", diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 1b1dcc178f..29ff63b0c3 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -1398,16 +1398,6 @@ describe("sortjobsby function", () => { afterEach(() => { jest.restoreAllMocks(); }); - it("if there are no jobs in the zosmf level yet", async () => { - createGlobalMocks(); - const testtree = new ZosJobsProvider(); - //act - await jobActions.sortJobsBy(testtree.mSessionNodes[0], testtree, "jobname"); - await jobActions.sortJobsBy(testtree.mSessionNodes[0], testtree, "jobid"); - await jobActions.sortJobsBy(testtree.mSessionNodes[0], testtree, "retcode"); - //assert - expect(mocked(vscode.window.showInformationMessage)).toBeCalled(); - }); it("sort by name if same sort by increasing id", async () => { const globalMocks = createGlobalMocks(); const testtree = new ZosJobsProvider(); diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index e4b691a3c3..be15b95964 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1296,7 +1296,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { // If children nodes already exist, sort now and avoid extra refresh node.children.sort(ZoweDatasetNode.sortBy(method)); this.nodeDataChanged(node); diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 43dedc7c98..596308f59c 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 115; +export const COMMAND_COUNT = 116; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000; From 26df708eae61dcf22ec08802b466ad64bd7768ad Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Thu, 5 Oct 2023 09:26:59 -0400 Subject: [PATCH 19/47] ds: move 'Sort by' options to session context menu Signed-off-by: Trae Yelovich --- .../i18n/sample/package.i18n.json | 5 ++- packages/zowe-explorer/package.json | 42 ++++++++++++------- packages/zowe-explorer/package.nls.json | 5 ++- .../zowe-explorer/src/dataset/DatasetTree.ts | 17 ++++---- .../src/dataset/ZoweDatasetNode.ts | 8 +++- packages/zowe-explorer/src/dataset/init.ts | 19 +++++---- 6 files changed, 61 insertions(+), 35 deletions(-) diff --git a/packages/zowe-explorer/i18n/sample/package.i18n.json b/packages/zowe-explorer/i18n/sample/package.i18n.json index b543b506a6..dacb5d9c69 100644 --- a/packages/zowe-explorer/i18n/sample/package.i18n.json +++ b/packages/zowe-explorer/i18n/sample/package.i18n.json @@ -148,8 +148,9 @@ "createZoweSchema.reload.infoMessage": "Team Configuration file created. Location: {0}. \n Please reload your window.", "copyFile": "Copy", "pasteFile": "Paste", - "jobs.sortbyreturncode": "Sort by Return Code", - "jobs.sortbyid": "Sort by ID", + "jobs.sortByReturnCode": "Sort by Return Code", + "jobs.sortById": "Sort by ID", + "jobs.sortByDateSubmitted": "Sort by Date Submitted", "sortByName": "Sort by Name", "ds.sortByUserId": "Sort by User ID", "ds.sortByModified": "Sort by Date Modified" diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index 3e586fa038..b07cf75798 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -122,18 +122,23 @@ ], "commands": [ { - "command": "zowe.jobs.sortbyreturncode", - "title": "%jobs.sortbyreturncode%", + "command": "zowe.jobs.sortByReturnCode", + "title": "%jobs.sortByReturnCode%", "category": "Zowe Explorer" }, { - "command": "zowe.jobs.sortbyname", + "command": "zowe.jobs.sortByName", "title": "%sortByName%", "category": "Zowe Explorer" }, { - "command": "zowe.jobs.sortbyid", - "title": "%jobs.sortbyid%", + "command": "zowe.jobs.sortById", + "title": "%jobs.sortById%", + "category": "Zowe Explorer" + }, + { + "command": "zowe.jobs.sortByDateSubmitted", + "title": "%jobs.sortByDateSubmitted%", "category": "Zowe Explorer" }, { @@ -1103,19 +1108,19 @@ "group": "002_zowe_dsWorkspace@4" }, { - "when": "view == zowe.ds.explorer && viewItem =~ /^(pds).*/ && !listMultiSelection", - "command": "zowe.ds.sortByModified", - "group": "003_zowe_dsSort@1" + "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", + "command": "zowe.ds.sortByName", + "group": "003_zowe_dsSort@0" }, { - "when": "view == zowe.ds.explorer && viewItem =~ /^(pds).*/ && !listMultiSelection", - "command": "zowe.ds.sortByName", - "group": "003_zowe_dsSort@2" + "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", + "command": "zowe.ds.sortByModified", + "group": "003_zowe_dsSort@1" }, { - "when": "view == zowe.ds.explorer && viewItem =~ /^(pds).*/ && !listMultiSelection", + "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", "command": "zowe.ds.sortByUserId", - "group": "003_zowe_dsSort@3" + "group": "003_zowe_dsSort@2" }, { "when": "view == zowe.ds.explorer && viewItem =~ /^fileError.*/", @@ -1324,18 +1329,23 @@ }, { "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", - "command": "zowe.jobs.sortbyid", + "command": "zowe.jobs.sortById", "group": "000_zowe_jobsProfileModification@1" }, { "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", - "command": "zowe.jobs.sortbyname", + "command": "zowe.jobs.sortByName", "group": "000_zowe_jobsProfileModification@2" }, { "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", - "command": "zowe.jobs.sortbyreturncode", + "command": "zowe.jobs.sortByReturnCode", "group": "000_zowe_jobsProfileModification@2" + }, + { + "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", + "command": "zowe.jobs.sortByDateSubmitted", + "group": "000_zowe_jobsProfileModification@3" } ], "commandPalette": [ diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json index b543b506a6..dacb5d9c69 100644 --- a/packages/zowe-explorer/package.nls.json +++ b/packages/zowe-explorer/package.nls.json @@ -148,8 +148,9 @@ "createZoweSchema.reload.infoMessage": "Team Configuration file created. Location: {0}. \n Please reload your window.", "copyFile": "Copy", "pasteFile": "Paste", - "jobs.sortbyreturncode": "Sort by Return Code", - "jobs.sortbyid": "Sort by ID", + "jobs.sortByReturnCode": "Sort by Return Code", + "jobs.sortById": "Sort by ID", + "jobs.sortByDateSubmitted": "Sort by Date Submitted", "sortByName": "Sort by Name", "ds.sortByUserId": "Sort by User ID", "ds.sortByModified": "Sort by Date Modified" diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index be15b95964..b8679fa2f9 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1290,16 +1290,19 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { - // If children nodes already exist, sort now and avoid extra refresh - node.children.sort(ZoweDatasetNode.sortBy(method)); - this.nodeDataChanged(node); + // children nodes already exist, sort and repaint to avoid extra refresh + for (const c of node.children) { + if (contextually.isPds(c) && c.children) { + c.children.sort(ZoweDatasetNode.sortBy(method)); + this.nodeDataChanged(c); + } + } } else { this.refreshElement(node); } diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index 26d12a7904..f334917162 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -270,7 +270,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod this.children = this.children .concat(newChildren) .filter((c) => (c.label as string) in elementChildren) - .sort(ZoweDatasetNode.sortBy(this.sortMethod)); + .sort(ZoweDatasetNode.sortBy(this.getSessionNode().sortMethod ?? DatasetSort.Name)); } return this.children; @@ -278,6 +278,12 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod public static sortBy(method: DatasetSort): (a: IZoweDatasetTreeNode, b: IZoweDatasetTreeNode) => number { return (a, b): number => { + // we can only sort PDS members, compared nodes are at same level (same parent) + const aParent = a.getParent(); + if (aParent == null || !contextually.isPds(aParent)) { + return (a.label as string) < (b.label as string) ? -1 : 1; + } + switch (method) { case DatasetSort.LastModified: return a.stats?.m4date < b.stats?.m4date ? -1 : 1; diff --git a/packages/zowe-explorer/src/dataset/init.ts b/packages/zowe-explorer/src/dataset/init.ts index 9668ee1040..61275cb3de 100644 --- a/packages/zowe-explorer/src/dataset/init.ts +++ b/packages/zowe-explorer/src/dataset/init.ts @@ -199,17 +199,22 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogin", (node: IZoweTreeNode) => datasetProvider.ssoLogin(node))); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogout", (node: IZoweTreeNode) => datasetProvider.ssoLogout(node))); context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.sortByName", (node: IZoweDatasetTreeNode): void => datasetProvider.sortBy(DatasetSort.Name, node)) + vscode.commands.registerCommand("zowe.ds.sortByName", (node: IZoweDatasetTreeNode): void => { + node.sortMethod = DatasetSort.Name; + datasetProvider.sortPdsBy(node.sortMethod, node); + }) ); context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.sortByModified", (node: IZoweDatasetTreeNode): void => - datasetProvider.sortBy(DatasetSort.LastModified, node) - ) + vscode.commands.registerCommand("zowe.ds.sortByModified", (node: IZoweDatasetTreeNode): void => { + node.sortMethod = DatasetSort.LastModified; + datasetProvider.sortPdsBy(node.sortMethod, node); + }) ); context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.sortByUserId", (node: IZoweDatasetTreeNode): void => - datasetProvider.sortBy(DatasetSort.UserId, node) - ) + vscode.commands.registerCommand("zowe.ds.sortByUserId", (node: IZoweDatasetTreeNode): void => { + node.sortMethod = DatasetSort.UserId; + datasetProvider.sortPdsBy(node.sortMethod, node); + }) ); context.subscriptions.push( vscode.workspace.onDidChangeConfiguration(async (e) => { From 0f48273a37e6e9a1111d8437c463fcf1c662f3ae Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Thu, 5 Oct 2023 10:15:50 -0400 Subject: [PATCH 20/47] ds: Change sort UX to single menu option Signed-off-by: Trae Yelovich --- .../i18n/sample/package.i18n.json | 7 +++-- .../sample/src/dataset/DatasetTree.i18n.json | 4 +++ packages/zowe-explorer/package.json | 26 +++--------------- packages/zowe-explorer/package.nls.json | 7 +++-- .../zowe-explorer/src/dataset/DatasetTree.ts | 27 +++++++++++++++++-- packages/zowe-explorer/src/dataset/init.ts | 21 ++------------- 6 files changed, 44 insertions(+), 48 deletions(-) diff --git a/packages/zowe-explorer/i18n/sample/package.i18n.json b/packages/zowe-explorer/i18n/sample/package.i18n.json index dacb5d9c69..faa6e36edd 100644 --- a/packages/zowe-explorer/i18n/sample/package.i18n.json +++ b/packages/zowe-explorer/i18n/sample/package.i18n.json @@ -152,6 +152,9 @@ "jobs.sortById": "Sort by ID", "jobs.sortByDateSubmitted": "Sort by Date Submitted", "sortByName": "Sort by Name", - "ds.sortByUserId": "Sort by User ID", - "ds.sortByModified": "Sort by Date Modified" + "ds.selectSortOpt": "Select a sorting option for PDS members in {0}.", + "ds.sortBy": "Sort by...", + "ds.sortByName": "Name", + "ds.sortByModified": "Date Modified", + "ds.sortByUserId": "User ID" } diff --git a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json index 53eff9a390..37581454c9 100644 --- a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json @@ -26,5 +26,9 @@ "renameDataSet.log.debug": "Renaming data set ", "renameDataSet.error": "Unable to rename data set:", "dataset.validation": "Enter a valid data set name.", + "ds.sortByName": "Name", + "ds.sortByModified": "Date Modified", + "ds.sortByUserId": "User ID", + "ds.selectSortOpt": "Select a sorting option for PDS members in {0}.", "defaultFilterPrompt.option.prompt.search": "$(plus) Create a new filter. For example: HLQ.*, HLQ.aaa.bbb, HLQ.ccc.ddd(member)" } diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index b07cf75798..3611339555 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -287,18 +287,8 @@ "category": "Zowe Explorer" }, { - "command": "zowe.ds.sortByName", - "title": "%sortByName%", - "category": "Zowe Explorer" - }, - { - "command": "zowe.ds.sortByModified", - "title": "%ds.sortByModified%", - "category": "Zowe Explorer" - }, - { - "command": "zowe.ds.sortByUserId", - "title": "%ds.sortByUserId%", + "command": "zowe.ds.sortBy", + "title": "%ds.sortBy%", "category": "Zowe Explorer" }, { @@ -1109,19 +1099,9 @@ }, { "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", - "command": "zowe.ds.sortByName", + "command": "zowe.ds.sortBy", "group": "003_zowe_dsSort@0" }, - { - "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", - "command": "zowe.ds.sortByModified", - "group": "003_zowe_dsSort@1" - }, - { - "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", - "command": "zowe.ds.sortByUserId", - "group": "003_zowe_dsSort@2" - }, { "when": "view == zowe.ds.explorer && viewItem =~ /^fileError.*/", "command": "zowe.ds.showFileErrorDetails", diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json index dacb5d9c69..faa6e36edd 100644 --- a/packages/zowe-explorer/package.nls.json +++ b/packages/zowe-explorer/package.nls.json @@ -152,6 +152,9 @@ "jobs.sortById": "Sort by ID", "jobs.sortByDateSubmitted": "Sort by Date Submitted", "sortByName": "Sort by Name", - "ds.sortByUserId": "Sort by User ID", - "ds.sortByModified": "Sort by Date Modified" + "ds.selectSortOpt": "Select a sorting option for PDS members in {0}.", + "ds.sortBy": "Sort by...", + "ds.sortByName": "Name", + "ds.sortByModified": "Date Modified", + "ds.sortByUserId": "User ID" } diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index b8679fa2f9..fbe9181ac4 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1294,12 +1294,35 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree { + const options = [localize("ds.sortByName", "Name"), localize("ds.sortByModified", "Date Modified"), localize("ds.sortByUserId", "User ID")]; + + const selection = await Gui.showQuickPick(options, { + placeHolder: localize("ds.selectSortOpt", "Select a sorting option for PDS members in {0}.", node.label as string), + }); + if (selection == null) { + return; + } + + switch (selection) { + case options[0]: + node.sortMethod = DatasetSort.Name; + break; + case options[1]: + node.sortMethod = DatasetSort.LastModified; + break; + case options[2]: + node.sortMethod = DatasetSort.UserId; + break; + default: + return; + } + if (node.children != null && node.children.length > 0) { // children nodes already exist, sort and repaint to avoid extra refresh for (const c of node.children) { if (contextually.isPds(c) && c.children) { - c.children.sort(ZoweDatasetNode.sortBy(method)); + c.children.sort(ZoweDatasetNode.sortBy(node.sortMethod)); this.nodeDataChanged(c); } } diff --git a/packages/zowe-explorer/src/dataset/init.ts b/packages/zowe-explorer/src/dataset/init.ts index 61275cb3de..a1ca9e9e3b 100644 --- a/packages/zowe-explorer/src/dataset/init.ts +++ b/packages/zowe-explorer/src/dataset/init.ts @@ -13,7 +13,7 @@ import * as globals from "../globals"; import * as vscode from "vscode"; import * as dsActions from "./actions"; import * as refreshActions from "../shared/refresh"; -import { IZoweDatasetTreeNode, IZoweTreeNode, IZoweTree, DatasetSort } from "@zowe/zowe-explorer-api"; +import { IZoweDatasetTreeNode, IZoweTreeNode, IZoweTree } from "@zowe/zowe-explorer-api"; import { Profiles } from "../Profiles"; import { createDatasetTree } from "./DatasetTree"; import { ZoweDatasetNode } from "./ZoweDatasetNode"; @@ -198,24 +198,7 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro ); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogin", (node: IZoweTreeNode) => datasetProvider.ssoLogin(node))); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogout", (node: IZoweTreeNode) => datasetProvider.ssoLogout(node))); - context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.sortByName", (node: IZoweDatasetTreeNode): void => { - node.sortMethod = DatasetSort.Name; - datasetProvider.sortPdsBy(node.sortMethod, node); - }) - ); - context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.sortByModified", (node: IZoweDatasetTreeNode): void => { - node.sortMethod = DatasetSort.LastModified; - datasetProvider.sortPdsBy(node.sortMethod, node); - }) - ); - context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.sortByUserId", (node: IZoweDatasetTreeNode): void => { - node.sortMethod = DatasetSort.UserId; - datasetProvider.sortPdsBy(node.sortMethod, node); - }) - ); + context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.sortBy", (node: IZoweDatasetTreeNode) => datasetProvider.sortPdsBy(node))); context.subscriptions.push( vscode.workspace.onDidChangeConfiguration(async (e) => { await datasetProvider.onDidChangeConfiguration(e); From 67411ef892e658a75ac8c559c38132ef4f10a168 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Thu, 5 Oct 2023 10:36:38 -0400 Subject: [PATCH 21/47] ds: Update sorting UX to use inline icon beside session Signed-off-by: Trae Yelovich --- packages/zowe-explorer/package.json | 44 ++++++++++++++++++++----- packages/zowe-explorer/package.nls.json | 2 +- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index 3611339555..f26922f17b 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -289,7 +289,8 @@ { "command": "zowe.ds.sortBy", "title": "%ds.sortBy%", - "category": "Zowe Explorer" + "category": "Zowe Explorer", + "icon": "$(list-ordered)" }, { "command": "zowe.cmd.deleteProfile", @@ -979,13 +980,43 @@ }, { "when": "view == zowe.uss.explorer && viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", - "command": "zowe.profileManagement", - "group": "099_zowe_ussProfileAuthentication@99" + "command": "zowe.promptCredentials", + "group": "098_zowe_ussProfileAuthentication@3" + }, + { + "when": "view == zowe.uss.explorer && viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", + "command": "zowe.uss.ssoLogin", + "group": "098_zowe_ussProfileAuthentication@4" + }, + { + "when": "view == zowe.uss.explorer && viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", + "command": "zowe.uss.ssoLogout", + "group": "098_zowe_ussProfileAuthentication@5" + }, + { + "when": "viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", + "command": "zowe.uss.editSession", + "group": "099_zowe_ussProfileModification@1" + }, + { + "when": "viewItem =~ /^(?!.*_fav.*)ussSession.*/", + "command": "zowe.uss.removeSession", + "group": "099_zowe_ussProfileModification@98" + }, + { + "when": "viewItem =~ /^(?!.*_fav.*)ussSession.*/ && !listMultiSelection", + "command": "zowe.uss.deleteProfile", + "group": "099_zowe_ussProfileModification@99" + }, + { + "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", + "command": "zowe.ds.sortBy", + "group": "inline@0" }, { "when": "view == zowe.ds.explorer && viewItem =~ /^(?!.*_fav.*)session.*/ && !listMultiSelection", "command": "zowe.ds.pattern", - "group": "inline" + "group": "inline@1" }, { "when": "view == zowe.ds.explorer && viewItem =~ /^(pds|ds|migr).*_fav.*/", @@ -1097,11 +1128,6 @@ "command": "zowe.ds.removeFavProfile", "group": "002_zowe_dsWorkspace@4" }, - { - "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", - "command": "zowe.ds.sortBy", - "group": "003_zowe_dsSort@0" - }, { "when": "view == zowe.ds.explorer && viewItem =~ /^fileError.*/", "command": "zowe.ds.showFileErrorDetails", diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json index faa6e36edd..0665344b8a 100644 --- a/packages/zowe-explorer/package.nls.json +++ b/packages/zowe-explorer/package.nls.json @@ -153,7 +153,7 @@ "jobs.sortByDateSubmitted": "Sort by Date Submitted", "sortByName": "Sort by Name", "ds.selectSortOpt": "Select a sorting option for PDS members in {0}.", - "ds.sortBy": "Sort by...", + "ds.sortBy": "Sort PDS members...", "ds.sortByName": "Name", "ds.sortByModified": "Date Modified", "ds.sortByUserId": "User ID" From 59a565fd0b7ec6b460028c8209f6136c596e9600 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Thu, 5 Oct 2023 16:21:59 -0400 Subject: [PATCH 22/47] fix(ds, jobs): Add session-level and per-PDS sort, cache job sort method - Converted "Sort jobs" feature to use a quick pick and icon - Added "selected" checkmark beside active sorting method for DS & jobs - Optimized job sorting by avoiding refresh if children exist - Adjusted localized strings for jobs and DS sorting Signed-off-by: Trae Yelovich --- .../src/tree/IZoweTreeNode.ts | 27 +++++--- .../i18n/sample/package.i18n.json | 12 +--- .../sample/src/dataset/DatasetTree.i18n.json | 7 +- .../i18n/sample/src/dataset/utils.i18n.json | 5 ++ .../i18n/sample/src/job/actions.i18n.json | 1 + .../i18n/sample/src/job/utils.i18n.json | 6 ++ packages/zowe-explorer/package.json | 13 ++-- packages/zowe-explorer/package.nls.json | 15 ++-- .../zowe-explorer/src/dataset/DatasetTree.ts | 69 +++++++++++-------- .../src/dataset/ZoweDatasetNode.ts | 28 +++++--- packages/zowe-explorer/src/dataset/init.ts | 4 +- packages/zowe-explorer/src/dataset/utils.ts | 14 ++++ .../zowe-explorer/src/job/ZosJobsProvider.ts | 13 +++- packages/zowe-explorer/src/job/ZoweJobNode.ts | 26 +++---- packages/zowe-explorer/src/job/actions.ts | 30 ++++---- packages/zowe-explorer/src/job/init.ts | 12 +--- packages/zowe-explorer/src/job/utils.ts | 24 +++++++ 17 files changed, 194 insertions(+), 112 deletions(-) create mode 100644 packages/zowe-explorer/i18n/sample/src/dataset/utils.i18n.json create mode 100644 packages/zowe-explorer/i18n/sample/src/job/utils.i18n.json diff --git a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts index d2ff45e178..6a997e9719 100644 --- a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts @@ -20,17 +20,24 @@ export enum NodeAction { Download = "download", } -export enum DatasetSort { - LastModified, - Name, - UserId, -} - export type DatasetStats = { user: string; m4date: Date; }; +export enum DatasetSortOpts { + Name, + LastModified, + UserId, +} + +export enum JobSortOpts { + Id, + DateSubmitted, + Name, + ReturnCode, +} + /** * The base interface for Zowe tree nodes that are implemented by vscode.TreeItem. * @@ -89,6 +96,10 @@ export interface IZoweTreeNode { * whether the node was double-clicked */ wasDoubleClicked?: boolean; + /** + * Sorting method for this node's children + */ + sortMethod?: DatasetSortOpts | JobSortOpts; /** * Retrieves the node label */ @@ -146,10 +157,6 @@ export interface IZoweDatasetTreeNode extends IZoweTreeNode { * Search criteria for a Dataset member search */ memberPattern?: string; - /** - * Sort members of a node by the given sorting method - */ - sortMethod?: DatasetSort; /** * Additional statistics about this data set */ diff --git a/packages/zowe-explorer/i18n/sample/package.i18n.json b/packages/zowe-explorer/i18n/sample/package.i18n.json index faa6e36edd..846aae972f 100644 --- a/packages/zowe-explorer/i18n/sample/package.i18n.json +++ b/packages/zowe-explorer/i18n/sample/package.i18n.json @@ -148,13 +148,7 @@ "createZoweSchema.reload.infoMessage": "Team Configuration file created. Location: {0}. \n Please reload your window.", "copyFile": "Copy", "pasteFile": "Paste", - "jobs.sortByReturnCode": "Sort by Return Code", - "jobs.sortById": "Sort by ID", - "jobs.sortByDateSubmitted": "Sort by Date Submitted", - "sortByName": "Sort by Name", - "ds.selectSortOpt": "Select a sorting option for PDS members in {0}.", - "ds.sortBy": "Sort by...", - "ds.sortByName": "Name", - "ds.sortByModified": "Date Modified", - "ds.sortByUserId": "User ID" + "jobs.sortBy": "Sort jobs...", + "jobs.selectSortOpt": "Select a sorting option for jobs in {0}", + "ds.sortBy": "Sort PDS members..." } diff --git a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json index 37581454c9..ffdd605c77 100644 --- a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json @@ -26,9 +26,8 @@ "renameDataSet.log.debug": "Renaming data set ", "renameDataSet.error": "Unable to rename data set:", "dataset.validation": "Enter a valid data set name.", - "ds.sortByName": "Name", - "ds.sortByModified": "Date Modified", - "ds.sortByUserId": "User ID", - "ds.selectSortOpt": "Select a sorting option for PDS members in {0}.", + "ds.allPdsSort": "all PDS members in {0}", + "ds.singlePdsSort": "the PDS members in {0}", + "ds.selectSortOpt": "Select a sorting option for {0}", "defaultFilterPrompt.option.prompt.search": "$(plus) Create a new filter. For example: HLQ.*, HLQ.aaa.bbb, HLQ.ccc.ddd(member)" } diff --git a/packages/zowe-explorer/i18n/sample/src/dataset/utils.i18n.json b/packages/zowe-explorer/i18n/sample/src/dataset/utils.i18n.json new file mode 100644 index 0000000000..55ee3b4e20 --- /dev/null +++ b/packages/zowe-explorer/i18n/sample/src/dataset/utils.i18n.json @@ -0,0 +1,5 @@ +{ + "ds.sortByName": "Name", + "ds.sortByModified": "Date Modified", + "ds.sortByUserId": "User ID" +} diff --git a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json index 816a92caba..51671482a8 100644 --- a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json @@ -22,6 +22,7 @@ "cancelJobs.notCancelled": "The job was not cancelled.", "cancelJobs.failed": "One or more jobs failed to cancel: {0}", "cancelJobs.succeeded": "Cancelled selected jobs successfully.", + "jobs.selectSortOpt": "Select a sorting option for jobs in {0}", "filterJobs.message": "Use the search button to display jobs", "filterJobs.prompt.message": "Enter local filter..." } diff --git a/packages/zowe-explorer/i18n/sample/src/job/utils.i18n.json b/packages/zowe-explorer/i18n/sample/src/job/utils.i18n.json new file mode 100644 index 0000000000..79f5b24932 --- /dev/null +++ b/packages/zowe-explorer/i18n/sample/src/job/utils.i18n.json @@ -0,0 +1,6 @@ +{ + "jobs.sortById": "Job ID", + "jobs.sortByDateSubmitted": "Date Submitted", + "jobs.sortByName": "Job Name", + "jobs.sortByReturnCode": "Return Code" +} diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index f26922f17b..774260dc6a 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -1141,7 +1141,7 @@ { "when": "view == zowe.ds.explorer && viewItem =~ /^(ds.*|^pds.*)/", "command": "zowe.ds.hMigrateDataSet", - "group": "099_zowe_dsModification@0" + "group": "z_zowe_dsModification@0" }, { "when": "view == zowe.ds.explorer && viewItem =~ /^ds.*/", @@ -1166,7 +1166,7 @@ { "when": "view == zowe.ds.explorer && viewItem =~ /^pds.*/ && !listMultiSelection", "command": "zowe.ds.renameDataSet", - "group": "099_zowe_dsModification@2" + "group": "z_zowe_dsModification@2" }, { "when": "view == zowe.ds.explorer && viewItem =~ /^ds.*/", @@ -1181,7 +1181,7 @@ { "when": "view == zowe.ds.explorer && viewItem =~ /^pds.*/", "command": "zowe.ds.deleteDataset", - "group": "099_zowe_dsModification@5" + "group": "z_zowe_dsModification@5" }, { "when": "view == zowe.ds.explorer && viewItem =~ /^migr.*/", @@ -1193,10 +1193,15 @@ "command": "zowe.profileManagement", "group": "099_zowe_dsProfileAuthentication@99" }, + { + "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", + "command": "zowe.jobs.sortBy", + "group": "inline@0" + }, { "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)server.*/ && !listMultiSelection", "command": "zowe.jobs.search", - "group": "inline" + "group": "inline@1" }, { "when": "view == zowe.jobs.explorer && viewItem =~ /^(?!.*_fav.*)job.*/", diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json index 0665344b8a..18b81148a0 100644 --- a/packages/zowe-explorer/package.nls.json +++ b/packages/zowe-explorer/package.nls.json @@ -148,13 +148,10 @@ "createZoweSchema.reload.infoMessage": "Team Configuration file created. Location: {0}. \n Please reload your window.", "copyFile": "Copy", "pasteFile": "Paste", - "jobs.sortByReturnCode": "Sort by Return Code", - "jobs.sortById": "Sort by ID", - "jobs.sortByDateSubmitted": "Sort by Date Submitted", - "sortByName": "Sort by Name", - "ds.selectSortOpt": "Select a sorting option for PDS members in {0}.", - "ds.sortBy": "Sort PDS members...", - "ds.sortByName": "Name", - "ds.sortByModified": "Date Modified", - "ds.sortByUserId": "User ID" + "jobs.sortBy": "Sort jobs...", + "ds.allPdsSort": "all PDS members in {0}", + "ds.singlePdsSort": "the PDS members in {0}", + "ds.selectSortOpt": "Select a sorting option for {0}", + "jobs.selectSortOpt": "Select a sorting option for jobs in {0}", + "ds.sortBy": "Sort PDS members..." } diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index fbe9181ac4..244508bfed 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -15,15 +15,14 @@ import * as nls from "vscode-nls"; import * as globals from "../globals"; import * as dsActions from "./actions"; import { - Gui, DataSetAllocTemplate, + Gui, ValidProfileEnum, IZoweTree, IZoweDatasetTreeNode, PersistenceSchemaEnum, NodeInteraction, IZoweTreeNode, - DatasetSort, } from "@zowe/zowe-explorer-api"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; @@ -37,7 +36,7 @@ import * as contextually from "../shared/context"; import { resetValidationSettings } from "../shared/actions"; import { closeOpenedTextFile } from "../utils/workspace"; import { IDataSet, IListOptions, imperative } from "@zowe/cli"; -import { validateDataSetName, validateMemberName } from "./utils"; +import { DATASET_SORT_OPTS, validateDataSetName, validateMemberName } from "./utils"; import { SettingsConfig } from "../utils/SettingsConfig"; import { ZoweLogger } from "../utils/LoggerUtils"; import { TreeViewUtils } from "../utils/TreeViewUtils"; @@ -1290,44 +1289,56 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree { - const options = [localize("ds.sortByName", "Name"), localize("ds.sortByModified", "Date Modified"), localize("ds.sortByUserId", "User ID")]; - - const selection = await Gui.showQuickPick(options, { - placeHolder: localize("ds.selectSortOpt", "Select a sorting option for PDS members in {0}.", node.label as string), - }); + public async sortPdsMembers(node: IZoweDatasetTreeNode): Promise { + const isSession = contextually.isSession(node); + + const specifier = isSession + ? localize("ds.allPdsSort", "all PDS members in {0}", node.label as string) + : localize("ds.singlePdsSort", "the PDS members in {0}", node.label as string); + const selection = await Gui.showQuickPick( + DATASET_SORT_OPTS.map((sortOpt, i) => (node.sortMethod === i ? `${sortOpt} $(check)` : sortOpt)), + { + placeHolder: localize("ds.selectSortOpt", "Select a sorting option for {0}", specifier), + } + ); if (selection == null) { return; } - switch (selection) { - case options[0]: - node.sortMethod = DatasetSort.Name; - break; - case options[1]: - node.sortMethod = DatasetSort.LastModified; - break; - case options[2]: - node.sortMethod = DatasetSort.UserId; - break; - default: - return; + const sortMethod = DATASET_SORT_OPTS.indexOf(selection); + if (sortMethod == -1) { + return; } - if (node.children != null && node.children.length > 0) { - // children nodes already exist, sort and repaint to avoid extra refresh - for (const c of node.children) { - if (contextually.isPds(c) && c.children) { - c.children.sort(ZoweDatasetNode.sortBy(node.sortMethod)); - this.nodeDataChanged(c); + node.sortMethod = sortMethod; + + if (isSession) { + // if a session was selected, apply this sort to ALL PDS members + if (node.children != null && node.children.length > 0) { + // children nodes already exist, sort and repaint to avoid extra refresh + for (const c of node.children) { + if (contextually.isPds(c) && c.children) { + c.sortMethod = sortMethod; + c.children.sort(ZoweDatasetNode.sortBy(sortMethod)); + this.nodeDataChanged(c); + } } + } else { + this.refreshElement(node); } } else { - this.refreshElement(node); + // Only sort the PDS members for this PDS + if (node.children != null && node.children.length > 0) { + // children nodes already exist, sort and repaint to avoid extra refresh + node.children.sort(ZoweDatasetNode.sortBy(sortMethod)); + this.nodeDataChanged(node); + } else { + this.refreshElement(node); + } } } } diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index f334917162..2cc4b1083d 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -13,13 +13,14 @@ import * as zowe from "@zowe/cli"; import * as vscode from "vscode"; import * as globals from "../globals"; import { errorHandling } from "../utils/ProfilesUtils"; -import { DatasetSort, DatasetStats, Gui, NodeAction, IZoweDatasetTreeNode, ZoweTreeNode } from "@zowe/zowe-explorer-api"; +import { DatasetSortOpts, DatasetStats, Gui, NodeAction, IZoweDatasetTreeNode, ZoweTreeNode } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; import { getIconByNode } from "../generators/icons"; import * as contextually from "../shared/context"; import * as nls from "vscode-nls"; import { Profiles } from "../Profiles"; import { ZoweLogger } from "../utils/LoggerUtils"; + // Set up localization nls.config({ messageFormat: nls.MessageFormat.bundle, @@ -43,8 +44,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod public errorDetails: zowe.imperative.ImperativeError; public ongoingActions: Record> = {}; public wasDoubleClicked: boolean = false; - public sortMethod: DatasetSort = DatasetSort.Name; public stats: DatasetStats; + public sortMethod = DatasetSortOpts.Name; /** * Creates an instance of ZoweDatasetNode @@ -267,31 +268,38 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod .filter((label) => this.children.find((c) => (c.label as string) === label) == null) .map((label) => elementChildren[label]); + const sortMethod = (this.sortMethod ? this.sortMethod : this.getSessionNode().sortMethod) as DatasetSortOpts; + this.children = this.children .concat(newChildren) .filter((c) => (c.label as string) in elementChildren) - .sort(ZoweDatasetNode.sortBy(this.getSessionNode().sortMethod ?? DatasetSort.Name)); + .sort(ZoweDatasetNode.sortBy(sortMethod ?? DatasetSortOpts.Name)); } return this.children; } - public static sortBy(method: DatasetSort): (a: IZoweDatasetTreeNode, b: IZoweDatasetTreeNode) => number { + /** + * Returns a sorting function based on the given sorting method. + * If the nodes are not PDS members, it will simply sort by name. + * @param method The sorting method to use + * @returns A function that sorts 2 nodes based on the given sorting method + */ + public static sortBy(method: DatasetSortOpts): (a: IZoweDatasetTreeNode, b: IZoweDatasetTreeNode) => number { return (a, b): number => { - // we can only sort PDS members, compared nodes are at same level (same parent) const aParent = a.getParent(); if (aParent == null || !contextually.isPds(aParent)) { return (a.label as string) < (b.label as string) ? -1 : 1; } switch (method) { - case DatasetSort.LastModified: - return a.stats?.m4date < b.stats?.m4date ? -1 : 1; - case DatasetSort.UserId: - return a.stats?.user < b.stats?.user ? -1 : 1; - case DatasetSort.Name: + case DatasetSortOpts.Name: default: return (a.label as string) < (b.label as string) ? -1 : 1; + case DatasetSortOpts.LastModified: + return a.stats?.m4date < b.stats?.m4date ? -1 : 1; + case DatasetSortOpts.UserId: + return a.stats?.user < b.stats?.user ? -1 : 1; } }; } diff --git a/packages/zowe-explorer/src/dataset/init.ts b/packages/zowe-explorer/src/dataset/init.ts index a1ca9e9e3b..dfb156ac98 100644 --- a/packages/zowe-explorer/src/dataset/init.ts +++ b/packages/zowe-explorer/src/dataset/init.ts @@ -198,7 +198,9 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro ); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogin", (node: IZoweTreeNode) => datasetProvider.ssoLogin(node))); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogout", (node: IZoweTreeNode) => datasetProvider.ssoLogout(node))); - context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.sortBy", (node: IZoweDatasetTreeNode) => datasetProvider.sortPdsBy(node))); + context.subscriptions.push( + vscode.commands.registerCommand("zowe.ds.sortBy", async (node: IZoweDatasetTreeNode) => datasetProvider.sortPdsMembers(node)) + ); context.subscriptions.push( vscode.workspace.onDidChangeConfiguration(async (e) => { await datasetProvider.onDidChangeConfiguration(e); diff --git a/packages/zowe-explorer/src/dataset/utils.ts b/packages/zowe-explorer/src/dataset/utils.ts index 1debf45d39..c7445dce71 100644 --- a/packages/zowe-explorer/src/dataset/utils.ts +++ b/packages/zowe-explorer/src/dataset/utils.ts @@ -10,9 +10,23 @@ */ import * as globals from "../globals"; +import * as nls from "vscode-nls"; import { IZoweNodeType } from "@zowe/zowe-explorer-api"; import { ZoweLogger } from "../utils/LoggerUtils"; +// Set up localization +nls.config({ + messageFormat: nls.MessageFormat.bundle, + bundleFormat: nls.BundleFormat.standalone, +})(); +const localize: nls.LocalizeFunc = nls.loadMessageBundle(); + +export const DATASET_SORT_OPTS = [ + localize("ds.sortByName", "Name"), + localize("ds.sortByModified", "Date Modified"), + localize("ds.sortByUserId", "User ID"), +]; + export function getProfileAndDataSetName(node: IZoweNodeType): { profileName: string; dataSetName: string; diff --git a/packages/zowe-explorer/src/job/ZosJobsProvider.ts b/packages/zowe-explorer/src/job/ZosJobsProvider.ts index 3333724e6f..2747b322ff 100644 --- a/packages/zowe-explorer/src/job/ZosJobsProvider.ts +++ b/packages/zowe-explorer/src/job/ZosJobsProvider.ts @@ -12,7 +12,7 @@ import * as vscode from "vscode"; import * as globals from "../globals"; import { IJob, imperative } from "@zowe/cli"; -import { Gui, ValidProfileEnum, IZoweTree, IZoweJobTreeNode, PersistenceSchemaEnum, NodeInteraction } from "@zowe/zowe-explorer-api"; +import { Gui, ValidProfileEnum, IZoweTree, IZoweJobTreeNode, PersistenceSchemaEnum, NodeInteraction, JobSortOpts } from "@zowe/zowe-explorer-api"; import { FilterItem, errorHandling } from "../utils/ProfilesUtils"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; @@ -1122,6 +1122,17 @@ export class ZosJobsProvider extends ZoweTreeProvider implements IZoweTree this.children.find((ch) => ch.label === c.label) == null); + const sortMethod = contextually.isSession(this) ? this.sortMethod : JobSortOpts.Id; // Remove any children that are no longer present in the built record this.children = this.children .concat(newChildren) .filter((ch) => Object.values(elementChildren).find((recordCh) => recordCh.label === ch.label) != null) - .sort((a, b) => Job.sortJobs(a, b)); + .sort(Job.sortJobs(sortMethod)); this.dirty = false; return this.children; } - public static sortJobs(a: IZoweJobTreeNode, b: IZoweJobTreeNode): number { - if (a.job.jobid > b.job.jobid) { - return 1; - } - - if (a.job.jobid < b.job.jobid) { - return -1; - } - - return 0; + public static sortJobs(method: JobSortOpts): (x: IZoweJobTreeNode, y: IZoweJobTreeNode) => number { + return (x, y) => { + const keyToSortBy = JOB_SORT_KEYS[method]; + if (keyToSortBy !== "jobid" && x["job"][keyToSortBy] == y["job"][keyToSortBy]) { + return x["job"]["jobid"] > y["job"]["jobid"] ? 1 : -1; + } else { + return x["job"][keyToSortBy] > y["job"][keyToSortBy] ? 1 : -1; + } + }; } public getSessionNode(): IZoweJobTreeNode { diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 06d36e3b35..f8bb17b8ba 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -21,6 +21,7 @@ import SpoolProvider, { encodeJobFile, getSpoolFiles, matchSpool } from "../Spoo import { ZoweLogger } from "../utils/LoggerUtils"; import { getDefaultUri } from "../shared/utils"; import { ZosJobsProvider } from "./ZosJobsProvider"; +import { JOB_SORT_OPTS } from "./utils"; // Set up localization nls.config({ @@ -529,19 +530,24 @@ export async function cancelJobs(jobsProvider: IZoweTree, node await Gui.showMessage(localize("cancelJobs.succeeded", "Cancelled selected jobs successfully.")); } } -export function sortJobsBy(session: IZoweJobTreeNode, jobsProvider: ZosJobsProvider, key: keyof zowe.IJob): void { - if (session.children != null) { - session.children.sort((x, y) => { - if (key !== "jobid" && x["job"][key] == y["job"][key]) { - return x["job"]["jobid"] > y["job"]["jobid"] ? 1 : -1; - } else { - return x["job"][key] > y["job"][key] ? 1 : -1; - } - }); - jobsProvider.nodeDataChanged(session); - } else { - jobsProvider.refreshElement(session); +export async function sortJobs(session: IZoweJobTreeNode, jobsProvider: ZosJobsProvider): Promise { + const selection = await Gui.showQuickPick( + JOB_SORT_OPTS.map((sortOpt, i) => (i === session.sortMethod ? `${sortOpt} $(check)` : sortOpt)), + { + placeHolder: localize("jobs.selectSortOpt", "Select a sorting option for jobs in {0}", session.label as string), + } + ); + if (selection == null) { + return; } + + const optIndex = JOB_SORT_OPTS.indexOf(selection); + if (optIndex == -1) { + return; + } + + session.sortMethod = optIndex; + jobsProvider.sortBy(session); } export async function filterJobs(jobsProvider: IZoweTree, job: IZoweJobTreeNode): Promise { diff --git a/packages/zowe-explorer/src/job/init.ts b/packages/zowe-explorer/src/job/init.ts index 3842ff395b..cca4b1f913 100644 --- a/packages/zowe-explorer/src/job/init.ts +++ b/packages/zowe-explorer/src/job/init.ts @@ -171,17 +171,7 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis await jobActions.cancelJobs(jobsProvider, getSelectedNodeList(node, nodeList)); }) ); - context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.sortbyname", (job) => jobActions.sortJobsBy(job, jobsProvider, "jobname"))); - context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.sortbyid", (job) => jobActions.sortJobsBy(job, jobsProvider, "jobid"))); - context.subscriptions.push( - vscode.commands.registerCommand("zowe.jobs.sortbyreturncode", (job) => jobActions.sortJobsBy(job, jobsProvider, "retcode")) - ); - context.subscriptions.push( - vscode.commands.registerCommand("zowe.jobs.filterJobs", async (job) => { - await jobActions.filterJobs(jobsProvider, job); - }) - ); - + context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.sortBy", async (job) => jobActions.sortJobs(job, jobsProvider))); initSubscribers(context, jobsProvider); return jobsProvider; } diff --git a/packages/zowe-explorer/src/job/utils.ts b/packages/zowe-explorer/src/job/utils.ts index 9a5f3d7611..9e9db05d3c 100644 --- a/packages/zowe-explorer/src/job/utils.ts +++ b/packages/zowe-explorer/src/job/utils.ts @@ -9,8 +9,32 @@ * */ +import { JobSortOpts } from "@zowe/zowe-explorer-api"; import { ZoweLogger } from "../utils/LoggerUtils"; import { FilterItem } from "../utils/ProfilesUtils"; +import * as nls from "vscode-nls"; +import { IJob } from "@zowe/cli"; + +// Set up localization +nls.config({ + messageFormat: nls.MessageFormat.bundle, + bundleFormat: nls.BundleFormat.standalone, +})(); +const localize: nls.LocalizeFunc = nls.loadMessageBundle(); + +export const JOB_SORT_OPTS = [ + localize("jobs.sortById", "Job ID"), + localize("jobs.sortByDateSubmitted", "Date Submitted"), + localize("jobs.sortByName", "Job Name"), + localize("jobs.sortByReturnCode", "Return Code"), +]; + +export const JOB_SORT_KEYS: Record = { + [JobSortOpts.Id]: "jobid", + [JobSortOpts.DateSubmitted]: "exec-submitted", + [JobSortOpts.Name]: "jobname", + [JobSortOpts.ReturnCode]: "retcode", +}; export async function resolveQuickPickHelper(quickpick): Promise { ZoweLogger.trace("job.utils.resolveQuickPickHelper called."); From 841a82e1a47b3f8e918c6b693a3874cc241c3787 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Fri, 6 Oct 2023 11:07:53 -0400 Subject: [PATCH 23/47] tests(ds, jobs): Fix test cases to work with UX changes Signed-off-by: Trae Yelovich --- .../__tests__/__unit__/ZoweNode.unit.test.ts | 5 +- .../__unit__/dataset/DatasetTree.unit.test.ts | 74 +++++++++++++------ .../__unit__/dataset/init.unit.test.ts | 15 +--- .../__tests__/__unit__/extension.unit.test.ts | 9 +-- .../__unit__/job/ZoweJobNode.unit.test.ts | 4 +- .../__unit__/job/actions.unit.test.ts | 22 +++--- .../src/dataset/ZoweDatasetNode.ts | 6 +- packages/zowe-explorer/src/globals.ts | 2 +- 8 files changed, 81 insertions(+), 56 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts index 72da57e2c7..8aa7ec2dec 100644 --- a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts @@ -18,6 +18,7 @@ import { List, imperative } from "@zowe/cli"; import { Profiles } from "../../src/Profiles"; import * as globals from "../../src/globals"; import { ZoweLogger } from "../../src/utils/LoggerUtils"; +import { DatasetSortOpts } from "@zowe/zowe-explorer-api"; describe("Unit Tests (Jest)", () => { // Globals @@ -354,11 +355,13 @@ describe("Unit Tests (Jest)", () => { }; }), }); + const sessionNode = { getSessionNode: jest.fn(), sortMethod: DatasetSortOpts.Name } as unknown as ZoweDatasetNode; + jest.spyOn(ZoweDatasetNode.prototype, "getSessionNode").mockReturnValueOnce(sessionNode); // Creating a rootNode const pds = new ZoweDatasetNode( "[root]: something", vscode.TreeItemCollapsibleState.Collapsed, - { getSessionNode: jest.fn() } as unknown as ZoweDatasetNode, + sessionNode, session, undefined, undefined, diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index e11409d50a..76d3d6a604 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -15,7 +15,7 @@ import * as fs from "fs"; import * as zowe from "@zowe/cli"; import { DatasetTree } from "../../../src/dataset/DatasetTree"; import { ZoweDatasetNode } from "../../../src/dataset/ZoweDatasetNode"; -import { DatasetSort, Gui, IZoweDatasetTreeNode, ProfilesCache, ValidProfileEnum } from "@zowe/zowe-explorer-api"; +import { Gui, IZoweDatasetTreeNode, ProfilesCache, ValidProfileEnum } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../../../src/ZoweExplorerApiRegister"; import { Profiles } from "../../../src/Profiles"; import * as utils from "../../../src/utils/ProfilesUtils"; @@ -2702,52 +2702,80 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { const mocks = { nodeDataChanged: jest.spyOn(DatasetTree.prototype, "nodeDataChanged"), refreshElement: jest.spyOn(DatasetTree.prototype, "refreshElement"), + showQuickPick: jest.spyOn(Gui, "showQuickPick"), + getParent: jest.spyOn(ZoweDatasetNode.prototype, "getParent"), }; - const testNode = new ZoweDatasetNode("test", vscode.TreeItemCollapsibleState.Collapsed, null, createISession()); + const testSession = new ZoweDatasetNode("testSession", vscode.TreeItemCollapsibleState.Collapsed, null, createISession()); + const testPds = new ZoweDatasetNode("testPds", vscode.TreeItemCollapsibleState.Collapsed, testSession, createISession()); + testPds.contextValue = globals.DS_PDS_CONTEXT; beforeEach(() => { - testNode.children = [ - { label: "A", stats: { user: "someUser", m4date: Date.now() } } as unknown as ZoweDatasetNode, - { label: "B", stats: { user: "anotherUser", m4date: Date.parse("2022-01-01T12:00:00") } } as unknown as ZoweDatasetNode, - { label: "C", stats: { user: "someUser", m4date: Date.parse("2022-03-15T16:30:00") } } as unknown as ZoweDatasetNode, + mocks.getParent.mockReturnValue(testSession); + testPds.children = [ + { label: "A", stats: { user: "someUser", m4date: Date.now() }, getParent: mocks.getParent } as unknown as ZoweDatasetNode, + { + label: "B", + stats: { user: "anotherUser", m4date: Date.parse("2022-01-01T12:00:00") }, + getParent: mocks.getParent, + } as unknown as ZoweDatasetNode, + { + label: "C", + stats: { user: "someUser", m4date: Date.parse("2022-03-15T16:30:00") }, + getParent: mocks.getParent, + } as unknown as ZoweDatasetNode, ]; + testSession.children = [testPds]; }); afterEach(() => { - mocks.nodeDataChanged.mockClear(); - mocks.refreshElement.mockClear(); + for (const mock of Object.values(mocks)) { + mock.mockClear(); + } }); afterAll(() => { - mocks.nodeDataChanged.mockRestore(); - mocks.refreshElement.mockRestore(); + for (const mock of Object.values(mocks)) { + mock.mockRestore(); + } }); - it("calls refreshElement if no children exist", () => { - testNode.children = []; - testTree.sortBy(DatasetSort.Name, testNode); + it("calls refreshElement if no children exist", async () => { + // case 1: called on session node + mocks.showQuickPick.mockResolvedValueOnce("Name" as any); + testPds.children = []; + await testTree.sortPdsMembers(testPds); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); + + // case 2: called on PDS node + mocks.showQuickPick.mockResolvedValueOnce("Name" as any); + testSession.children = []; + await testTree.sortPdsMembers(testSession); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - expect(mocks.refreshElement).toHaveBeenCalledWith(testNode); + expect(mocks.refreshElement).toHaveBeenCalledWith(testSession); }); - it("sorts by name", () => { - testTree.sortBy(DatasetSort.Name, testNode); + it("sorts by name", async () => { + mocks.showQuickPick.mockResolvedValueOnce("Name" as any); + await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(testNode.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["A", "B", "C"]); + expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["A", "B", "C"]); }); - it("sorts by last modified date", () => { - testTree.sortBy(DatasetSort.LastModified, testNode); + it("sorts by last modified date", async () => { + mocks.showQuickPick.mockResolvedValueOnce("Date Modified" as any); + await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(testNode.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "C", "A"]); + expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "C", "A"]); }); - it("sorts by user ID", () => { - testTree.sortBy(DatasetSort.UserId, testNode); + it("sorts by user ID", async () => { + mocks.showQuickPick.mockResolvedValueOnce("User ID" as any); + await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(testNode.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "A", "C"]); + expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "A", "C"]); }); }); diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts index 0fc7719101..fedbf07666 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts @@ -19,7 +19,6 @@ import { initDatasetProvider } from "../../../src/dataset/init"; import { Profiles } from "../../../src/Profiles"; import { IJestIt, ITestContext, processSubscriptions, spyOnSubscriptions } from "../../__common__/testUtils"; import { ZoweLogger } from "../../../src/utils/LoggerUtils"; -import { DatasetSort } from "@zowe/zowe-explorer-api"; describe("Test src/dataset/extension", () => { describe("initDatasetProvider", () => { @@ -46,7 +45,7 @@ describe("Test src/dataset/extension", () => { onDidChangeConfiguration: jest.fn(), getTreeView: jest.fn(), refreshElement: jest.fn(), - sortBy: jest.fn(), + sortPdsMembers: jest.fn(), }; const commands: IJestIt[] = [ { @@ -253,16 +252,8 @@ describe("Test src/dataset/extension", () => { mock: [{ spy: jest.spyOn(dsProvider, "ssoLogout"), arg: [test.value] }], }, { - name: "zowe.ds.sortByName", - mock: [{ spy: jest.spyOn(dsProvider, "sortBy"), arg: [DatasetSort.Name, test.value] }], - }, - { - name: "zowe.ds.sortByModified", - mock: [{ spy: jest.spyOn(dsProvider, "sortBy"), arg: [DatasetSort.LastModified, test.value] }], - }, - { - name: "zowe.ds.sortByUserId", - mock: [{ spy: jest.spyOn(dsProvider, "sortBy"), arg: [DatasetSort.UserId, test.value] }], + name: "zowe.ds.sortBy", + mock: [{ spy: jest.spyOn(dsProvider, "sortPdsMembers"), arg: [test.value] }], }, { name: "onDidChangeConfiguration", diff --git a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts index 8ef7f6536b..c778c229db 100644 --- a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts @@ -176,9 +176,7 @@ async function createGlobalMocks() { "zowe.ds.enableValidation", "zowe.ds.ssoLogin", "zowe.ds.ssoLogout", - "zowe.ds.sortByName", - "zowe.ds.sortByModified", - "zowe.ds.sortByUserId", + "zowe.ds.sortBy", "zowe.uss.addFavorite", "zowe.uss.removeFavorite", "zowe.uss.addSession", @@ -241,10 +239,7 @@ async function createGlobalMocks() { "zowe.jobs.startPolling", "zowe.jobs.stopPolling", "zowe.jobs.cancelJob", - "zowe.jobs.sortbyname", - "zowe.jobs.sortbyid", - "zowe.jobs.sortbyreturncode", - "zowe.jobs.filterJobs", + "zowe.jobs.sortBy", "zowe.manualPoll", "zowe.updateSecureCredentials", "zowe.promptCredentials", diff --git a/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts index 2ab7070292..4bfa4a8f33 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts @@ -16,7 +16,7 @@ import * as zowe from "@zowe/cli"; import * as globals from "../../../src/globals"; import { createIJobFile, createIJobObject, createJobSessionNode } from "../../../__mocks__/mockCreators/jobs"; import { Job } from "../../../src/job/ZoweJobNode"; -import { IZoweJobTreeNode, ProfilesCache, Gui } from "@zowe/zowe-explorer-api"; +import { IZoweJobTreeNode, ProfilesCache, Gui, JobSortOpts } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../../../src/ZoweExplorerApiRegister"; import { Profiles } from "../../../src/Profiles"; import * as sessUtils from "../../../src/utils/SessionUtils"; @@ -851,7 +851,7 @@ describe("Job - sortJobs", () => { jobid: "JOBID120", }, } as IZoweJobTreeNode, - ].sort((a, b) => Job.sortJobs(a, b)); + ].sort(Job.sortJobs(JobSortOpts.Id)); expect(sorted[0].job.jobid).toBe("JOBID120"); expect(sorted[1].job.jobid).toBe("JOBID120"); expect(sorted[2].job.jobid).toBe("JOBID123"); diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 29ff63b0c3..433f40ec3d 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -1404,11 +1404,12 @@ describe("sortjobsby function", () => { const expected = new ZosJobsProvider(); testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; expected.mSessionNodes[0].children = [...[globalMocks()[1], globalMocks()[0], globalMocks()[2]]]; - const sortbynamespy = jest.spyOn(jobActions, "sortJobsBy"); + jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce("Job Name" as any); + const sortbynamespy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); //act - await jobActions.sortJobsBy(testtree.mSessionNodes[0], testtree, "jobname"); + await jobActions.sortJobs(testtree.mSessionNodes[0], testtree); //asert - expect(sortbynamespy).toBeCalledWith(testtree.mSessionNodes[0], testtree, "jobname"); + expect(sortbynamespy).toBeCalledWith(testtree.mSessionNodes[0]); expect(sortbynamespy).toHaveBeenCalled(); expect(sortbynamespy.mock.calls[0][0].children).toStrictEqual(expected.mSessionNodes[0].children); }); @@ -1418,11 +1419,12 @@ describe("sortjobsby function", () => { const expected = new ZosJobsProvider(); testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; expected.mSessionNodes[0].children = [...[globalMocks()[1], globalMocks()[0], globalMocks()[2]]]; - const sortbyidspy = jest.spyOn(jobActions, "sortJobsBy"); + const sortbyidspy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); + jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce("Job ID" as any); //act - await jobActions.sortJobsBy(testtree.mSessionNodes[0], testtree, "jobid"); + await jobActions.sortJobs(testtree.mSessionNodes[0], testtree); //asert - expect(sortbyidspy).toBeCalledWith(testtree.mSessionNodes[0], testtree, "jobid"); + expect(sortbyidspy).toBeCalledWith(testtree.mSessionNodes[0]); expect(sortbyidspy).toHaveBeenCalled(); expect(sortbyidspy.mock.calls[0][0].children).toStrictEqual(expected.mSessionNodes[0].children); }); @@ -1432,11 +1434,13 @@ describe("sortjobsby function", () => { const expected = new ZosJobsProvider(); testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; expected.mSessionNodes[0].children = [...[globalMocks()[0], globalMocks()[1], globalMocks()[2]]]; - const sortbyretcodespy = jest.spyOn(jobActions, "sortJobsBy"); + const sortbyretcodespy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); + jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce("Return Code" as any); + //act - await jobActions.sortJobsBy(testtree.mSessionNodes[0], testtree, "retcode"); + await jobActions.sortJobs(testtree.mSessionNodes[0], testtree); //asert - expect(sortbyretcodespy).toBeCalledWith(testtree.mSessionNodes[0], testtree, "retcode"); + expect(sortbyretcodespy).toBeCalledWith(testtree.mSessionNodes[0]); expect(sortbyretcodespy).toHaveBeenCalled(); expect(sortbyretcodespy.mock.calls[0][0].children).toStrictEqual(expected.mSessionNodes[0].children); }); diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index 2cc4b1083d..f66e6a864f 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -268,7 +268,11 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod .filter((label) => this.children.find((c) => (c.label as string) === label) == null) .map((label) => elementChildren[label]); - const sortMethod = (this.sortMethod ? this.sortMethod : this.getSessionNode().sortMethod) as DatasetSortOpts; + // get sort method for session + const sessionSortMethod = contextually.isSession(this) ? this.sortMethod : this.getSessionNode().sortMethod; + + // use the PDS sort method if one is defined; otherwise, use session sort method + const sortMethod = (this.sortMethod ?? sessionSortMethod) as DatasetSortOpts; this.children = this.children .concat(newChildren) diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 596308f59c..b4dda79aef 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 116; +export const COMMAND_COUNT = 112; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000; From f4d67e26c38ebdc613f23766edc8d7b0fd707136 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Mon, 9 Oct 2023 13:44:01 -0400 Subject: [PATCH 24/47] feat(ds): Filter PDS members (session-level and PDS-level); fix broken tests Signed-off-by: Trae Yelovich --- .../src/tree/IZoweTreeNode.ts | 26 +++- .../__tests__/__unit__/ZoweNode.unit.test.ts | 3 +- .../__unit__/dataset/DatasetTree.unit.test.ts | 10 +- .../__tests__/__unit__/extension.unit.test.ts | 1 + .../__unit__/job/ZoweJobNode.unit.test.ts | 4 +- .../__unit__/job/actions.unit.test.ts | 20 ++- .../i18n/sample/package.i18n.json | 5 + .../sample/src/dataset/DatasetTree.i18n.json | 9 ++ .../i18n/sample/src/dataset/utils.i18n.json | 7 +- .../i18n/sample/src/globals.i18n.json | 1 + .../i18n/sample/src/job/utils.i18n.json | 9 +- .../i18n/sample/src/shared/utils.i18n.json | 2 + packages/zowe-explorer/package.json | 16 ++- packages/zowe-explorer/package.nls.json | 2 + .../zowe-explorer/src/dataset/DatasetTree.ts | 127 ++++++++++++++++-- .../src/dataset/ZoweDatasetNode.ts | 80 +++++++++-- packages/zowe-explorer/src/dataset/init.ts | 6 + packages/zowe-explorer/src/dataset/utils.ts | 16 ++- packages/zowe-explorer/src/globals.ts | 3 +- .../zowe-explorer/src/job/ZosJobsProvider.ts | 4 +- packages/zowe-explorer/src/job/ZoweJobNode.ts | 27 ++-- packages/zowe-explorer/src/job/actions.ts | 26 +++- packages/zowe-explorer/src/job/utils.ts | 9 +- packages/zowe-explorer/src/shared/utils.ts | 7 +- yarn.lock | 5 + 25 files changed, 355 insertions(+), 70 deletions(-) diff --git a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts index 6a997e9719..0055a72814 100644 --- a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts @@ -31,6 +31,26 @@ export enum DatasetSortOpts { UserId, } +export enum SortDirection { + Ascending, + Descending, +} + +export enum DatasetFilterOpts { + LastModified, + UserId, +} + +export type DatasetFilter = { + method: DatasetFilterOpts; + value: string; +}; + +export type NodeSort = { + method: DatasetSortOpts | JobSortOpts; + direction: SortDirection; +}; + export enum JobSortOpts { Id, DateSubmitted, @@ -99,7 +119,7 @@ export interface IZoweTreeNode { /** * Sorting method for this node's children */ - sortMethod?: DatasetSortOpts | JobSortOpts; + sort?: NodeSort; /** * Retrieves the node label */ @@ -161,6 +181,10 @@ export interface IZoweDatasetTreeNode extends IZoweTreeNode { * Additional statistics about this data set */ stats?: Partial; + /** + * Filter method for this data set's children + */ + filter?: DatasetFilter; /** * Retrieves child nodes of this IZoweDatasetTreeNode * diff --git a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts index 8aa7ec2dec..f7a716e91c 100644 --- a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts @@ -356,7 +356,7 @@ describe("Unit Tests (Jest)", () => { }), }); const sessionNode = { getSessionNode: jest.fn(), sortMethod: DatasetSortOpts.Name } as unknown as ZoweDatasetNode; - jest.spyOn(ZoweDatasetNode.prototype, "getSessionNode").mockReturnValueOnce(sessionNode); + const getSessionNodeSpy = jest.spyOn(ZoweDatasetNode.prototype, "getSessionNode").mockReturnValue(sessionNode); // Creating a rootNode const pds = new ZoweDatasetNode( "[root]: something", @@ -384,6 +384,7 @@ describe("Unit Tests (Jest)", () => { expect(pdsChildren[0].contextValue).toEqual(globals.DS_FILE_ERROR_CONTEXT); expect(pdsChildren[1].label).toEqual("GOODMEM1"); expect(pdsChildren[1].contextValue).toEqual(globals.DS_MEMBER_CONTEXT); + getSessionNodeSpy.mockRestore(); }); /************************************************************************************************************* diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index 76d3d6a604..6063c7f6b4 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -2741,14 +2741,14 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { it("calls refreshElement if no children exist", async () => { // case 1: called on session node - mocks.showQuickPick.mockResolvedValueOnce("Name" as any); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); testPds.children = []; await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); // case 2: called on PDS node - mocks.showQuickPick.mockResolvedValueOnce("Name" as any); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); testSession.children = []; await testTree.sortPdsMembers(testSession); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); @@ -2756,7 +2756,7 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { }); it("sorts by name", async () => { - mocks.showQuickPick.mockResolvedValueOnce("Name" as any); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); @@ -2764,7 +2764,7 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { }); it("sorts by last modified date", async () => { - mocks.showQuickPick.mockResolvedValueOnce("Date Modified" as any); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(calendar) Date Modified" }); await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); @@ -2772,7 +2772,7 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { }); it("sorts by user ID", async () => { - mocks.showQuickPick.mockResolvedValueOnce("User ID" as any); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(account) User ID" }); await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); diff --git a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts index c778c229db..7e5df21fd1 100644 --- a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts @@ -177,6 +177,7 @@ async function createGlobalMocks() { "zowe.ds.ssoLogin", "zowe.ds.ssoLogout", "zowe.ds.sortBy", + "zowe.ds.filterBy", "zowe.uss.addFavorite", "zowe.uss.removeFavorite", "zowe.uss.addSession", diff --git a/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts index 4bfa4a8f33..ec06c21ef7 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts @@ -16,7 +16,7 @@ import * as zowe from "@zowe/cli"; import * as globals from "../../../src/globals"; import { createIJobFile, createIJobObject, createJobSessionNode } from "../../../__mocks__/mockCreators/jobs"; import { Job } from "../../../src/job/ZoweJobNode"; -import { IZoweJobTreeNode, ProfilesCache, Gui, JobSortOpts } from "@zowe/zowe-explorer-api"; +import { IZoweJobTreeNode, ProfilesCache, Gui, JobSortOpts, SortDirection } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../../../src/ZoweExplorerApiRegister"; import { Profiles } from "../../../src/Profiles"; import * as sessUtils from "../../../src/utils/SessionUtils"; @@ -851,7 +851,7 @@ describe("Job - sortJobs", () => { jobid: "JOBID120", }, } as IZoweJobTreeNode, - ].sort(Job.sortJobs(JobSortOpts.Id)); + ].sort(Job.sortJobs({ method: JobSortOpts.Id, direction: SortDirection.Ascending })); expect(sorted[0].job.jobid).toBe("JOBID120"); expect(sorted[1].job.jobid).toBe("JOBID120"); expect(sorted[2].job.jobid).toBe("JOBID123"); diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 433f40ec3d..b80d907591 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -11,7 +11,7 @@ import * as vscode from "vscode"; import * as zowe from "@zowe/cli"; -import { Gui, IZoweJobTreeNode, ValidProfileEnum } from "@zowe/zowe-explorer-api"; +import { Gui, IZoweJobTreeNode, JobSortOpts, SortDirection, ValidProfileEnum } from "@zowe/zowe-explorer-api"; import { Job, Spool } from "../../../src/job/ZoweJobNode"; import { createISession, @@ -1402,9 +1402,13 @@ describe("sortjobsby function", () => { const globalMocks = createGlobalMocks(); const testtree = new ZosJobsProvider(); const expected = new ZosJobsProvider(); + testtree.mSessionNodes[0].sort = { + method: JobSortOpts.Id, + direction: SortDirection.Ascending, + }; testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; expected.mSessionNodes[0].children = [...[globalMocks()[1], globalMocks()[0], globalMocks()[2]]]; - jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce("Job Name" as any); + jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce({ label: "$(case-sensitive) Job Name" }); const sortbynamespy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); //act await jobActions.sortJobs(testtree.mSessionNodes[0], testtree); @@ -1417,10 +1421,14 @@ describe("sortjobsby function", () => { const globalMocks = createGlobalMocks(); const testtree = new ZosJobsProvider(); const expected = new ZosJobsProvider(); + testtree.mSessionNodes[0].sort = { + method: JobSortOpts.Id, + direction: SortDirection.Ascending, + }; testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; expected.mSessionNodes[0].children = [...[globalMocks()[1], globalMocks()[0], globalMocks()[2]]]; const sortbyidspy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); - jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce("Job ID" as any); + jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce({ label: "$(list-ordered) Job ID (default)" }); //act await jobActions.sortJobs(testtree.mSessionNodes[0], testtree); //asert @@ -1432,10 +1440,14 @@ describe("sortjobsby function", () => { const globalMocks = createGlobalMocks(); const testtree = new ZosJobsProvider(); const expected = new ZosJobsProvider(); + testtree.mSessionNodes[0].sort = { + method: JobSortOpts.Id, + direction: SortDirection.Ascending, + }; testtree.mSessionNodes[0].children = [...[globalMocks()[2], globalMocks()[1], globalMocks()[0]]]; expected.mSessionNodes[0].children = [...[globalMocks()[0], globalMocks()[1], globalMocks()[2]]]; const sortbyretcodespy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); - jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce("Return Code" as any); + jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce({ label: "$(symbol-numeric) Return Code" }); //act await jobActions.sortJobs(testtree.mSessionNodes[0], testtree); diff --git a/packages/zowe-explorer/i18n/sample/package.i18n.json b/packages/zowe-explorer/i18n/sample/package.i18n.json index 846aae972f..af2e332f35 100644 --- a/packages/zowe-explorer/i18n/sample/package.i18n.json +++ b/packages/zowe-explorer/i18n/sample/package.i18n.json @@ -149,6 +149,11 @@ "copyFile": "Copy", "pasteFile": "Paste", "jobs.sortBy": "Sort jobs...", + "ds.allPdsSort": "all PDS members in {0}", + "ds.singlePdsSort": "the PDS members in {0}", + "ds.selectFilterOpt": "Set a filter for {0}", + "ds.selectSortOpt": "Select a sorting option for {0}", "jobs.selectSortOpt": "Select a sorting option for jobs in {0}", + "ds.filterBy": "Filter PDS members...", "ds.sortBy": "Sort PDS members..." } diff --git a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json index ffdd605c77..c3ac5386eb 100644 --- a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json @@ -29,5 +29,14 @@ "ds.allPdsSort": "all PDS members in {0}", "ds.singlePdsSort": "the PDS members in {0}", "ds.selectSortOpt": "Select a sorting option for {0}", + "setSortDirection": "$(fold) Sort Direction", + "sort.asc": "Ascending", + "sort.desc": "Descending", + "sort.selectDirection": "Select a sorting direction", + "ds.clearAllFilters": "$(clear-all) Clear all filters", + "ds.clearPdsFilter": "$(clear-all) Clear filters for this PDS", + "ds.selectFilterOpt": "Set a filter for {0}", + "ds.filterEntry.title": "Enter a value to filter by", + "ds.filterEntry.invalidDate": "Invalid date format specified", "defaultFilterPrompt.option.prompt.search": "$(plus) Create a new filter. For example: HLQ.*, HLQ.aaa.bbb, HLQ.ccc.ddd(member)" } diff --git a/packages/zowe-explorer/i18n/sample/src/dataset/utils.i18n.json b/packages/zowe-explorer/i18n/sample/src/dataset/utils.i18n.json index 55ee3b4e20..0aad47e3d5 100644 --- a/packages/zowe-explorer/i18n/sample/src/dataset/utils.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/dataset/utils.i18n.json @@ -1,5 +1,6 @@ { - "ds.sortByName": "Name", - "ds.sortByModified": "Date Modified", - "ds.sortByUserId": "User ID" + "ds.sortByName": "$(case-sensitive) Name (default)", + "ds.sortByModified": "$(calendar) Date Modified", + "ds.sortByUserId": "$(account) User ID", + "setSortDirection": "$(fold) Sort Direction" } diff --git a/packages/zowe-explorer/i18n/sample/src/globals.i18n.json b/packages/zowe-explorer/i18n/sample/src/globals.i18n.json index ec7d8bddd5..a6c64f66d8 100644 --- a/packages/zowe-explorer/i18n/sample/src/globals.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/globals.i18n.json @@ -17,6 +17,7 @@ "createFile.attribute.storclass": "Enter the SMS storage class", "createFile.attribute.volser": "Enter the volume serial on which the data set should be placed", "zowe.separator.recentFilters": "Recent Filters", + "zowe.separator.options": "Options", "globals.defineGlobals.isTheia": "Zowe Explorer is running in Theia environment.", "globals.defineGlobals.tempFolder": "Zowe Explorer's temp folder is located at {0}", "globals.setActivated.success": "Zowe Explorer has activated successfully.", diff --git a/packages/zowe-explorer/i18n/sample/src/job/utils.i18n.json b/packages/zowe-explorer/i18n/sample/src/job/utils.i18n.json index 79f5b24932..b32e729f24 100644 --- a/packages/zowe-explorer/i18n/sample/src/job/utils.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/job/utils.i18n.json @@ -1,6 +1,7 @@ { - "jobs.sortById": "Job ID", - "jobs.sortByDateSubmitted": "Date Submitted", - "jobs.sortByName": "Job Name", - "jobs.sortByReturnCode": "Return Code" + "jobs.sortById": "$(list-ordered) Job ID (default)", + "jobs.sortByDateSubmitted": "$(calendar) Date Submitted", + "jobs.sortByName": "$(case-sensitive) Job Name", + "jobs.sortByReturnCode": "$(symbol-numeric) Return Code", + "setSortDirection": "$(fold) Sort Direction" } diff --git a/packages/zowe-explorer/i18n/sample/src/shared/utils.i18n.json b/packages/zowe-explorer/i18n/sample/src/shared/utils.i18n.json index 7bdcd3a109..9ee68cb025 100644 --- a/packages/zowe-explorer/i18n/sample/src/shared/utils.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/shared/utils.i18n.json @@ -3,6 +3,8 @@ "zowe.jobs.confirmSubmission.yourJobs": "Your jobs", "zowe.jobs.confirmSubmission.otherUserJobs": "Other user jobs", "zowe.jobs.confirmSubmission.allJobs": "All jobs", + "sort.asc": "Ascending", + "sort.desc": "Descending", "uploadContent.putContents": "Uploading USS file", "saveFile.response.save.title": "Saving data set...", "saveUSSFile.response.title": "Saving file...", diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json index 774260dc6a..e77f74a440 100644 --- a/packages/zowe-explorer/package.json +++ b/packages/zowe-explorer/package.json @@ -286,6 +286,12 @@ "title": "%deleteProfile%", "category": "Zowe Explorer" }, + { + "command": "zowe.ds.filterBy", + "title": "%ds.filterBy%", + "category": "Zowe Explorer", + "icon": "$(list-filter)" + }, { "command": "zowe.ds.sortBy", "title": "%ds.sortBy%", @@ -1010,13 +1016,18 @@ }, { "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", - "command": "zowe.ds.sortBy", + "command": "zowe.ds.filterBy", "group": "inline@0" }, + { + "when": "view == zowe.ds.explorer && viewItem =~ /^session.*/ && !listMultiSelection", + "command": "zowe.ds.sortBy", + "group": "inline@1" + }, { "when": "view == zowe.ds.explorer && viewItem =~ /^(?!.*_fav.*)session.*/ && !listMultiSelection", "command": "zowe.ds.pattern", - "group": "inline@1" + "group": "inline@2" }, { "when": "view == zowe.ds.explorer && viewItem =~ /^(pds|ds|migr).*_fav.*/", @@ -1999,6 +2010,7 @@ "dependencies": { "@zowe/secrets-for-zowe-sdk": "7.18.4", "@zowe/zowe-explorer-api": "2.12.0-SNAPSHOT", + "dayjs": "^1.11.10", "fs-extra": "8.0.1", "isbinaryfile": "4.0.4", "js-yaml": "3.13.1", diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json index 18b81148a0..af2e332f35 100644 --- a/packages/zowe-explorer/package.nls.json +++ b/packages/zowe-explorer/package.nls.json @@ -151,7 +151,9 @@ "jobs.sortBy": "Sort jobs...", "ds.allPdsSort": "all PDS members in {0}", "ds.singlePdsSort": "the PDS members in {0}", + "ds.selectFilterOpt": "Set a filter for {0}", "ds.selectSortOpt": "Select a sorting option for {0}", "jobs.selectSortOpt": "Select a sorting option for jobs in {0}", + "ds.filterBy": "Filter PDS members...", "ds.sortBy": "Sort PDS members..." } diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index 244508bfed..a7e17bf326 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -23,20 +23,23 @@ import { PersistenceSchemaEnum, NodeInteraction, IZoweTreeNode, + DatasetFilter, + DatasetSortOpts, } from "@zowe/zowe-explorer-api"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; import { FilterDescriptor, FilterItem, errorHandling, syncSessionNode } from "../utils/ProfilesUtils"; -import { sortTreeItems, getAppName, getDocumentFilePath } from "../shared/utils"; +import { sortTreeItems, getAppName, getDocumentFilePath, SORT_OPTS_TO_ENUM } from "../shared/utils"; import { ZoweTreeProvider } from "../abstract/ZoweTreeProvider"; import { ZoweDatasetNode } from "./ZoweDatasetNode"; import { getIconById, getIconByNode, IconId, IIconItem } from "../generators/icons"; +import * as dayjs from "dayjs"; import * as fs from "fs"; import * as contextually from "../shared/context"; import { resetValidationSettings } from "../shared/actions"; import { closeOpenedTextFile } from "../utils/workspace"; import { IDataSet, IListOptions, imperative } from "@zowe/cli"; -import { DATASET_SORT_OPTS, validateDataSetName, validateMemberName } from "./utils"; +import { DATASET_FILTER_KEYS, DATASET_FILTER_OPTS, DATASET_SORT_OPTS, validateDataSetName, validateMemberName } from "./utils"; import { SettingsConfig } from "../utils/SettingsConfig"; import { ZoweLogger } from "../utils/LoggerUtils"; import { TreeViewUtils } from "../utils/TreeViewUtils"; @@ -1300,7 +1303,10 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree (node.sortMethod === i ? `${sortOpt} $(check)` : sortOpt)), + DATASET_SORT_OPTS.map((sortOpt, i) => ({ + label: node.sort?.method === i ? `${sortOpt} $(check)` : sortOpt, + description: i === DATASET_SORT_OPTS.length - 1 ? Object.keys(SORT_OPTS_TO_ENUM)[node.sort.direction] : null, + })), { placeHolder: localize("ds.selectSortOpt", "Select a sorting option for {0}", specifier), } @@ -1309,12 +1315,26 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { // children nodes already exist, sort and repaint to avoid extra refresh - node.children.sort(ZoweDatasetNode.sortBy(sortMethod)); + node.children.sort(ZoweDatasetNode.sortBy(node.sort)); this.nodeDataChanged(node); } else { this.refreshElement(node); } } } + + public updateFilters(node: IZoweDatasetTreeNode, newFilter: DatasetFilter | null, isSession: boolean): void { + node.filter = newFilter; + + if (isSession) { + // if a session was selected, apply this sort to ALL PDS members + if (node.children != null && node.children.length > 0) { + // children nodes already exist, sort and repaint to avoid extra refresh + for (const c of node.children) { + const asDs = c as IZoweDatasetTreeNode; + if (contextually.isPds(c) && c.children) { + if (asDs.filter != null) { + // if there was a filter set for this PDS, refresh it to get any missing nodes back + // since refreshing the PDS will filter its children, we can return early + this.refreshElement(c); + continue; + } + if (newFilter != null) { + c.children = c.children.filter(ZoweDatasetNode.filterBy(newFilter)); + this.nodeDataChanged(c); + } else { + this.refreshElement(c); + } + } + } + } else { + this.refreshElement(node); + } + } else { + // Only sort the PDS members for this PDS + if (node.filter != null) { + // if there was a filter set for this PDS, refresh it to get any missing nodes back + // since refreshing the PDS will filter its children, we can return early + this.refreshElement(node); + return; + } + if (node.children != null && node.children.length > 0 && newFilter != null) { + // children nodes already exist, sort and repaint to avoid extra refresh + node.children = node.children.filter(ZoweDatasetNode.filterBy(newFilter)); + this.nodeDataChanged(node); + } else { + this.refreshElement(node); + } + } + } + + public async filterPdsMembers(node: IZoweDatasetTreeNode): Promise { + const isSession = contextually.isSession(node); + + const specifier = isSession + ? localize("ds.allPdsSort", "all PDS members in {0}", node.label as string) + : localize("ds.singlePdsSort", "the PDS members in {0}", node.label as string); + const clearFilter = isSession + ? localize("ds.clearAllFilters", "$(clear-all) Clear all filters") + : localize("ds.clearPdsFilter", "$(clear-all) Clear filters for this PDS"); + const selection = ( + await Gui.showQuickPick( + [...DATASET_FILTER_OPTS.map((sortOpt, i) => (node.filter?.method === i ? `${sortOpt} $(check)` : sortOpt)), clearFilter], + { + placeHolder: localize("ds.selectFilterOpt", "Set a filter for {0}", specifier), + } + ) + )?.replace(" $(check)", ""); + if (selection === clearFilter) { + this.updateFilters(node, null, isSession); + return; + } + + if (selection == null || !(selection in DATASET_FILTER_KEYS)) { + return; + } + + const filterMethod = DATASET_FILTER_KEYS[selection]; + const filter = await Gui.showInputBox({ + title: localize("ds.filterEntry.title", "Enter a value to filter by"), + placeHolder: "", + validateInput(value) { + return dayjs(value).isValid() ? null : localize("ds.filterEntry.invalidDate", "Invalid date format specified"); + }, + }); + if (filter == null) { + return; + } + + const newFilter = { + method: filterMethod, + value: filter, + }; + + this.updateFilters(node, newFilter, isSession); + } } diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index f66e6a864f..1cfd7b2f8e 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -13,13 +13,25 @@ import * as zowe from "@zowe/cli"; import * as vscode from "vscode"; import * as globals from "../globals"; import { errorHandling } from "../utils/ProfilesUtils"; -import { DatasetSortOpts, DatasetStats, Gui, NodeAction, IZoweDatasetTreeNode, ZoweTreeNode } from "@zowe/zowe-explorer-api"; +import { + DatasetFilter, + DatasetFilterOpts, + DatasetSortOpts, + DatasetStats, + Gui, + NodeAction, + IZoweDatasetTreeNode, + ZoweTreeNode, + SortDirection, + NodeSort, +} from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; import { getIconByNode } from "../generators/icons"; import * as contextually from "../shared/context"; import * as nls from "vscode-nls"; import { Profiles } from "../Profiles"; import { ZoweLogger } from "../utils/LoggerUtils"; +import * as dayjs from "dayjs"; // Set up localization nls.config({ @@ -45,7 +57,11 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod public ongoingActions: Record> = {}; public wasDoubleClicked: boolean = false; public stats: DatasetStats; - public sortMethod = DatasetSortOpts.Name; + public sort: NodeSort = { + method: DatasetSortOpts.Name, + direction: SortDirection.Ascending, + }; + public filter: DatasetFilter; /** * Creates an instance of ZoweDatasetNode @@ -268,16 +284,21 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod .filter((label) => this.children.find((c) => (c.label as string) === label) == null) .map((label) => elementChildren[label]); - // get sort method for session - const sessionSortMethod = contextually.isSession(this) ? this.sortMethod : this.getSessionNode().sortMethod; + // get sort settings for session + const sessionSort = contextually.isSession(this) ? this.sort : this.getSessionNode().sort; - // use the PDS sort method if one is defined; otherwise, use session sort method - const sortMethod = (this.sortMethod ?? sessionSortMethod) as DatasetSortOpts; + // use the PDS sort settings if defined; otherwise, use session sort method + const sortOpts = this.sort ?? sessionSort; + + // use the PDS filter if one is set, otherwise try using the session filter + const sessionFilter = contextually.isSession(this) ? this.filter : this.getSessionNode().filter; + const filter = this.filter ?? sessionFilter; this.children = this.children .concat(newChildren) .filter((c) => (c.label as string) in elementChildren) - .sort(ZoweDatasetNode.sortBy(sortMethod ?? DatasetSortOpts.Name)); + .filter(filter ? ZoweDatasetNode.filterBy(filter) : (_c): boolean => true) + .sort(ZoweDatasetNode.sortBy(sortOpts)); } return this.children; @@ -289,21 +310,54 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod * @param method The sorting method to use * @returns A function that sorts 2 nodes based on the given sorting method */ - public static sortBy(method: DatasetSortOpts): (a: IZoweDatasetTreeNode, b: IZoweDatasetTreeNode) => number { + public static sortBy(sort: NodeSort): (a: IZoweDatasetTreeNode, b: IZoweDatasetTreeNode) => number { return (a, b): number => { + const sortLessThan = sort.direction == SortDirection.Ascending ? -1 : 1; + const sortGreaterThan = sortLessThan * -1; + const aParent = a.getParent(); if (aParent == null || !contextually.isPds(aParent)) { - return (a.label as string) < (b.label as string) ? -1 : 1; + return (a.label as string) < (b.label as string) ? sortLessThan : sortGreaterThan; } - switch (method) { + switch (sort.method) { case DatasetSortOpts.Name: default: - return (a.label as string) < (b.label as string) ? -1 : 1; + return (a.label as string) < (b.label as string) ? sortLessThan : sortGreaterThan; case DatasetSortOpts.LastModified: - return a.stats?.m4date < b.stats?.m4date ? -1 : 1; + return a.stats?.m4date < b.stats?.m4date ? sortLessThan : sortGreaterThan; case DatasetSortOpts.UserId: - return a.stats?.user < b.stats?.user ? -1 : 1; + return a.stats?.user < b.stats?.user ? sortLessThan : sortGreaterThan; + } + }; + } + + /** + * Returns a filter function based on the given method. + * If the nodes are not PDS members, it will not filter those nodes. + * @param method The sorting method to use + * @returns A function that sorts 2 nodes based on the given sorting method + */ + public static filterBy(filter: DatasetFilter): (node: IZoweDatasetTreeNode) => boolean { + const isDateFilter = (f: string): boolean => { + return dayjs(f).isValid(); + }; + + return (node): boolean => { + const aParent = node.getParent(); + if (aParent == null || !contextually.isPds(aParent)) { + return true; + } + + switch (filter.method) { + case DatasetFilterOpts.LastModified: + if (!isDateFilter(filter.value)) { + return true; + } + + return dayjs(node.stats?.m4date).isSame(filter.value, "day"); + case DatasetFilterOpts.UserId: + return node.stats?.user === filter.value; } }; } diff --git a/packages/zowe-explorer/src/dataset/init.ts b/packages/zowe-explorer/src/dataset/init.ts index dfb156ac98..441b370455 100644 --- a/packages/zowe-explorer/src/dataset/init.ts +++ b/packages/zowe-explorer/src/dataset/init.ts @@ -201,6 +201,12 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro context.subscriptions.push( vscode.commands.registerCommand("zowe.ds.sortBy", async (node: IZoweDatasetTreeNode) => datasetProvider.sortPdsMembers(node)) ); + context.subscriptions.push( + vscode.commands.registerCommand( + "zowe.ds.filterBy", + async (node: IZoweDatasetTreeNode): Promise => datasetProvider.filterPdsMembers(node) + ) + ); context.subscriptions.push( vscode.workspace.onDidChangeConfiguration(async (e) => { await datasetProvider.onDidChangeConfiguration(e); diff --git a/packages/zowe-explorer/src/dataset/utils.ts b/packages/zowe-explorer/src/dataset/utils.ts index c7445dce71..40d861c577 100644 --- a/packages/zowe-explorer/src/dataset/utils.ts +++ b/packages/zowe-explorer/src/dataset/utils.ts @@ -11,7 +11,7 @@ import * as globals from "../globals"; import * as nls from "vscode-nls"; -import { IZoweNodeType } from "@zowe/zowe-explorer-api"; +import { DatasetFilterOpts, IZoweNodeType } from "@zowe/zowe-explorer-api"; import { ZoweLogger } from "../utils/LoggerUtils"; // Set up localization @@ -22,11 +22,19 @@ nls.config({ const localize: nls.LocalizeFunc = nls.loadMessageBundle(); export const DATASET_SORT_OPTS = [ - localize("ds.sortByName", "Name"), - localize("ds.sortByModified", "Date Modified"), - localize("ds.sortByUserId", "User ID"), + localize("ds.sortByName", "$(case-sensitive) Name (default)"), + localize("ds.sortByModified", "$(calendar) Date Modified"), + localize("ds.sortByUserId", "$(account) User ID"), + localize("setSortDirection", "$(fold) Sort Direction"), ]; +export const DATASET_FILTER_OPTS = [localize("ds.sortByModified", "$(calendar) Date Modified"), localize("ds.sortByUserId", "$(account) User ID")]; + +export const DATASET_FILTER_KEYS: Record = { + [localize("ds.sortByModified", "$(calendar) Date Modified")]: DatasetFilterOpts.LastModified, + [localize("ds.sortByUserId", "$(account) User ID")]: DatasetFilterOpts.UserId, +}; + export function getProfileAndDataSetName(node: IZoweNodeType): { profileName: string; dataSetName: string; diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index b4dda79aef..6686c4c069 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 112; +export const COMMAND_COUNT = 113; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000; @@ -279,6 +279,7 @@ export enum JobPickerTypes { export const SEPARATORS = { BLANK: { kind: vscode.QuickPickItemKind.Separator, label: "" }, RECENT_FILTERS: { kind: vscode.QuickPickItemKind.Separator, label: localize("zowe.separator.recentFilters", "Recent Filters") }, + OPTIONS: { kind: vscode.QuickPickItemKind.Separator, label: localize("zowe.separator.options", "Options") }, }; /** diff --git a/packages/zowe-explorer/src/job/ZosJobsProvider.ts b/packages/zowe-explorer/src/job/ZosJobsProvider.ts index 2747b322ff..c1c3dd0f2d 100644 --- a/packages/zowe-explorer/src/job/ZosJobsProvider.ts +++ b/packages/zowe-explorer/src/job/ZosJobsProvider.ts @@ -1124,10 +1124,8 @@ export class ZosJobsProvider extends ZoweTreeProvider implements IZoweTree this.children.find((ch) => ch.label === c.label) == null); - const sortMethod = contextually.isSession(this) ? this.sortMethod : JobSortOpts.Id; + const sortMethod = contextually.isSession(this) ? this.sort : { method: JobSortOpts.Id, direction: SortDirection.Ascending }; // Remove any children that are no longer present in the built record this.children = this.children .concat(newChildren) @@ -238,13 +244,16 @@ export class Job extends ZoweTreeNode implements IZoweJobTreeNode { return this.children; } - public static sortJobs(method: JobSortOpts): (x: IZoweJobTreeNode, y: IZoweJobTreeNode) => number { + public static sortJobs(sortOpts: NodeSort): (x: IZoweJobTreeNode, y: IZoweJobTreeNode) => number { return (x, y) => { - const keyToSortBy = JOB_SORT_KEYS[method]; + const sortLessThan = sortOpts.direction == SortDirection.Ascending ? -1 : 1; + const sortGreaterThan = sortLessThan * -1; + + const keyToSortBy = JOB_SORT_KEYS[sortOpts.method]; if (keyToSortBy !== "jobid" && x["job"][keyToSortBy] == y["job"][keyToSortBy]) { - return x["job"]["jobid"] > y["job"]["jobid"] ? 1 : -1; + return x["job"]["jobid"] > y["job"]["jobid"] ? sortGreaterThan : sortLessThan; } else { - return x["job"][keyToSortBy] > y["job"][keyToSortBy] ? 1 : -1; + return x["job"][keyToSortBy] > y["job"][keyToSortBy] ? sortGreaterThan : sortLessThan; } }; } diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index f8bb17b8ba..f6a110e615 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -14,12 +14,12 @@ import * as zowe from "@zowe/cli"; import { errorHandling } from "../utils/ProfilesUtils"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; -import { Gui, IZoweTree, IZoweJobTreeNode } from "@zowe/zowe-explorer-api"; +import { Gui, IZoweTree, IZoweJobTreeNode, JobSortOpts } from "@zowe/zowe-explorer-api"; import { Job, Spool } from "./ZoweJobNode"; import * as nls from "vscode-nls"; import SpoolProvider, { encodeJobFile, getSpoolFiles, matchSpool } from "../SpoolProvider"; import { ZoweLogger } from "../utils/LoggerUtils"; -import { getDefaultUri } from "../shared/utils"; +import { SORT_OPTS_TO_ENUM, getDefaultUri } from "../shared/utils"; import { ZosJobsProvider } from "./ZosJobsProvider"; import { JOB_SORT_OPTS } from "./utils"; @@ -532,7 +532,10 @@ export async function cancelJobs(jobsProvider: IZoweTree, node } export async function sortJobs(session: IZoweJobTreeNode, jobsProvider: ZosJobsProvider): Promise { const selection = await Gui.showQuickPick( - JOB_SORT_OPTS.map((sortOpt, i) => (i === session.sortMethod ? `${sortOpt} $(check)` : sortOpt)), + JOB_SORT_OPTS.map((sortOpt, i) => ({ + label: i === session.sort.method ? `${sortOpt} $(check)` : sortOpt, + description: i === JOB_SORT_OPTS.length - 1 ? Object.keys(SORT_OPTS_TO_ENUM)[session.sort.direction] : null, + })), { placeHolder: localize("jobs.selectSortOpt", "Select a sorting option for jobs in {0}", session.label as string), } @@ -540,13 +543,26 @@ export async function sortJobs(session: IZoweJobTreeNode, jobsProvider: ZosJobsP if (selection == null) { return; } + if (selection.label === localize("setSortDirection", "$(fold) Sort Direction")) { + const dir = await Gui.showQuickPick([localize("sort.asc", "Ascending"), localize("sort.desc", "Descending")], { + placeHolder: localize("sort.selectDirection", "Select a sorting direction"), + }); + if (dir != null) { + session.sort = { + ...(session.sort ?? { method: JobSortOpts.Id }), + direction: SORT_OPTS_TO_ENUM[dir], + }; + } + await sortJobs(session, jobsProvider); + return; + } - const optIndex = JOB_SORT_OPTS.indexOf(selection); + const optIndex = JOB_SORT_OPTS.indexOf(selection.label.replace(" $(check)", "")); if (optIndex == -1) { return; } - session.sortMethod = optIndex; + session.sort.method = optIndex; jobsProvider.sortBy(session); } diff --git a/packages/zowe-explorer/src/job/utils.ts b/packages/zowe-explorer/src/job/utils.ts index 9e9db05d3c..9747139144 100644 --- a/packages/zowe-explorer/src/job/utils.ts +++ b/packages/zowe-explorer/src/job/utils.ts @@ -23,10 +23,11 @@ nls.config({ const localize: nls.LocalizeFunc = nls.loadMessageBundle(); export const JOB_SORT_OPTS = [ - localize("jobs.sortById", "Job ID"), - localize("jobs.sortByDateSubmitted", "Date Submitted"), - localize("jobs.sortByName", "Job Name"), - localize("jobs.sortByReturnCode", "Return Code"), + localize("jobs.sortById", "$(list-ordered) Job ID (default)"), + localize("jobs.sortByDateSubmitted", "$(calendar) Date Submitted"), + localize("jobs.sortByName", "$(case-sensitive) Job Name"), + localize("jobs.sortByReturnCode", "$(symbol-numeric) Return Code"), + localize("setSortDirection", "$(fold) Sort Direction"), ]; export const JOB_SORT_KEYS: Record = { diff --git a/packages/zowe-explorer/src/shared/utils.ts b/packages/zowe-explorer/src/shared/utils.ts index 354c9a6732..c0c4bec81a 100644 --- a/packages/zowe-explorer/src/shared/utils.ts +++ b/packages/zowe-explorer/src/shared/utils.ts @@ -16,7 +16,7 @@ import * as vscode from "vscode"; import * as path from "path"; import * as globals from "../globals"; import * as os from "os"; -import { Gui, IZoweTreeNode, IZoweNodeType, IZoweDatasetTreeNode, IZoweUSSTreeNode, IZoweJobTreeNode } from "@zowe/zowe-explorer-api"; +import { Gui, IZoweTreeNode, IZoweNodeType, IZoweDatasetTreeNode, IZoweUSSTreeNode, IZoweJobTreeNode, SortDirection } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; import * as nls from "vscode-nls"; import { IZosFilesResponse, imperative } from "@zowe/cli"; @@ -45,6 +45,11 @@ export const JOB_SUBMIT_DIALOG_OPTS = [ localize("zowe.jobs.confirmSubmission.allJobs", "All jobs"), ]; +export const SORT_OPTS_TO_ENUM: Record = { + [localize("sort.asc", "Ascending")]: SortDirection.Ascending, + [localize("sort.desc", "Descending")]: SortDirection.Descending, +}; + export function filterTreeByString(value: string, treeItems: vscode.QuickPickItem[]): vscode.QuickPickItem[] { ZoweLogger.trace("shared.utils.filterTreeByString called."); const filteredArray: vscode.QuickPickItem[] = []; diff --git a/yarn.lock b/yarn.lock index 08e9d1f09a..37ae9cb03e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4573,6 +4573,11 @@ dateformat@3.0.2: resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.2.tgz#9a4df4bff158ac2f34bc637abdb15471607e1659" integrity sha1-mk30v/FYrC80vGN6vbFUcWB+Flk= +dayjs@^1.11.10: + version "1.11.10" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== + debug-fabulous@1.X: version "1.1.0" resolved "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz#af8a08632465224ef4174a9f06308c3c2a1ebc8e" From 7de9bb36e6c90829fee09a2a43ec11d0c7b2b1b5 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Mon, 9 Oct 2023 14:32:22 -0400 Subject: [PATCH 25/47] fix: some SonarCloud code smells Signed-off-by: Trae Yelovich --- .../__tests__/__unit__/job/ZoweJobNode.unit.test.ts | 5 ++--- packages/zowe-explorer/src/dataset/DatasetTree.ts | 13 +++++-------- .../zowe-explorer/src/dataset/ZoweDatasetNode.ts | 6 +++--- packages/zowe-explorer/src/job/ZosJobsProvider.ts | 5 ++--- packages/zowe-explorer/src/job/init.ts | 2 +- 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts index ec06c21ef7..ac20a2055c 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/ZoweJobNode.unit.test.ts @@ -583,9 +583,8 @@ describe("ZoweJobNode unit tests - Function saveSearch", () => { const expectedJob = favJob; expectedJob.contextValue = globals.JOBS_SESSION_CONTEXT + globals.FAV_SUFFIX; - const savedFavJob = await globalMocks.testJobsProvider.saveSearch(favJob); - - expect(savedFavJob).toEqual(expectedJob); + globalMocks.testJobsProvider.saveSearch(favJob); + expect(expectedJob.contextValue).toEqual(favJob.contextValue); }); }); diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index a7e17bf326..0e002eba92 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1350,15 +1350,12 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { + // children nodes already exist, sort and repaint to avoid extra refresh + node.children.sort(ZoweDatasetNode.sortBy(node.sort)); + this.nodeDataChanged(node); } else { - // Only sort the PDS members for this PDS - if (node.children != null && node.children.length > 0) { - // children nodes already exist, sort and repaint to avoid extra refresh - node.children.sort(ZoweDatasetNode.sortBy(node.sort)); - this.nodeDataChanged(node); - } else { - this.refreshElement(node); - } + this.refreshElement(node); } } diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index 1cfd7b2f8e..364cc2b983 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -321,13 +321,13 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } switch (sort.method) { - case DatasetSortOpts.Name: - default: - return (a.label as string) < (b.label as string) ? sortLessThan : sortGreaterThan; case DatasetSortOpts.LastModified: return a.stats?.m4date < b.stats?.m4date ? sortLessThan : sortGreaterThan; case DatasetSortOpts.UserId: return a.stats?.user < b.stats?.user ? sortLessThan : sortGreaterThan; + case DatasetSortOpts.Name: + default: + return (a.label as string) < (b.label as string) ? sortLessThan : sortGreaterThan; } }; } diff --git a/packages/zowe-explorer/src/job/ZosJobsProvider.ts b/packages/zowe-explorer/src/job/ZosJobsProvider.ts index c1c3dd0f2d..f87700e297 100644 --- a/packages/zowe-explorer/src/job/ZosJobsProvider.ts +++ b/packages/zowe-explorer/src/job/ZosJobsProvider.ts @@ -12,7 +12,7 @@ import * as vscode from "vscode"; import * as globals from "../globals"; import { IJob, imperative } from "@zowe/cli"; -import { Gui, ValidProfileEnum, IZoweTree, IZoweJobTreeNode, PersistenceSchemaEnum, NodeInteraction, JobSortOpts } from "@zowe/zowe-explorer-api"; +import { Gui, ValidProfileEnum, IZoweTree, IZoweJobTreeNode, PersistenceSchemaEnum, NodeInteraction } from "@zowe/zowe-explorer-api"; import { FilterItem, errorHandling } from "../utils/ProfilesUtils"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; @@ -159,10 +159,9 @@ export class ZosJobsProvider extends ZoweTreeProvider implements IZoweTree void jobsProvider.saveSearch(node))); + context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.saveSearch", (node): void => jobsProvider.saveSearch(node))); context.subscriptions.push( vscode.commands.registerCommand("zowe.jobs.removeSearchFavorite", async (node): Promise => jobsProvider.removeFavorite(node)) ); From 0801cee5be053ddfc85fed67402000f19790aa88 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Mon, 9 Oct 2023 17:04:09 -0400 Subject: [PATCH 26/47] tests(ds): Bump patch coverage for DatasetTree.filterPdsMembers Signed-off-by: Trae Yelovich --- .../__unit__/dataset/DatasetTree.unit.test.ts | 105 +++++++++++++++++- .../zowe-explorer/src/dataset/DatasetTree.ts | 18 +-- 2 files changed, 110 insertions(+), 13 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index 6063c7f6b4..42f33d7df9 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -2697,19 +2697,20 @@ describe("Dataset Tree Unit Tests - Function initializeFavorites", () => { }); }); -describe("Dataset Tree Unit Tests - Function sortBy", () => { +describe("Dataset Tree Unit Tests - Function sortPdsMembers", () => { const testTree = new DatasetTree(); - const mocks = { + const getBlockMocks = (): Record => ({ nodeDataChanged: jest.spyOn(DatasetTree.prototype, "nodeDataChanged"), refreshElement: jest.spyOn(DatasetTree.prototype, "refreshElement"), showQuickPick: jest.spyOn(Gui, "showQuickPick"), getParent: jest.spyOn(ZoweDatasetNode.prototype, "getParent"), - }; + }); const testSession = new ZoweDatasetNode("testSession", vscode.TreeItemCollapsibleState.Collapsed, null, createISession()); const testPds = new ZoweDatasetNode("testPds", vscode.TreeItemCollapsibleState.Collapsed, testSession, createISession()); testPds.contextValue = globals.DS_PDS_CONTEXT; beforeEach(() => { + const mocks = getBlockMocks(); mocks.getParent.mockReturnValue(testSession); testPds.children = [ { label: "A", stats: { user: "someUser", m4date: Date.now() }, getParent: mocks.getParent } as unknown as ZoweDatasetNode, @@ -2728,26 +2729,29 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { }); afterEach(() => { + const mocks = getBlockMocks(); for (const mock of Object.values(mocks)) { mock.mockClear(); } }); afterAll(() => { + const mocks = getBlockMocks(); for (const mock of Object.values(mocks)) { mock.mockRestore(); } }); it("calls refreshElement if no children exist", async () => { - // case 1: called on session node + const mocks = getBlockMocks(); + // case 1: called on PDS node mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); testPds.children = []; await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); - // case 2: called on PDS node + // case 2: called on session node mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); testSession.children = []; await testTree.sortPdsMembers(testSession); @@ -2756,6 +2760,7 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { }); it("sorts by name", async () => { + const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); @@ -2764,6 +2769,7 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { }); it("sorts by last modified date", async () => { + const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce({ label: "$(calendar) Date Modified" }); await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); @@ -2772,6 +2778,7 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { }); it("sorts by user ID", async () => { + const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce({ label: "$(account) User ID" }); await testTree.sortPdsMembers(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); @@ -2779,3 +2786,91 @@ describe("Dataset Tree Unit Tests - Function sortBy", () => { expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "A", "C"]); }); }); + +describe("Dataset Tree Unit Tests - Function filterPdsMembers", () => { + const testTree = new DatasetTree(); + const getBlockMocks = (): Record => ({ + nodeDataChanged: jest.spyOn(DatasetTree.prototype, "nodeDataChanged"), + refreshElement: jest.spyOn(DatasetTree.prototype, "refreshElement"), + showQuickPick: jest.spyOn(Gui, "showQuickPick"), + getParent: jest.spyOn(ZoweDatasetNode.prototype, "getParent"), + showInputBox: jest.spyOn(Gui, "showInputBox"), + }); + const testSession = new ZoweDatasetNode("testSession", vscode.TreeItemCollapsibleState.Collapsed, null, createISession()); + const testPds = new ZoweDatasetNode("testPds", vscode.TreeItemCollapsibleState.Collapsed, testSession, createISession()); + testPds.contextValue = globals.DS_PDS_CONTEXT; + + beforeEach(() => { + const mocks = getBlockMocks(); + mocks.getParent.mockReturnValue(testSession); + testPds.children = [ + { label: "A", stats: { user: "someUser", m4date: Date.now() }, getParent: mocks.getParent } as unknown as ZoweDatasetNode, + { + label: "B", + stats: { user: "anotherUser", m4date: Date.parse("2022-01-01T12:00:00") }, + getParent: mocks.getParent, + } as unknown as ZoweDatasetNode, + { + label: "C", + stats: { user: "someUser", m4date: Date.parse("2022-03-15T16:30:00") }, + getParent: mocks.getParent, + } as unknown as ZoweDatasetNode, + ]; + testSession.children = [testPds]; + }); + + afterEach(() => { + const mocks = getBlockMocks(); + for (const mock of Object.values(mocks)) { + mock.mockReset(); + } + }); + + afterAll(() => { + const mocks = getBlockMocks(); + for (const mock of Object.values(mocks)) { + mock.mockRestore(); + } + }); + + it("calls refreshElement if no children exist", async () => { + const mocks = getBlockMocks(); + // case 1: called on PDS node + mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); + mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); + testPds.children = []; + await testTree.filterPdsMembers(testPds); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); + testPds.filter = undefined as any; + + // case 2: called on session node + mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); + mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); + testSession.children = []; + await testTree.filterPdsMembers(testSession); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + expect(mocks.refreshElement).toHaveBeenCalledWith(testSession); + }); + + it("filters by last modified date", async () => { + const mocks = getBlockMocks(); + mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); + mocks.showInputBox.mockResolvedValueOnce("2022-03-15"); + await testTree.filterPdsMembers(testPds); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["C"]); + testPds.filter = undefined as any; + }); + + it("filters by user ID", async () => { + const mocks = getBlockMocks(); + mocks.showQuickPick.mockResolvedValue("$(account) User ID" as any); + mocks.showInputBox.mockResolvedValueOnce("anotherUser"); + await testTree.filterPdsMembers(testPds); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B"]); + }); +}); diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index 0e002eba92..a73eca237a 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1360,6 +1360,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { c.children = c.children.filter(ZoweDatasetNode.filterBy(newFilter)); this.nodeDataChanged(c); } else { @@ -1388,13 +1390,13 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0 && newFilter != null) { + + if (newFilter != null && node.children != null && node.children.length > 0) { // children nodes already exist, sort and repaint to avoid extra refresh node.children = node.children.filter(ZoweDatasetNode.filterBy(newFilter)); this.nodeDataChanged(node); From 4b287a96dafaa3fb8247c6624a25997144334d9d Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Mon, 9 Oct 2023 19:12:37 -0400 Subject: [PATCH 27/47] chore(ds/sort): code refactor, add typedoc, cleanup strings Signed-off-by: Trae Yelovich --- packages/zowe-explorer-api/src/index.ts | 1 + .../src/tree/IZoweTreeNode.ts | 44 +---- packages/zowe-explorer-api/src/tree/index.ts | 1 + .../zowe-explorer-api/src/tree/sorting.ts | 43 +++++ .../__unit__/dataset/DatasetTree.unit.test.ts | 22 +-- .../__unit__/dataset/init.unit.test.ts | 9 +- .../sample/src/dataset/DatasetTree.i18n.json | 7 +- .../zowe-explorer/src/dataset/DatasetTree.ts | 157 +++++++++++------- packages/zowe-explorer/src/dataset/init.ts | 4 +- packages/zowe-explorer/src/dataset/utils.ts | 5 - packages/zowe-explorer/src/job/actions.ts | 8 +- packages/zowe-explorer/src/shared/utils.ts | 5 +- 12 files changed, 181 insertions(+), 125 deletions(-) create mode 100644 packages/zowe-explorer-api/src/tree/sorting.ts diff --git a/packages/zowe-explorer-api/src/index.ts b/packages/zowe-explorer-api/src/index.ts index 33d3dad8c4..a9e308a3f0 100644 --- a/packages/zowe-explorer-api/src/index.ts +++ b/packages/zowe-explorer-api/src/index.ts @@ -23,6 +23,7 @@ export * from "./tree/ZoweExplorerTreeApi"; export * from "./tree/ZoweTreeNode"; export * from "./tree/IZoweTree"; export * from "./tree/IZoweTreeNode"; +export * from "./tree/sorting"; export * from "./utils"; export * from "./vscode/ZoweVsCodeExtension"; export * from "./vscode/ui"; diff --git a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts index 0055a72814..e5f0d3c1fd 100644 --- a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts @@ -13,6 +13,7 @@ import * as vscode from "vscode"; import { IJob, imperative } from "@zowe/cli"; import { IZoweTree } from "./IZoweTree"; import { FileAttributes } from "../utils/files"; +import { DatasetFilter, NodeSort } from "./sorting"; export type IZoweNodeType = IZoweDatasetTreeNode | IZoweUSSTreeNode | IZoweJobTreeNode; @@ -20,44 +21,6 @@ export enum NodeAction { Download = "download", } -export type DatasetStats = { - user: string; - m4date: Date; -}; - -export enum DatasetSortOpts { - Name, - LastModified, - UserId, -} - -export enum SortDirection { - Ascending, - Descending, -} - -export enum DatasetFilterOpts { - LastModified, - UserId, -} - -export type DatasetFilter = { - method: DatasetFilterOpts; - value: string; -}; - -export type NodeSort = { - method: DatasetSortOpts | JobSortOpts; - direction: SortDirection; -}; - -export enum JobSortOpts { - Id, - DateSubmitted, - Name, - ReturnCode, -} - /** * The base interface for Zowe tree nodes that are implemented by vscode.TreeItem. * @@ -162,6 +125,11 @@ export interface IZoweTreeNode { setSessionToChoice(sessionObj: imperative.Session): void; } +export type DatasetStats = { + user: string; + m4date: Date; +}; + /** * Extended interface for Zowe Dataset tree nodes. * diff --git a/packages/zowe-explorer-api/src/tree/index.ts b/packages/zowe-explorer-api/src/tree/index.ts index 420c04ac45..23d5dd01bc 100644 --- a/packages/zowe-explorer-api/src/tree/index.ts +++ b/packages/zowe-explorer-api/src/tree/index.ts @@ -9,6 +9,7 @@ * */ +export * from "./sorting"; export * from "./ZoweExplorerTreeApi"; export * from "./ZoweTreeNode"; export * from "./IZoweTree"; diff --git a/packages/zowe-explorer-api/src/tree/sorting.ts b/packages/zowe-explorer-api/src/tree/sorting.ts new file mode 100644 index 0000000000..cefcf68eb0 --- /dev/null +++ b/packages/zowe-explorer-api/src/tree/sorting.ts @@ -0,0 +1,43 @@ +/** + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + * + */ + +export enum DatasetSortOpts { + Name, + LastModified, + UserId, +} + +export enum SortDirection { + Ascending, + Descending, +} + +export enum DatasetFilterOpts { + LastModified, + UserId, +} + +export type DatasetFilter = { + method: DatasetFilterOpts; + value: string; +}; + +export type NodeSort = { + method: DatasetSortOpts | JobSortOpts; + direction: SortDirection; +}; + +export enum JobSortOpts { + Id, + DateSubmitted, + Name, + ReturnCode, +} diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index 42f33d7df9..893e1fc414 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -2697,7 +2697,7 @@ describe("Dataset Tree Unit Tests - Function initializeFavorites", () => { }); }); -describe("Dataset Tree Unit Tests - Function sortPdsMembers", () => { +describe("Dataset Tree Unit Tests - Function sortPdsMembersDialog", () => { const testTree = new DatasetTree(); const getBlockMocks = (): Record => ({ nodeDataChanged: jest.spyOn(DatasetTree.prototype, "nodeDataChanged"), @@ -2747,14 +2747,14 @@ describe("Dataset Tree Unit Tests - Function sortPdsMembers", () => { // case 1: called on PDS node mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); testPds.children = []; - await testTree.sortPdsMembers(testPds); + await testTree.sortPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); // case 2: called on session node mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); testSession.children = []; - await testTree.sortPdsMembers(testSession); + await testTree.sortPdsMembersDialog(testSession); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); expect(mocks.refreshElement).toHaveBeenCalledWith(testSession); }); @@ -2762,7 +2762,7 @@ describe("Dataset Tree Unit Tests - Function sortPdsMembers", () => { it("sorts by name", async () => { const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); - await testTree.sortPdsMembers(testPds); + await testTree.sortPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["A", "B", "C"]); @@ -2771,7 +2771,7 @@ describe("Dataset Tree Unit Tests - Function sortPdsMembers", () => { it("sorts by last modified date", async () => { const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce({ label: "$(calendar) Date Modified" }); - await testTree.sortPdsMembers(testPds); + await testTree.sortPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "C", "A"]); @@ -2780,14 +2780,14 @@ describe("Dataset Tree Unit Tests - Function sortPdsMembers", () => { it("sorts by user ID", async () => { const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce({ label: "$(account) User ID" }); - await testTree.sortPdsMembers(testPds); + await testTree.sortPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "A", "C"]); }); }); -describe("Dataset Tree Unit Tests - Function filterPdsMembers", () => { +describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { const testTree = new DatasetTree(); const getBlockMocks = (): Record => ({ nodeDataChanged: jest.spyOn(DatasetTree.prototype, "nodeDataChanged"), @@ -2839,7 +2839,7 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembers", () => { mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); testPds.children = []; - await testTree.filterPdsMembers(testPds); + await testTree.filterPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); testPds.filter = undefined as any; @@ -2848,7 +2848,7 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembers", () => { mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); testSession.children = []; - await testTree.filterPdsMembers(testSession); + await testTree.filterPdsMembersDialog(testSession); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); expect(mocks.refreshElement).toHaveBeenCalledWith(testSession); }); @@ -2857,7 +2857,7 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembers", () => { const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); mocks.showInputBox.mockResolvedValueOnce("2022-03-15"); - await testTree.filterPdsMembers(testPds); + await testTree.filterPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["C"]); @@ -2868,7 +2868,7 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembers", () => { const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValue("$(account) User ID" as any); mocks.showInputBox.mockResolvedValueOnce("anotherUser"); - await testTree.filterPdsMembers(testPds); + await testTree.filterPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B"]); diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts index fedbf07666..2697d100c1 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/init.unit.test.ts @@ -45,7 +45,8 @@ describe("Test src/dataset/extension", () => { onDidChangeConfiguration: jest.fn(), getTreeView: jest.fn(), refreshElement: jest.fn(), - sortPdsMembers: jest.fn(), + sortPdsMembersDialog: jest.fn(), + filterPdsMembersDialog: jest.fn(), }; const commands: IJestIt[] = [ { @@ -253,7 +254,11 @@ describe("Test src/dataset/extension", () => { }, { name: "zowe.ds.sortBy", - mock: [{ spy: jest.spyOn(dsProvider, "sortPdsMembers"), arg: [test.value] }], + mock: [{ spy: jest.spyOn(dsProvider, "sortPdsMembersDialog"), arg: [test.value] }], + }, + { + name: "zowe.ds.filterBy", + mock: [{ spy: jest.spyOn(dsProvider, "filterPdsMembersDialog"), arg: [test.value] }], }, { name: "onDidChangeConfiguration", diff --git a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json index c3ac5386eb..d59c6ecff8 100644 --- a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json @@ -33,10 +33,11 @@ "sort.asc": "Ascending", "sort.desc": "Descending", "sort.selectDirection": "Select a sorting direction", - "ds.clearAllFilters": "$(clear-all) Clear all filters", - "ds.clearPdsFilter": "$(clear-all) Clear filters for this PDS", + "ds.clearProfileFilter": "$(clear-all) Clear filter for profile", + "ds.clearPdsFilter": "$(clear-all) Clear filter for PDS", "ds.selectFilterOpt": "Set a filter for {0}", - "ds.filterEntry.title": "Enter a value to filter by", "ds.filterEntry.invalidDate": "Invalid date format specified", + "ds.filterEntry.title": "Enter a value to filter by", + "ds.filterEntry.invalid": "Invalid filter specified", "defaultFilterPrompt.option.prompt.search": "$(plus) Create a new filter. For example: HLQ.*, HLQ.aaa.bbb, HLQ.ccc.ddd(member)" } diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index a73eca237a..f21a6a0ffa 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -25,11 +25,14 @@ import { IZoweTreeNode, DatasetFilter, DatasetSortOpts, + SortDirection, + NodeSort, + DatasetFilterOpts, } from "@zowe/zowe-explorer-api"; import { Profiles } from "../Profiles"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; import { FilterDescriptor, FilterItem, errorHandling, syncSessionNode } from "../utils/ProfilesUtils"; -import { sortTreeItems, getAppName, getDocumentFilePath, SORT_OPTS_TO_ENUM } from "../shared/utils"; +import { sortTreeItems, getAppName, getDocumentFilePath, SORT_DIRS } from "../shared/utils"; import { ZoweTreeProvider } from "../abstract/ZoweTreeProvider"; import { ZoweDatasetNode } from "./ZoweDatasetNode"; import { getIconById, getIconByNode, IconId, IIconItem } from "../generators/icons"; @@ -39,7 +42,7 @@ import * as contextually from "../shared/context"; import { resetValidationSettings } from "../shared/actions"; import { closeOpenedTextFile } from "../utils/workspace"; import { IDataSet, IListOptions, imperative } from "@zowe/cli"; -import { DATASET_FILTER_KEYS, DATASET_FILTER_OPTS, DATASET_SORT_OPTS, validateDataSetName, validateMemberName } from "./utils"; +import { DATASET_FILTER_OPTS, DATASET_SORT_OPTS, validateDataSetName, validateMemberName } from "./utils"; import { SettingsConfig } from "../utils/SettingsConfig"; import { ZoweLogger } from "../utils/LoggerUtils"; import { TreeViewUtils } from "../utils/TreeViewUtils"; @@ -1293,19 +1296,57 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree { + public updateSortForNode(node: IZoweDatasetTreeNode, sortOpts: NodeSort, isSession: boolean): void { + node.sort = sortOpts; + + if (isSession) { + // if a session was selected, apply this sort to ALL PDS members + if (node.children != null && node.children.length > 0) { + // children nodes already exist, sort and repaint to avoid extra refresh + for (const c of node.children) { + if (contextually.isPds(c) && c.children) { + c.sort = node.sort; + c.children.sort(ZoweDatasetNode.sortBy(node.sort)); + this.nodeDataChanged(c); + } + } + } else { + this.refreshElement(node); + } + } else if (node.children != null && node.children.length > 0) { + // children nodes already exist, sort and repaint to avoid extra refresh + node.children.sort(ZoweDatasetNode.sortBy(node.sort)); + this.nodeDataChanged(node); + } else { + this.refreshElement(node); + } + } + + /** + * Presents a dialog to the user with options and methods for sorting PDS members. + * @param node The node that was interacted with (via icon or right-click -> "Sort PDS members...") + */ + public async sortPdsMembersDialog(node: IZoweDatasetTreeNode): Promise { const isSession = contextually.isSession(node); + // Assume defaults if a user hasn't selected any sort options yet + const sortOpts = node.sort ?? { + method: DatasetSortOpts.Name, + direction: SortDirection.Ascending, + }; + + // Adapt menus to user based on the node that was interacted with const specifier = isSession ? localize("ds.allPdsSort", "all PDS members in {0}", node.label as string) : localize("ds.singlePdsSort", "the PDS members in {0}", node.label as string); const selection = await Gui.showQuickPick( - DATASET_SORT_OPTS.map((sortOpt, i) => ({ - label: node.sort?.method === i ? `${sortOpt} $(check)` : sortOpt, - description: i === DATASET_SORT_OPTS.length - 1 ? Object.keys(SORT_OPTS_TO_ENUM)[node.sort.direction] : null, + DATASET_SORT_OPTS.map((opt, i) => ({ + label: sortOpts.method === i ? `${opt} $(check)` : opt, + description: i === DATASET_SORT_OPTS.length - 1 ? SORT_DIRS[sortOpts.direction] : null, })), { placeHolder: localize("ds.selectSortOpt", "Select a sorting option for {0}", specifier), @@ -1316,50 +1357,37 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { - // children nodes already exist, sort and repaint to avoid extra refresh - for (const c of node.children) { - if (contextually.isPds(c) && c.children) { - c.sort = node.sort; - c.children.sort(ZoweDatasetNode.sortBy(node.sort)); - this.nodeDataChanged(c); - } - } - } else { - this.refreshElement(node); - } - } else if (node.children != null && node.children.length > 0) { - // children nodes already exist, sort and repaint to avoid extra refresh - node.children.sort(ZoweDatasetNode.sortBy(node.sort)); - this.nodeDataChanged(node); - } else { - this.refreshElement(node); - } + // Update sort for node based on selections + this.updateSortForNode(node, { ...sortOpts, method: sortMethod }, isSession); } - public updateFilters(node: IZoweDatasetTreeNode, newFilter: DatasetFilter | null, isSession: boolean): void { + /** + * Updates or resets the filter for a given data set node. + * @param node The node whose filter should be updated/reset + * @param newFilter Either a valid `DatasetFilter` object, or `null` to reset the filter + * @param isSession Whether the node is a session + */ + public updateFilterForNode(node: IZoweDatasetTreeNode, newFilter: DatasetFilter | null, isSession: boolean): void { const oldFilter = node.filter; node.filter = newFilter; @@ -1406,15 +1434,20 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree { + /** + * Presents a dialog to the user with options and methods for sorting PDS members. + * @param node The data set node that was interacted with (via icon or right-click => "Filter PDS members...") + */ + public async filterPdsMembersDialog(node: IZoweDatasetTreeNode): Promise { const isSession = contextually.isSession(node); + // Adapt menus to user based on the node that was interacted with const specifier = isSession ? localize("ds.allPdsSort", "all PDS members in {0}", node.label as string) : localize("ds.singlePdsSort", "the PDS members in {0}", node.label as string); const clearFilter = isSession - ? localize("ds.clearAllFilters", "$(clear-all) Clear all filters") - : localize("ds.clearPdsFilter", "$(clear-all) Clear filters for this PDS"); + ? localize("ds.clearProfileFilter", "$(clear-all) Clear filter for profile") + : localize("ds.clearPdsFilter", "$(clear-all) Clear filter for PDS"); const selection = ( await Gui.showQuickPick( [...DATASET_FILTER_OPTS.map((sortOpt, i) => (node.filter?.method === i ? `${sortOpt} $(check)` : sortOpt)), clearFilter], @@ -1423,32 +1456,44 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree { + return dayjs(value).isValid() ? null : localize("ds.filterEntry.invalidDate", "Invalid date format specified"); + }; + const filter = await Gui.showInputBox({ title: localize("ds.filterEntry.title", "Enter a value to filter by"), placeHolder: "", - validateInput(value) { - return dayjs(value).isValid() ? null : localize("ds.filterEntry.invalidDate", "Invalid date format specified"); - }, + validateInput: + filterMethod === DatasetFilterOpts.LastModified + ? dateValidation + : (val): string => (val.length > 0 ? null : localize("ds.filterEntry.invalid", "Invalid filter specified")), }); + + // User dismissed filter entry, go back to filter selection if (filter == null) { + await this.filterPdsMembersDialog(node); return; } - const newFilter = { - method: filterMethod, - value: filter, - }; - - this.updateFilters(node, newFilter, isSession); + // Update filter for node based on selection & filter entry + this.updateFilterForNode( + node, + { + method: filterMethod, + value: filter, + }, + isSession + ); } } diff --git a/packages/zowe-explorer/src/dataset/init.ts b/packages/zowe-explorer/src/dataset/init.ts index 441b370455..01ffc33e0f 100644 --- a/packages/zowe-explorer/src/dataset/init.ts +++ b/packages/zowe-explorer/src/dataset/init.ts @@ -199,12 +199,12 @@ export async function initDatasetProvider(context: vscode.ExtensionContext): Pro context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogin", (node: IZoweTreeNode) => datasetProvider.ssoLogin(node))); context.subscriptions.push(vscode.commands.registerCommand("zowe.ds.ssoLogout", (node: IZoweTreeNode) => datasetProvider.ssoLogout(node))); context.subscriptions.push( - vscode.commands.registerCommand("zowe.ds.sortBy", async (node: IZoweDatasetTreeNode) => datasetProvider.sortPdsMembers(node)) + vscode.commands.registerCommand("zowe.ds.sortBy", async (node: IZoweDatasetTreeNode) => datasetProvider.sortPdsMembersDialog(node)) ); context.subscriptions.push( vscode.commands.registerCommand( "zowe.ds.filterBy", - async (node: IZoweDatasetTreeNode): Promise => datasetProvider.filterPdsMembers(node) + async (node: IZoweDatasetTreeNode): Promise => datasetProvider.filterPdsMembersDialog(node) ) ); context.subscriptions.push( diff --git a/packages/zowe-explorer/src/dataset/utils.ts b/packages/zowe-explorer/src/dataset/utils.ts index 40d861c577..0d8601dfad 100644 --- a/packages/zowe-explorer/src/dataset/utils.ts +++ b/packages/zowe-explorer/src/dataset/utils.ts @@ -30,11 +30,6 @@ export const DATASET_SORT_OPTS = [ export const DATASET_FILTER_OPTS = [localize("ds.sortByModified", "$(calendar) Date Modified"), localize("ds.sortByUserId", "$(account) User ID")]; -export const DATASET_FILTER_KEYS: Record = { - [localize("ds.sortByModified", "$(calendar) Date Modified")]: DatasetFilterOpts.LastModified, - [localize("ds.sortByUserId", "$(account) User ID")]: DatasetFilterOpts.UserId, -}; - export function getProfileAndDataSetName(node: IZoweNodeType): { profileName: string; dataSetName: string; diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index f6a110e615..854000b00f 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -19,7 +19,7 @@ import { Job, Spool } from "./ZoweJobNode"; import * as nls from "vscode-nls"; import SpoolProvider, { encodeJobFile, getSpoolFiles, matchSpool } from "../SpoolProvider"; import { ZoweLogger } from "../utils/LoggerUtils"; -import { SORT_OPTS_TO_ENUM, getDefaultUri } from "../shared/utils"; +import { SORT_DIRS, getDefaultUri } from "../shared/utils"; import { ZosJobsProvider } from "./ZosJobsProvider"; import { JOB_SORT_OPTS } from "./utils"; @@ -534,7 +534,7 @@ export async function sortJobs(session: IZoweJobTreeNode, jobsProvider: ZosJobsP const selection = await Gui.showQuickPick( JOB_SORT_OPTS.map((sortOpt, i) => ({ label: i === session.sort.method ? `${sortOpt} $(check)` : sortOpt, - description: i === JOB_SORT_OPTS.length - 1 ? Object.keys(SORT_OPTS_TO_ENUM)[session.sort.direction] : null, + description: i === JOB_SORT_OPTS.length - 1 ? SORT_DIRS[session.sort.direction] : null, })), { placeHolder: localize("jobs.selectSortOpt", "Select a sorting option for jobs in {0}", session.label as string), @@ -544,13 +544,13 @@ export async function sortJobs(session: IZoweJobTreeNode, jobsProvider: ZosJobsP return; } if (selection.label === localize("setSortDirection", "$(fold) Sort Direction")) { - const dir = await Gui.showQuickPick([localize("sort.asc", "Ascending"), localize("sort.desc", "Descending")], { + const dir = await Gui.showQuickPick(SORT_DIRS, { placeHolder: localize("sort.selectDirection", "Select a sorting direction"), }); if (dir != null) { session.sort = { ...(session.sort ?? { method: JobSortOpts.Id }), - direction: SORT_OPTS_TO_ENUM[dir], + direction: SORT_DIRS.indexOf(dir), }; } await sortJobs(session, jobsProvider); diff --git a/packages/zowe-explorer/src/shared/utils.ts b/packages/zowe-explorer/src/shared/utils.ts index c0c4bec81a..b0a5770d8c 100644 --- a/packages/zowe-explorer/src/shared/utils.ts +++ b/packages/zowe-explorer/src/shared/utils.ts @@ -45,10 +45,7 @@ export const JOB_SUBMIT_DIALOG_OPTS = [ localize("zowe.jobs.confirmSubmission.allJobs", "All jobs"), ]; -export const SORT_OPTS_TO_ENUM: Record = { - [localize("sort.asc", "Ascending")]: SortDirection.Ascending, - [localize("sort.desc", "Descending")]: SortDirection.Descending, -}; +export const SORT_DIRS: string[] = [localize("sort.asc", "Ascending"), localize("sort.desc", "Descending")]; export function filterTreeByString(value: string, treeItems: vscode.QuickPickItem[]): vscode.QuickPickItem[] { ZoweLogger.trace("shared.utils.filterTreeByString called."); From 30ebd6ecf5986f8a54b433dda75468c4b06364e3 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Mon, 9 Oct 2023 19:23:06 -0400 Subject: [PATCH 28/47] fix(ds/sort): some SonarCloud code smells Signed-off-by: Trae Yelovich --- packages/zowe-explorer/src/dataset/DatasetTree.ts | 6 +++--- packages/zowe-explorer/src/dataset/utils.ts | 2 +- packages/zowe-explorer/src/shared/utils.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index f21a6a0ffa..3974227d96 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1393,7 +1393,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { + if (node.children?.length > 0) { // children nodes already exist, sort and repaint to avoid extra refresh for (const c of node.children) { const asDs = c as IZoweDatasetTreeNode; @@ -1405,7 +1405,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { + if (newFilter != null && c.children?.length > 0) { c.children = c.children.filter(ZoweDatasetNode.filterBy(newFilter)); this.nodeDataChanged(c); } else { @@ -1424,7 +1424,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { + if (newFilter != null && node.children?.length > 0) { // children nodes already exist, sort and repaint to avoid extra refresh node.children = node.children.filter(ZoweDatasetNode.filterBy(newFilter)); this.nodeDataChanged(node); diff --git a/packages/zowe-explorer/src/dataset/utils.ts b/packages/zowe-explorer/src/dataset/utils.ts index 0d8601dfad..956ae28f31 100644 --- a/packages/zowe-explorer/src/dataset/utils.ts +++ b/packages/zowe-explorer/src/dataset/utils.ts @@ -11,7 +11,7 @@ import * as globals from "../globals"; import * as nls from "vscode-nls"; -import { DatasetFilterOpts, IZoweNodeType } from "@zowe/zowe-explorer-api"; +import { IZoweNodeType } from "@zowe/zowe-explorer-api"; import { ZoweLogger } from "../utils/LoggerUtils"; // Set up localization diff --git a/packages/zowe-explorer/src/shared/utils.ts b/packages/zowe-explorer/src/shared/utils.ts index b0a5770d8c..af0ce5a45c 100644 --- a/packages/zowe-explorer/src/shared/utils.ts +++ b/packages/zowe-explorer/src/shared/utils.ts @@ -16,7 +16,7 @@ import * as vscode from "vscode"; import * as path from "path"; import * as globals from "../globals"; import * as os from "os"; -import { Gui, IZoweTreeNode, IZoweNodeType, IZoweDatasetTreeNode, IZoweUSSTreeNode, IZoweJobTreeNode, SortDirection } from "@zowe/zowe-explorer-api"; +import { Gui, IZoweTreeNode, IZoweNodeType, IZoweDatasetTreeNode, IZoweUSSTreeNode, IZoweJobTreeNode } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../ZoweExplorerApiRegister"; import * as nls from "vscode-nls"; import { IZosFilesResponse, imperative } from "@zowe/cli"; From 3750998f47e09c5a218e34686f174c27e10d85df Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Mon, 9 Oct 2023 21:38:36 -0400 Subject: [PATCH 29/47] test(ds): Add test cases for filter/sort options Signed-off-by: Trae Yelovich --- .../__unit__/dataset/DatasetTree.unit.test.ts | 48 +++++++++++------ .../src/abstract/ZoweTreeProvider.ts | 2 +- .../zowe-explorer/src/dataset/DatasetTree.ts | 53 +++++++++---------- 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index 893e1fc414..05c8af3d5a 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -15,7 +15,7 @@ import * as fs from "fs"; import * as zowe from "@zowe/cli"; import { DatasetTree } from "../../../src/dataset/DatasetTree"; import { ZoweDatasetNode } from "../../../src/dataset/ZoweDatasetNode"; -import { Gui, IZoweDatasetTreeNode, ProfilesCache, ValidProfileEnum } from "@zowe/zowe-explorer-api"; +import { DatasetFilterOpts, Gui, IZoweDatasetTreeNode, ProfilesCache, ValidProfileEnum } from "@zowe/zowe-explorer-api"; import { ZoweExplorerApiRegister } from "../../../src/ZoweExplorerApiRegister"; import { Profiles } from "../../../src/Profiles"; import * as utils from "../../../src/utils/ProfilesUtils"; @@ -2742,21 +2742,21 @@ describe("Dataset Tree Unit Tests - Function sortPdsMembersDialog", () => { } }); - it("calls refreshElement if no children exist", async () => { + // for sorting, we shouldn't need to refresh since all nodes + // should be intact, just in a different order + it("does nothing if no children exist", async () => { const mocks = getBlockMocks(); // case 1: called on PDS node mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); testPds.children = []; await testTree.sortPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); // case 2: called on session node mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); testSession.children = []; await testTree.sortPdsMembersDialog(testSession); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - expect(mocks.refreshElement).toHaveBeenCalledWith(testSession); }); it("sorts by name", async () => { @@ -2785,6 +2785,17 @@ describe("Dataset Tree Unit Tests - Function sortPdsMembersDialog", () => { expect(mocks.refreshElement).not.toHaveBeenCalled(); expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "A", "C"]); }); + + it("returns to sort selection dialog when sort direction selection is canceled", async () => { + const sortPdsMembersDialog = jest.spyOn(testTree, "sortPdsMembersDialog"); + const mocks = getBlockMocks(); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(fold) Sort Direction" }); + mocks.showQuickPick.mockResolvedValueOnce(undefined); + await testTree.sortPdsMembersDialog(testPds); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(sortPdsMembersDialog).toHaveBeenCalledTimes(2); + }); }); describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { @@ -2817,6 +2828,7 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { } as unknown as ZoweDatasetNode, ]; testSession.children = [testPds]; + testPds.filter = undefined as any; }); afterEach(() => { @@ -2833,40 +2845,42 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { } }); - it("calls refreshElement if no children exist", async () => { + it("calls refreshElement if children were removed from a previous filter", async () => { const mocks = getBlockMocks(); - // case 1: called on PDS node - mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); + mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); + + testPds.filter = { method: DatasetFilterOpts.UserId, value: "invalidUserId" }; testPds.children = []; await testTree.filterPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); - testPds.filter = undefined as any; + }); - // case 2: called on session node - mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); - mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); - testSession.children = []; - await testTree.filterPdsMembersDialog(testSession); + it("returns to filter selection dialog when filter entry is canceled", async () => { + const filterPdsMembersSpy = jest.spyOn(testTree, "filterPdsMembersDialog"); + const mocks = getBlockMocks(); + mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); + mocks.showInputBox.mockResolvedValueOnce(undefined); + await testTree.filterPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - expect(mocks.refreshElement).toHaveBeenCalledWith(testSession); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(filterPdsMembersSpy).toHaveBeenCalledTimes(2); }); it("filters by last modified date", async () => { const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValue("$(calendar) Date Modified" as any); + mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); mocks.showInputBox.mockResolvedValueOnce("2022-03-15"); await testTree.filterPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); expect(mocks.refreshElement).not.toHaveBeenCalled(); expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["C"]); - testPds.filter = undefined as any; }); it("filters by user ID", async () => { const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValue("$(account) User ID" as any); + mocks.showQuickPick.mockResolvedValueOnce("$(account) User ID" as any); mocks.showInputBox.mockResolvedValueOnce("anotherUser"); await testTree.filterPdsMembersDialog(testPds); expect(mocks.nodeDataChanged).toHaveBeenCalled(); diff --git a/packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts b/packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts index afbdfef2e2..eddd2868d5 100644 --- a/packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts +++ b/packages/zowe-explorer/src/abstract/ZoweTreeProvider.ts @@ -87,7 +87,7 @@ export class ZoweTreeProvider { /** * Fire the "onDidChangeTreeData" event to signal that a node in the tree has changed. - * Unlike `refreshElement`, this function does NOT signal a refresh for the given node - + * Unlike `refreshElement`, this function does *not* signal a refresh for the given node - * it simply tells VS Code to repaint the node in the tree. * @param node The node that should be repainted */ diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index 3974227d96..4ac818c759 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1305,7 +1305,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { + if (node.children?.length > 0) { // children nodes already exist, sort and repaint to avoid extra refresh for (const c of node.children) { if (contextually.isPds(c) && c.children) { @@ -1314,15 +1314,11 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { + } else if (node.children?.length > 0) { // children nodes already exist, sort and repaint to avoid extra refresh node.children.sort(ZoweDatasetNode.sortBy(node.sort)); this.nodeDataChanged(node); - } else { - this.refreshElement(node); } } @@ -1391,16 +1387,22 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { // children nodes already exist, sort and repaint to avoid extra refresh for (const c of node.children) { const asDs = c as IZoweDatasetTreeNode; - if (contextually.isPds(c) && c.children) { - // If a filter was NOT set for this PDS AND there was an old session-wide filter set, - // refresh it to get any missing nodes back since it will use the new session-wide filter - if (asDs.filter == null && oldFilter != null) { + + // PDS-level filters should have precedence over a session-level filter + if (asDs.filter != null) { + continue; + } + + if (contextually.isPds(c)) { + // If there was an old session-wide filter set: refresh to get any + // missing nodes - new filter will be applied + if (oldFilter != null) { this.refreshElement(c); continue; } @@ -1413,24 +1415,21 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree 0) { - // children nodes already exist, sort and repaint to avoid extra refresh - node.children = node.children.filter(ZoweDatasetNode.filterBy(newFilter)); - this.nodeDataChanged(node); - } else { - this.refreshElement(node); - } + // Updating filter for PDS node + // if a filter was already set, just refresh to grab any missing nodes + if (oldFilter != null) { + this.refreshElement(node); + return; + } + + // since there wasn't a previous filter, sort and repaint existing nodes + if (newFilter != null && node.children?.length > 0) { + node.children = node.children.filter(ZoweDatasetNode.filterBy(newFilter)); + this.nodeDataChanged(node); } } From 5c7ac3943fedcbc66d7c180e13c822fd9f97ad65 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Tue, 10 Oct 2023 09:27:26 -0400 Subject: [PATCH 30/47] fix: sort direction should only apply to PDS members - Also adds unit tests for dataset filtering, sorting, and sorting jobs Signed-off-by: Trae Yelovich --- .../__unit__/dataset/DatasetTree.unit.test.ts | 37 +++++++++++++++++-- .../__unit__/job/actions.unit.test.ts | 19 +++++++++- .../src/dataset/ZoweDatasetNode.ts | 24 +++++++----- packages/zowe-explorer/src/job/actions.ts | 7 +--- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index 05c8af3d5a..57b901c912 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -2845,7 +2845,7 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { } }); - it("calls refreshElement if children were removed from a previous filter", async () => { + it("calls refreshElement if PDS children were removed from a previous filter", async () => { const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); @@ -2868,7 +2868,7 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { expect(filterPdsMembersSpy).toHaveBeenCalledTimes(2); }); - it("filters by last modified date", async () => { + it("filters single PDS by last modified date", async () => { const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); mocks.showInputBox.mockResolvedValueOnce("2022-03-15"); @@ -2878,7 +2878,7 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["C"]); }); - it("filters by user ID", async () => { + it("filters single PDS by user ID", async () => { const mocks = getBlockMocks(); mocks.showQuickPick.mockResolvedValueOnce("$(account) User ID" as any); mocks.showInputBox.mockResolvedValueOnce("anotherUser"); @@ -2887,4 +2887,35 @@ describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { expect(mocks.refreshElement).not.toHaveBeenCalled(); expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B"]); }); + + it("filters PDS members using the session node filter", async () => { + const mocks = getBlockMocks(); + const uidString = "$(account) User ID" as any; + const anotherUser = "anotherUser"; + mocks.showQuickPick.mockResolvedValueOnce(uidString).mockResolvedValueOnce(uidString); + mocks.showInputBox.mockResolvedValueOnce(anotherUser).mockResolvedValueOnce(anotherUser); + + // case 1: old filter was set on session, just refresh PDS to use new filter + testSession.filter = { + method: DatasetFilterOpts.LastModified, + value: "2020-01-01", + }; + await testTree.filterPdsMembersDialog(testSession); + expect(mocks.refreshElement).toHaveBeenCalled(); + + // case 2: no old filter present, PDS has children to be filtered + testSession.filter = undefined; + await testTree.filterPdsMembersDialog(testSession); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + }); + + it("clears filter for a PDS when selected in dialog", async () => { + const mocks = getBlockMocks(); + const resp = "$(clear-all) Clear filter for PDS" as any; + mocks.showQuickPick.mockResolvedValueOnce(resp); + const updateFilterForNode = jest.spyOn(DatasetTree.prototype, "updateFilterForNode"); + await testTree.filterPdsMembersDialog(testPds); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(updateFilterForNode).toHaveBeenCalledWith(testPds, null, false); + }); }); diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index b80d907591..78c4e2a6d8 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -1394,7 +1394,7 @@ describe("Job Actions Unit Tests - Misc. functions", () => { expect(statusMsgSpy).toHaveBeenCalledWith(`$(sync~spin) Polling: ${testDoc.fileName}...`); }); }); -describe("sortjobsby function", () => { +describe("sortJobs function", () => { afterEach(() => { jest.restoreAllMocks(); }); @@ -1456,6 +1456,23 @@ describe("sortjobsby function", () => { expect(sortbyretcodespy).toHaveBeenCalled(); expect(sortbyretcodespy.mock.calls[0][0].children).toStrictEqual(expected.mSessionNodes[0].children); }); + + it("updates sort options after selecting sort direction; returns user to sort selection", async () => { + const globalMocks = createGlobalMocks(); + const testtree = new ZosJobsProvider(); + testtree.mSessionNodes[0].sort = { + method: JobSortOpts.Id, + direction: SortDirection.Ascending, + }; + testtree.mSessionNodes[0].children = [globalMocks()[0]]; + const jobsSortBy = jest.spyOn(ZosJobsProvider.prototype, "sortBy"); + const quickPickSpy = jest.spyOn(Gui, "showQuickPick").mockResolvedValueOnce({ label: "$(fold) Sort Direction" }); + quickPickSpy.mockResolvedValueOnce("Descending" as any); + await jobActions.sortJobs(testtree.mSessionNodes[0], testtree); + expect(testtree.mSessionNodes[0].sort.direction).toBe(SortDirection.Descending); + expect(quickPickSpy).toHaveBeenCalledTimes(3); + expect(jobsSortBy).not.toHaveBeenCalled(); + }); }); describe("Job Actions Unit Tests - Filter Jobs", () => { diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index 364cc2b983..9b83a8915d 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -57,11 +57,8 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod public ongoingActions: Record> = {}; public wasDoubleClicked: boolean = false; public stats: DatasetStats; - public sort: NodeSort = { - method: DatasetSortOpts.Name, - direction: SortDirection.Ascending, - }; - public filter: DatasetFilter; + public sort?: NodeSort; + public filter?: DatasetFilter; /** * Creates an instance of ZoweDatasetNode @@ -96,6 +93,15 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod if (icon) { this.iconPath = icon.path; } + + if (this.getParent() == null) { + // set default sort options for session nodes + this.sort = { + method: DatasetSortOpts.Name, + direction: SortDirection.Ascending, + }; + } + if (!globals.ISTHEIA && contextually.isSession(this)) { this.id = this.label as string; } @@ -312,14 +318,14 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod */ public static sortBy(sort: NodeSort): (a: IZoweDatasetTreeNode, b: IZoweDatasetTreeNode) => number { return (a, b): number => { - const sortLessThan = sort.direction == SortDirection.Ascending ? -1 : 1; - const sortGreaterThan = sortLessThan * -1; - const aParent = a.getParent(); if (aParent == null || !contextually.isPds(aParent)) { - return (a.label as string) < (b.label as string) ? sortLessThan : sortGreaterThan; + return (a.label as string) < (b.label as string) ? -1 : 1; } + const sortLessThan = sort.direction == SortDirection.Ascending ? -1 : 1; + const sortGreaterThan = sortLessThan * -1; + switch (sort.method) { case DatasetSortOpts.LastModified: return a.stats?.m4date < b.stats?.m4date ? sortLessThan : sortGreaterThan; diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 854000b00f..d8ebbab75f 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -557,12 +557,7 @@ export async function sortJobs(session: IZoweJobTreeNode, jobsProvider: ZosJobsP return; } - const optIndex = JOB_SORT_OPTS.indexOf(selection.label.replace(" $(check)", "")); - if (optIndex == -1) { - return; - } - - session.sort.method = optIndex; + session.sort.method = JOB_SORT_OPTS.indexOf(selection.label.replace(" $(check)", "")); jobsProvider.sortBy(session); } From 0c9acae2b3af94237a4ed3ddac0ca9ce448fcbb9 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Tue, 10 Oct 2023 09:46:16 -0400 Subject: [PATCH 31/47] fix(test): Fix 'fake session node' sort options in test case Signed-off-by: Trae Yelovich --- .../zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts index f7a716e91c..f52fb1af0a 100644 --- a/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/ZoweNode.unit.test.ts @@ -18,7 +18,7 @@ import { List, imperative } from "@zowe/cli"; import { Profiles } from "../../src/Profiles"; import * as globals from "../../src/globals"; import { ZoweLogger } from "../../src/utils/LoggerUtils"; -import { DatasetSortOpts } from "@zowe/zowe-explorer-api"; +import { DatasetSortOpts, SortDirection } from "@zowe/zowe-explorer-api"; describe("Unit Tests (Jest)", () => { // Globals @@ -355,7 +355,10 @@ describe("Unit Tests (Jest)", () => { }; }), }); - const sessionNode = { getSessionNode: jest.fn(), sortMethod: DatasetSortOpts.Name } as unknown as ZoweDatasetNode; + const sessionNode = { + getSessionNode: jest.fn(), + sort: { method: DatasetSortOpts.Name, direction: SortDirection.Ascending }, + } as unknown as ZoweDatasetNode; const getSessionNodeSpy = jest.spyOn(ZoweDatasetNode.prototype, "getSessionNode").mockReturnValue(sessionNode); // Creating a rootNode const pds = new ZoweDatasetNode( From b1ba6a717c0270f2258da93e5f76d50fdf719152 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Tue, 10 Oct 2023 10:05:14 -0400 Subject: [PATCH 32/47] fix, chore: remove unnecessary refresh in sortBy; update CHANGELOGS Signed-off-by: Trae Yelovich --- packages/zowe-explorer-api/CHANGELOG.md | 3 +++ packages/zowe-explorer/CHANGELOG.md | 7 +++---- packages/zowe-explorer/src/job/ZosJobsProvider.ts | 2 -- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/zowe-explorer-api/CHANGELOG.md b/packages/zowe-explorer-api/CHANGELOG.md index c8fc4db2b2..7dc201454d 100644 --- a/packages/zowe-explorer-api/CHANGELOG.md +++ b/packages/zowe-explorer-api/CHANGELOG.md @@ -8,6 +8,9 @@ All notable changes to the "zowe-explorer-api" extension will be documented in t - Added optional `getTag` function to `ZoweExplorerAPI.IUss` for getting the tag of a file on USS. - Added new API {ZE Extender MetaData} to allow extenders to have the metadata of registered extenders to aid in team configuration file creation from a view that isn't Zowe Explorer's. [#2394](https://github.com/zowe/vscode-extension-for-zowe/issues/2394) +- Add `sort` and `filter` optional variables for storing sort/filter options alongside tree nodes. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) +- Add `stats` optional variable for storing dataset stats (such as user, modified date, etc.) +- Add option enums and types for sorting, filtering and sort direction in tree nodes. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) ### Bug fixes diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 6dc1dadb4c..ae5535716e 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -6,12 +6,11 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen ### New features and enhancements -- Added "Sort Jobs" feature for job nodes in Jobs tree view. [#2257](https://github.com/zowe/vscode-extension-for-zowe/issues/2251) -- Introduce a new user interface for managing profiles via right-click action "Manage Profile". +- Added "Sort Jobs" feature for job nodes in Jobs tree view: accessible via sort icon or right-clicking on session node. [#2257](https://github.com/zowe/vscode-extension-for-zowe/issues/2251) - Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) - Added new API {ZE Extender MetaData} to allow extenders to have the metadata of registered extenders to aid in team configuration file creation from a view that isn't Zowe Explorer's. [#2394](https://github.com/zowe/vscode-extension-for-zowe/issues/2394) -- Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) -- Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) +- Added "Sort PDS members" feature in Data Sets tree view: accessible via sort icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) +- Added "Filter PDS members" feature in Data Sets tree view: accessible via filter icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) ### Bug fixes diff --git a/packages/zowe-explorer/src/job/ZosJobsProvider.ts b/packages/zowe-explorer/src/job/ZosJobsProvider.ts index f87700e297..1da57d443b 100644 --- a/packages/zowe-explorer/src/job/ZosJobsProvider.ts +++ b/packages/zowe-explorer/src/job/ZosJobsProvider.ts @@ -1126,8 +1126,6 @@ export class ZosJobsProvider extends ZoweTreeProvider implements IZoweTree Date: Tue, 10 Oct 2023 14:15:57 -0400 Subject: [PATCH 33/47] fix(ds/filter): Refresh PDS if session/PDS had a previous filter Signed-off-by: Trae Yelovich --- packages/zowe-explorer/src/dataset/DatasetTree.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index 4ac818c759..8520764a3b 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1420,8 +1420,9 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree Date: Tue, 10 Oct 2023 18:29:04 -0400 Subject: [PATCH 34/47] fix(ds): Add sort/filter support for FTP response; fix test cases Signed-off-by: Trae Yelovich --- .../__unit__/dataset/DatasetTree.unit.test.ts | 351 ++++++++---------- .../src/dataset/ZoweDatasetNode.ts | 12 +- 2 files changed, 174 insertions(+), 189 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index 57b901c912..f8e6ddcc98 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -2696,36 +2696,34 @@ describe("Dataset Tree Unit Tests - Function initializeFavorites", () => { expect(() => testTree.initializeFavorites(log)).not.toThrow(); }); }); +describe("Dataset Tree Unit Tests - Sorting and Filtering operations", () => { + const tree = new DatasetTree(); + const nodesForSuite = (): Record => { + const session = new ZoweDatasetNode("testSession", vscode.TreeItemCollapsibleState.Collapsed, null, createISession()); + session.contextValue = globals.DS_SESSION_CONTEXT; + const pds = new ZoweDatasetNode("testPds", vscode.TreeItemCollapsibleState.Collapsed, session, createISession()); + pds.contextValue = globals.DS_PDS_CONTEXT; + + const nodeA = new ZoweDatasetNode("A", vscode.TreeItemCollapsibleState.Collapsed, pds, createISession()); + nodeA.stats = { user: "someUser", m4date: new Date() }; + const nodeB = new ZoweDatasetNode("B", vscode.TreeItemCollapsibleState.Collapsed, pds, createISession()); + nodeB.stats = { user: "anotherUser", m4date: new Date("2022-01-01T12:00:00") }; + const nodeC = new ZoweDatasetNode("C", vscode.TreeItemCollapsibleState.Collapsed, pds, createISession()); + nodeC.stats = { user: "someUser", m4date: new Date("2022-03-15T16:30:00") }; + pds.children = [nodeA, nodeB, nodeC]; + session.children = [pds]; + + return { + session, + pds, + }; + }; -describe("Dataset Tree Unit Tests - Function sortPdsMembersDialog", () => { - const testTree = new DatasetTree(); const getBlockMocks = (): Record => ({ nodeDataChanged: jest.spyOn(DatasetTree.prototype, "nodeDataChanged"), refreshElement: jest.spyOn(DatasetTree.prototype, "refreshElement"), showQuickPick: jest.spyOn(Gui, "showQuickPick"), - getParent: jest.spyOn(ZoweDatasetNode.prototype, "getParent"), - }); - const testSession = new ZoweDatasetNode("testSession", vscode.TreeItemCollapsibleState.Collapsed, null, createISession()); - const testPds = new ZoweDatasetNode("testPds", vscode.TreeItemCollapsibleState.Collapsed, testSession, createISession()); - testPds.contextValue = globals.DS_PDS_CONTEXT; - - beforeEach(() => { - const mocks = getBlockMocks(); - mocks.getParent.mockReturnValue(testSession); - testPds.children = [ - { label: "A", stats: { user: "someUser", m4date: Date.now() }, getParent: mocks.getParent } as unknown as ZoweDatasetNode, - { - label: "B", - stats: { user: "anotherUser", m4date: Date.parse("2022-01-01T12:00:00") }, - getParent: mocks.getParent, - } as unknown as ZoweDatasetNode, - { - label: "C", - stats: { user: "someUser", m4date: Date.parse("2022-03-15T16:30:00") }, - getParent: mocks.getParent, - } as unknown as ZoweDatasetNode, - ]; - testSession.children = [testPds]; + showInputBox: jest.spyOn(Gui, "showInputBox"), }); afterEach(() => { @@ -2742,180 +2740,161 @@ describe("Dataset Tree Unit Tests - Function sortPdsMembersDialog", () => { } }); - // for sorting, we shouldn't need to refresh since all nodes - // should be intact, just in a different order - it("does nothing if no children exist", async () => { - const mocks = getBlockMocks(); - // case 1: called on PDS node - mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); - testPds.children = []; - await testTree.sortPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - - // case 2: called on session node - mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); - testSession.children = []; - await testTree.sortPdsMembersDialog(testSession); - expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - }); - - it("sorts by name", async () => { - const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); - await testTree.sortPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).toHaveBeenCalled(); - expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["A", "B", "C"]); - }); - - it("sorts by last modified date", async () => { - const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValueOnce({ label: "$(calendar) Date Modified" }); - await testTree.sortPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).toHaveBeenCalled(); - expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "C", "A"]); - }); - - it("sorts by user ID", async () => { - const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValueOnce({ label: "$(account) User ID" }); - await testTree.sortPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).toHaveBeenCalled(); - expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "A", "C"]); - }); + describe("sortBy & sortPdsMembersDialog", () => { + // for sorting, we shouldn't need to refresh since all nodes + // should be intact, just in a different order + it("does nothing if no children exist", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + // case 1: called on PDS node + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); + nodes.pds.children = []; + await tree.sortPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + + // case 2: called on session node + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); + nodes.session.children = []; + await tree.sortPdsMembersDialog(nodes.session); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + }); - it("returns to sort selection dialog when sort direction selection is canceled", async () => { - const sortPdsMembersDialog = jest.spyOn(testTree, "sortPdsMembersDialog"); - const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValueOnce({ label: "$(fold) Sort Direction" }); - mocks.showQuickPick.mockResolvedValueOnce(undefined); - await testTree.sortPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(sortPdsMembersDialog).toHaveBeenCalledTimes(2); - }); -}); + it("sorts by name", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(case-sensitive) Name (default)" }); + await tree.sortPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(nodes.pds.children?.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["A", "B", "C"]); + }); -describe("Dataset Tree Unit Tests - Function filterPdsMembersDialog", () => { - const testTree = new DatasetTree(); - const getBlockMocks = (): Record => ({ - nodeDataChanged: jest.spyOn(DatasetTree.prototype, "nodeDataChanged"), - refreshElement: jest.spyOn(DatasetTree.prototype, "refreshElement"), - showQuickPick: jest.spyOn(Gui, "showQuickPick"), - getParent: jest.spyOn(ZoweDatasetNode.prototype, "getParent"), - showInputBox: jest.spyOn(Gui, "showInputBox"), - }); - const testSession = new ZoweDatasetNode("testSession", vscode.TreeItemCollapsibleState.Collapsed, null, createISession()); - const testPds = new ZoweDatasetNode("testPds", vscode.TreeItemCollapsibleState.Collapsed, testSession, createISession()); - testPds.contextValue = globals.DS_PDS_CONTEXT; + it("sorts by last modified date", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(calendar) Date Modified" }); + await tree.sortPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(nodes.pds.children?.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "C", "A"]); + }); - beforeEach(() => { - const mocks = getBlockMocks(); - mocks.getParent.mockReturnValue(testSession); - testPds.children = [ - { label: "A", stats: { user: "someUser", m4date: Date.now() }, getParent: mocks.getParent } as unknown as ZoweDatasetNode, - { - label: "B", - stats: { user: "anotherUser", m4date: Date.parse("2022-01-01T12:00:00") }, - getParent: mocks.getParent, - } as unknown as ZoweDatasetNode, - { - label: "C", - stats: { user: "someUser", m4date: Date.parse("2022-03-15T16:30:00") }, - getParent: mocks.getParent, - } as unknown as ZoweDatasetNode, - ]; - testSession.children = [testPds]; - testPds.filter = undefined as any; - }); + it("sorts by user ID", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(account) User ID" }); + await tree.sortPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(nodes.pds.children?.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "A", "C"]); + }); - afterEach(() => { - const mocks = getBlockMocks(); - for (const mock of Object.values(mocks)) { - mock.mockReset(); - } + it("returns to sort selection dialog when sort direction selection is canceled", async () => { + const sortPdsMembersDialog = jest.spyOn(tree, "sortPdsMembersDialog"); + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + mocks.showQuickPick.mockResolvedValueOnce({ label: "$(fold) Sort Direction" }); + mocks.showQuickPick.mockResolvedValueOnce(undefined); + await tree.sortPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(sortPdsMembersDialog).toHaveBeenCalledTimes(2); + }); }); - afterAll(() => { - const mocks = getBlockMocks(); - for (const mock of Object.values(mocks)) { - mock.mockRestore(); - } - }); + describe("filterBy & filterPdsMembersDialog", () => { + afterEach(() => { + const mocks = getBlockMocks(); + for (const mock of Object.values(mocks)) { + mock.mockReset(); + } + }); - it("calls refreshElement if PDS children were removed from a previous filter", async () => { - const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); - mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); + afterAll(() => { + const mocks = getBlockMocks(); + for (const mock of Object.values(mocks)) { + mock.mockRestore(); + } + }); - testPds.filter = { method: DatasetFilterOpts.UserId, value: "invalidUserId" }; - testPds.children = []; - await testTree.filterPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - expect(mocks.refreshElement).toHaveBeenCalledWith(testPds); - }); + it("calls refreshElement if PDS children were removed from a previous filter", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); + mocks.showInputBox.mockResolvedValueOnce("2022-01-01"); + + nodes.pds.filter = { method: DatasetFilterOpts.UserId, value: "invalidUserId" }; + nodes.pds.children = []; + await tree.filterPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + expect(mocks.refreshElement).toHaveBeenCalledWith(nodes.pds); + }); - it("returns to filter selection dialog when filter entry is canceled", async () => { - const filterPdsMembersSpy = jest.spyOn(testTree, "filterPdsMembersDialog"); - const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); - mocks.showInputBox.mockResolvedValueOnce(undefined); - await testTree.filterPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); - expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(filterPdsMembersSpy).toHaveBeenCalledTimes(2); - }); + it("returns to filter selection dialog when filter entry is canceled", async () => { + const filterPdsMembersSpy = jest.spyOn(tree, "filterPdsMembersDialog"); + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); + mocks.showInputBox.mockResolvedValueOnce(undefined); + await tree.filterPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).not.toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(filterPdsMembersSpy).toHaveBeenCalledTimes(2); + }); - it("filters single PDS by last modified date", async () => { - const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); - mocks.showInputBox.mockResolvedValueOnce("2022-03-15"); - await testTree.filterPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).toHaveBeenCalled(); - expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["C"]); - }); + it("filters single PDS by last modified date", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + mocks.showQuickPick.mockResolvedValueOnce("$(calendar) Date Modified" as any); + mocks.showInputBox.mockResolvedValueOnce("2022-03-15"); + await tree.filterPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(nodes.pds.children?.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["C"]); + }); - it("filters single PDS by user ID", async () => { - const mocks = getBlockMocks(); - mocks.showQuickPick.mockResolvedValueOnce("$(account) User ID" as any); - mocks.showInputBox.mockResolvedValueOnce("anotherUser"); - await testTree.filterPdsMembersDialog(testPds); - expect(mocks.nodeDataChanged).toHaveBeenCalled(); - expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(testPds.children.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B"]); - }); + it("filters single PDS by user ID", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + mocks.showQuickPick.mockResolvedValueOnce("$(account) User ID" as any); + mocks.showInputBox.mockResolvedValueOnce("anotherUser"); + await tree.filterPdsMembersDialog(nodes.pds); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(nodes.pds.children?.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B"]); + }); - it("filters PDS members using the session node filter", async () => { - const mocks = getBlockMocks(); - const uidString = "$(account) User ID" as any; - const anotherUser = "anotherUser"; - mocks.showQuickPick.mockResolvedValueOnce(uidString).mockResolvedValueOnce(uidString); - mocks.showInputBox.mockResolvedValueOnce(anotherUser).mockResolvedValueOnce(anotherUser); - - // case 1: old filter was set on session, just refresh PDS to use new filter - testSession.filter = { - method: DatasetFilterOpts.LastModified, - value: "2020-01-01", - }; - await testTree.filterPdsMembersDialog(testSession); - expect(mocks.refreshElement).toHaveBeenCalled(); + it("filters PDS members using the session node filter", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + const uidString = "$(account) User ID" as any; + const anotherUser = "anotherUser"; + mocks.showQuickPick.mockResolvedValueOnce(uidString).mockResolvedValueOnce(uidString); + mocks.showInputBox.mockResolvedValueOnce(anotherUser).mockResolvedValueOnce(anotherUser); + + // case 1: old filter was set on session, just refresh PDS to use new filter + nodes.session.filter = { + method: DatasetFilterOpts.LastModified, + value: "2020-01-01", + }; + await tree.filterPdsMembersDialog(nodes.session); + expect(mocks.refreshElement).toHaveBeenCalled(); - // case 2: no old filter present, PDS has children to be filtered - testSession.filter = undefined; - await testTree.filterPdsMembersDialog(testSession); - expect(mocks.nodeDataChanged).toHaveBeenCalled(); - }); + // case 2: no old filter present, PDS has children to be filtered + nodes.session.filter = undefined; + await tree.filterPdsMembersDialog(nodes.session); + expect(mocks.nodeDataChanged).toHaveBeenCalled(); + }); - it("clears filter for a PDS when selected in dialog", async () => { - const mocks = getBlockMocks(); - const resp = "$(clear-all) Clear filter for PDS" as any; - mocks.showQuickPick.mockResolvedValueOnce(resp); - const updateFilterForNode = jest.spyOn(DatasetTree.prototype, "updateFilterForNode"); - await testTree.filterPdsMembersDialog(testPds); - expect(mocks.refreshElement).not.toHaveBeenCalled(); - expect(updateFilterForNode).toHaveBeenCalledWith(testPds, null, false); + it("clears filter for a PDS when selected in dialog", async () => { + const mocks = getBlockMocks(); + const nodes = nodesForSuite(); + const resp = "$(clear-all) Clear filter for PDS" as any; + mocks.showQuickPick.mockResolvedValueOnce(resp); + const updateFilterForNode = jest.spyOn(DatasetTree.prototype, "updateFilterForNode"); + await tree.filterPdsMembersDialog(nodes.pds); + expect(mocks.refreshElement).not.toHaveBeenCalled(); + expect(updateFilterForNode).toHaveBeenCalledWith(nodes.pds, null, false); + }); }); }); diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index 9b83a8915d..fdd90426b4 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -262,11 +262,17 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } // get user and last modified date for sorting, if available - const { m4date, mtime, msec }: { m4date: string; mtime: string; msec: string } = item; - if (m4date) { + if ("m4date" in item) { + const { m4date, mtime, msec }: { m4date: string; mtime: string; msec: string } = item; temp.stats = { user: item.user, - m4date: new Date(`${m4date.replace(/\//g, "-")}T${mtime}:${msec}`), + m4date: dayjs(`${m4date} ${mtime}:${msec}`).toDate(), + }; + } else if ("id" in item || "changed" in item) { + // missing keys from API response; check for FTP keys + temp.stats = { + user: item.id, + m4date: item.changed ? dayjs(item.changed).toDate() : null, }; } elementChildren[temp.label.toString()] = temp; From f65cc2ccea3e324b15ad322cd380bb96f3e4f436 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Thu, 12 Oct 2023 13:54:56 -0400 Subject: [PATCH 35/47] fix(jobs/sort): Proper handling for jobs with null retcodes Signed-off-by: Trae Yelovich --- packages/zowe-explorer/src/job/ZoweJobNode.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/zowe-explorer/src/job/ZoweJobNode.ts b/packages/zowe-explorer/src/job/ZoweJobNode.ts index 3db60de46b..8c49527eee 100644 --- a/packages/zowe-explorer/src/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/job/ZoweJobNode.ts @@ -250,11 +250,22 @@ export class Job extends ZoweTreeNode implements IZoweJobTreeNode { const sortGreaterThan = sortLessThan * -1; const keyToSortBy = JOB_SORT_KEYS[sortOpts.method]; - if (keyToSortBy !== "jobid" && x["job"][keyToSortBy] == y["job"][keyToSortBy]) { - return x["job"]["jobid"] > y["job"]["jobid"] ? sortGreaterThan : sortLessThan; + let xCompare, yCompare; + if (keyToSortBy === "retcode") { + // some jobs (such as active ones) will have a null retcode + // in this case, use status as the key to compare for that node only + xCompare = x.job["retcode"] == null ? x.job["status"] : x.job["retcode"]; + yCompare = y.job["retcode"] == null ? y.job["status"] : y.job["retcode"]; } else { - return x["job"][keyToSortBy] > y["job"][keyToSortBy] ? sortGreaterThan : sortLessThan; + xCompare = x.job[keyToSortBy]; + yCompare = y.job[keyToSortBy]; } + + if (xCompare === yCompare) { + return x.job["jobid"] > y.job["jobid"] ? sortGreaterThan : sortLessThan; + } + + return xCompare > yCompare ? sortGreaterThan : sortLessThan; }; } From 74f68696043fcab13f1456f9fcc8bef00c334b55 Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Thu, 12 Oct 2023 15:41:45 -0400 Subject: [PATCH 36/47] fix(jobs/sort): use nullish coalescing for retcode/status check Signed-off-by: Trae Yelovich --- packages/zowe-explorer/src/job/ZoweJobNode.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/zowe-explorer/src/job/ZoweJobNode.ts b/packages/zowe-explorer/src/job/ZoweJobNode.ts index 8c49527eee..0ac960be2a 100644 --- a/packages/zowe-explorer/src/job/ZoweJobNode.ts +++ b/packages/zowe-explorer/src/job/ZoweJobNode.ts @@ -254,8 +254,8 @@ export class Job extends ZoweTreeNode implements IZoweJobTreeNode { if (keyToSortBy === "retcode") { // some jobs (such as active ones) will have a null retcode // in this case, use status as the key to compare for that node only - xCompare = x.job["retcode"] == null ? x.job["status"] : x.job["retcode"]; - yCompare = y.job["retcode"] == null ? y.job["status"] : y.job["retcode"]; + xCompare = x.job["retcode"] ?? x.job["status"]; + yCompare = y.job["retcode"] ?? y.job["status"]; } else { xCompare = x.job[keyToSortBy]; yCompare = y.job[keyToSortBy]; From 1d657e6a33d46e692287bc20f7522488cbf45e3d Mon Sep 17 00:00:00 2001 From: Trae Yelovich Date: Tue, 17 Oct 2023 10:47:51 -0400 Subject: [PATCH 37/47] feat(ds, jobs): Show status bar msg when sort/filter is applied - Renamed `m4date` to `modifiedDate` in `DatasetStats` API type Signed-off-by: Trae Yelovich --- packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts | 3 ++- .../__tests__/__unit__/dataset/DatasetTree.unit.test.ts | 6 +++--- .../i18n/sample/src/dataset/DatasetTree.i18n.json | 5 +++-- .../zowe-explorer/i18n/sample/src/job/actions.i18n.json | 5 +++-- packages/zowe-explorer/src/dataset/DatasetTree.ts | 3 +++ packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts | 8 ++++---- packages/zowe-explorer/src/job/actions.ts | 2 ++ 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts index e5f0d3c1fd..4f731b8c14 100644 --- a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts @@ -127,7 +127,8 @@ export interface IZoweTreeNode { export type DatasetStats = { user: string; - m4date: Date; + // built from "m4date", "mtime" and "msec" variables from z/OSMF API response + modifiedDate: Date; }; /** diff --git a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts index f8e6ddcc98..65cb06a4a7 100644 --- a/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/dataset/DatasetTree.unit.test.ts @@ -2705,11 +2705,11 @@ describe("Dataset Tree Unit Tests - Sorting and Filtering operations", () => { pds.contextValue = globals.DS_PDS_CONTEXT; const nodeA = new ZoweDatasetNode("A", vscode.TreeItemCollapsibleState.Collapsed, pds, createISession()); - nodeA.stats = { user: "someUser", m4date: new Date() }; + nodeA.stats = { user: "someUser", modifiedDate: new Date() }; const nodeB = new ZoweDatasetNode("B", vscode.TreeItemCollapsibleState.Collapsed, pds, createISession()); - nodeB.stats = { user: "anotherUser", m4date: new Date("2022-01-01T12:00:00") }; + nodeB.stats = { user: "anotherUser", modifiedDate: new Date("2022-01-01T12:00:00") }; const nodeC = new ZoweDatasetNode("C", vscode.TreeItemCollapsibleState.Collapsed, pds, createISession()); - nodeC.stats = { user: "someUser", m4date: new Date("2022-03-15T16:30:00") }; + nodeC.stats = { user: "someUser", modifiedDate: new Date("2022-03-15T16:30:00") }; pds.children = [nodeA, nodeB, nodeC]; session.children = [pds]; diff --git a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json index d59c6ecff8..a466d5c330 100644 --- a/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/dataset/DatasetTree.i18n.json @@ -30,14 +30,15 @@ "ds.singlePdsSort": "the PDS members in {0}", "ds.selectSortOpt": "Select a sorting option for {0}", "setSortDirection": "$(fold) Sort Direction", - "sort.asc": "Ascending", - "sort.desc": "Descending", "sort.selectDirection": "Select a sorting direction", + "sort.updated": "$(check) Sorting updated for {0}", "ds.clearProfileFilter": "$(clear-all) Clear filter for profile", "ds.clearPdsFilter": "$(clear-all) Clear filter for PDS", "ds.selectFilterOpt": "Set a filter for {0}", + "filter.cleared": "$(check) Filter cleared for {0}", "ds.filterEntry.invalidDate": "Invalid date format specified", "ds.filterEntry.title": "Enter a value to filter by", "ds.filterEntry.invalid": "Invalid filter specified", + "filter.updated": "$(check) Filter updated for {0}", "defaultFilterPrompt.option.prompt.search": "$(plus) Create a new filter. For example: HLQ.*, HLQ.aaa.bbb, HLQ.ccc.ddd(member)" } diff --git a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json index 51671482a8..eb36cd951f 100644 --- a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json @@ -23,6 +23,7 @@ "cancelJobs.failed": "One or more jobs failed to cancel: {0}", "cancelJobs.succeeded": "Cancelled selected jobs successfully.", "jobs.selectSortOpt": "Select a sorting option for jobs in {0}", - "filterJobs.message": "Use the search button to display jobs", - "filterJobs.prompt.message": "Enter local filter..." + "setSortDirection": "$(fold) Sort Direction", + "sort.selectDirection": "Select a sorting direction", + "sort.updated": "$(check) Sorting updated for {0}" } diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index 8520764a3b..c479f8141f 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -1375,6 +1375,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree, job: IZoweJobTreeNode): Promise { From 4492f420f28e723ebba8e839fd4c329512a1ff20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Oct 2023 17:35:32 +0000 Subject: [PATCH 38/47] chore(deps): Bump @babel/traverse from 7.17.10 to 7.23.2 Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.17.10 to 7.23.2. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 61 +++++++------------------------------------------------ 1 file changed, 7 insertions(+), 54 deletions(-) diff --git a/yarn.lock b/yarn.lock index 37ae9cb03e..d0f1c2792c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -139,7 +139,7 @@ "@jridgewell/gen-mapping" "^0.1.0" jsesc "^2.5.1" -"@babel/generator@^7.20.1", "@babel/generator@^7.20.2", "@babel/generator@^7.7.2": +"@babel/generator@^7.20.2", "@babel/generator@^7.7.2": version "7.20.4" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== @@ -278,14 +278,6 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.17.0" -"@babel/helper-function-name@^7.19.0": - version "7.19.0" - resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" - "@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" @@ -301,13 +293,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" @@ -582,7 +567,7 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== -"@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": +"@babel/parser@^7.18.10", "@babel/parser@^7.20.2": version "7.20.3" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== @@ -1287,42 +1272,10 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.10", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9": - version "7.17.10" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.10.tgz#1ee1a5ac39f4eac844e6cf855b35520e5eb6f8b5" - integrity sha512-VmbrTHQteIdUUQNTb+zE12SHS/xQVIShmBPhlNP12hD5poF2pbITW1Z4172d03HegaQWhLffdkRJYtAzp0AGcw== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.10" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.10" - "@babel/types" "^7.17.10" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.20.1", "@babel/traverse@^7.7.2": - version "7.20.1" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" - integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.1" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.1" - "@babel/types" "^7.20.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.23.0": - version "7.23.0" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz#18196ddfbcf4ccea324b7f6d3ada00d8c5a99c53" - integrity sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw== +"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.10", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9", "@babel/traverse@^7.20.1", "@babel/traverse@^7.23.0", "@babel/traverse@^7.7.2": + version "7.23.2" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== dependencies: "@babel/code-frame" "^7.22.13" "@babel/generator" "^7.23.0" @@ -1343,7 +1296,7 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2": +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.20.0", "@babel/types@^7.20.2": version "7.20.2" resolved "https://registry.npmjs.org/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== From e6f988b29b8f1c6b0b363303c983cdbd28a641f4 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Sat, 23 Sep 2023 00:25:50 +0530 Subject: [PATCH 39/47] updated with local filtering of jobs Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 1 + .../zowe-explorer/__tests__/__unit__/extension.unit.test.ts | 1 + .../zowe-explorer/__tests__/__unit__/job/init.unit.test.ts | 1 - .../zowe-explorer/i18n/sample/src/job/actions.i18n.json | 4 +++- packages/zowe-explorer/src/globals.ts | 2 +- packages/zowe-explorer/src/job/init.ts | 6 ++++++ 6 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index ae5535716e..88a92b1ea1 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -11,6 +11,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added new API {ZE Extender MetaData} to allow extenders to have the metadata of registered extenders to aid in team configuration file creation from a view that isn't Zowe Explorer's. [#2394](https://github.com/zowe/vscode-extension-for-zowe/issues/2394) - Added "Sort PDS members" feature in Data Sets tree view: accessible via sort icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) - Added "Filter PDS members" feature in Data Sets tree view: accessible via filter icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) +- Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) ### Bug fixes diff --git a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts index 7e5df21fd1..4f17e2531a 100644 --- a/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/extension.unit.test.ts @@ -241,6 +241,7 @@ async function createGlobalMocks() { "zowe.jobs.stopPolling", "zowe.jobs.cancelJob", "zowe.jobs.sortBy", + "zowe.jobs.filterJobs", "zowe.manualPoll", "zowe.updateSecureCredentials", "zowe.promptCredentials", diff --git a/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts index 3ba9e119db..56821ae57e 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/init.unit.test.ts @@ -55,7 +55,6 @@ describe("Test src/jobs/extension", () => { pollData: jest.fn(), refreshElement: jest.fn(), filterJobs: jest.fn(), - filterSpools: jest.fn(), }; const commands: IJestIt[] = [ { diff --git a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json index eb36cd951f..0cf7ce4d92 100644 --- a/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/job/actions.i18n.json @@ -25,5 +25,7 @@ "jobs.selectSortOpt": "Select a sorting option for jobs in {0}", "setSortDirection": "$(fold) Sort Direction", "sort.selectDirection": "Select a sorting direction", - "sort.updated": "$(check) Sorting updated for {0}" + "sort.updated": "$(check) Sorting updated for {0}", + "filterJobs.message": "Use the search button to display jobs", + "filterJobs.prompt.message": "Enter local filter..." } diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 6686c4c069..3624a6a84e 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 113; +export const COMMAND_COUNT = 115; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000; diff --git a/packages/zowe-explorer/src/job/init.ts b/packages/zowe-explorer/src/job/init.ts index 81527474a2..991dea18ca 100644 --- a/packages/zowe-explorer/src/job/init.ts +++ b/packages/zowe-explorer/src/job/init.ts @@ -172,6 +172,12 @@ export async function initJobsProvider(context: vscode.ExtensionContext): Promis }) ); context.subscriptions.push(vscode.commands.registerCommand("zowe.jobs.sortBy", async (job) => jobActions.sortJobs(job, jobsProvider))); + context.subscriptions.push( + vscode.commands.registerCommand("zowe.jobs.filterJobs", async (job) => { + await jobActions.filterJobs(jobsProvider, job); + }) + ); + initSubscribers(context, jobsProvider); return jobsProvider; } From 65c8cdcbe262fd26acf28e75e17d66a3470a8969 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Sat, 23 Sep 2023 08:45:12 +0530 Subject: [PATCH 40/47] Updated sample data in actions.unit.test.ts file Signed-off-by: SanthoshiBoyina --- .../__tests__/__unit__/job/actions.unit.test.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 78c4e2a6d8..7cb8cd50d1 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -1482,6 +1482,7 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { null, null, setJobObjects(createIJobObject(), "ZOWEUSR1", "JOB04945", "CC 0000"), + setJobObjects(createIJobObject(), "ZOWEUSR1", "JOB04945", "CC 0000"), null ); const node2 = new Job( @@ -1492,14 +1493,6 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { setJobObjects(createIJobObject(), "ZOWEUSR2", "JOB05037", "CC 0000"), null ); - const node3 = new Job( - "jobnew", - vscode.TreeItemCollapsibleState.None, - null, - null, - setJobObjects(createIJobObject(), "ZOWEUSR3", "TSU07707", "ABEND S222"), - null - ); it("To show showInformationMessage", async () => { const testTree = new ZosJobsProvider(); @@ -1518,7 +1511,7 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { node1.children = [node2, node3]; const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); - mockInputBox.value = "ZOWEUSR2(JOB05037) - CC 0000"; + mockInputBox.value = "ZOWEUSR1(JOB04945) - CC 0000"; createInputBoxSpy.mockReturnValue(mockInputBox); const filterJobsSpy = jest.spyOn(jobActions, "filterJobs"); await jobActions.filterJobs(testTree, node1); From 25839de9edb6588fa2ac88591270202a30e0fae0 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Thu, 28 Sep 2023 17:27:39 +0530 Subject: [PATCH 41/47] resolved conflicts Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 88a92b1ea1..24bb128c79 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added "Sort PDS members" feature in Data Sets tree view: accessible via sort icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) - Added "Filter PDS members" feature in Data Sets tree view: accessible via filter icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) - Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) +- Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) ### Bug fixes From 352f4bafd7a512996bf1fcfee404bf4e5cc714ee Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Sat, 23 Sep 2023 08:18:34 +0530 Subject: [PATCH 42/47] updated changelog file Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 24bb128c79..49c8040ea0 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added "Filter PDS members" feature in Data Sets tree view: accessible via filter icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) - Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) - Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) +- Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) ### Bug fixes From e30a00b79153ee80af654068e55a07f506e8e43f Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Fri, 29 Sep 2023 13:59:33 +0530 Subject: [PATCH 43/47] updated use of tree provider Signed-off-by: SanthoshiBoyina --- .../zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts | 2 ++ packages/zowe-explorer/src/job/actions.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 7cb8cd50d1..134326b592 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -95,6 +95,7 @@ function createGlobalMocks() { }); Object.defineProperty(Gui, "showMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(Gui, "infoMessage", { value: jest.fn(), configurable: true }); + Object.defineProperty(Gui, "infoMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(Gui, "warningMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(Gui, "errorMessage", { value: jest.fn(), configurable: true }); Object.defineProperty(Gui, "showOpenDialog", { value: jest.fn(), configurable: true }); @@ -1502,6 +1503,7 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { await jobActions.filterJobs(testTree, node1); expect(mocked(Gui.infoMessage)).toHaveBeenCalled(); + expect(mocked(Gui.infoMessage)).toHaveBeenCalled(); }); it("To filter jobs based on a combination of JobName, JobId and Return code", async () => { diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 305f3c247a..5b0624f88b 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -571,6 +571,7 @@ export async function filterJobs(jobsProvider: IZoweTree, job: const acutal_jobs = job["children"]; const inputBox = await vscode.window.createInputBox(); inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); + inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); inputBox.onDidChangeValue((query) => { query = query.toUpperCase(); job["children"] = acutal_jobs.filter((item) => `${item["job"].jobname}(${item["job"].jobid}) - ${item["job"].retcode}`.includes(query)); From 91ce7bb5ce20634ecdb12b24f2ccf81a7ce8c817 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Fri, 29 Sep 2023 15:20:50 +0530 Subject: [PATCH 44/47] resolved latest conflicts Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md index 49c8040ea0..88a92b1ea1 100644 --- a/packages/zowe-explorer/CHANGELOG.md +++ b/packages/zowe-explorer/CHANGELOG.md @@ -12,8 +12,6 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen - Added "Sort PDS members" feature in Data Sets tree view: accessible via sort icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) - Added "Filter PDS members" feature in Data Sets tree view: accessible via filter icon on session node, or by right-clicking a PDS or session. [#2420](https://github.com/zowe/vscode-extension-for-zowe/issues/2420) - Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) -- Added new edit feature on `Edit Attributes` view for changing file tags on USS [#2113](https://github.com/zowe/vscode-extension-for-zowe/issues/2113) -- Added "Local Filtering of Jobs Tree" feature for job nodes in Jobs tree view. [#2476](https://github.com/zowe/vscode-extension-for-zowe/issues/2476) ### Bug fixes From a4e1e05c552566fddd179bc03b7c332667eca7ff Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Tue, 10 Oct 2023 21:22:18 +0530 Subject: [PATCH 45/47] updated with filter jobs Signed-off-by: SanthoshiBoyina --- .../__tests__/__unit__/job/actions.unit.test.ts | 10 +++++++++- packages/zowe-explorer/src/globals.ts | 2 +- packages/zowe-explorer/src/job/actions.ts | 5 +++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 134326b592..7d1a1c2940 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -1494,6 +1494,14 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { setJobObjects(createIJobObject(), "ZOWEUSR2", "JOB05037", "CC 0000"), null ); + const node3 = new Job( + "jobnew", + vscode.TreeItemCollapsibleState.None, + null, + null, + setJobObjects(createIJobObject(), "ZOWEUSR3", "TSU07707", "ABEND S222"), + null + ); it("To show showInformationMessage", async () => { const testTree = new ZosJobsProvider(); @@ -1513,7 +1521,7 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { node1.children = [node2, node3]; const createInputBoxSpy = jest.spyOn(vscode.window, "createInputBox"); - mockInputBox.value = "ZOWEUSR1(JOB04945) - CC 0000"; + mockInputBox.value = "ZOWEUSR2(JOB05037) - CC 0000"; createInputBoxSpy.mockReturnValue(mockInputBox); const filterJobsSpy = jest.spyOn(jobActions, "filterJobs"); await jobActions.filterJobs(testTree, node1); diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index 3624a6a84e..b8d95dfc77 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 115; +export const COMMAND_COUNT = 114; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000; diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 5b0624f88b..7648d96ff9 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -569,17 +569,22 @@ export async function filterJobs(jobsProvider: IZoweTree, job: return; } const acutal_jobs = job["children"]; + const acutal_jobs = job["children"]; const inputBox = await vscode.window.createInputBox(); inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); inputBox.onDidChangeValue((query) => { query = query.toUpperCase(); job["children"] = acutal_jobs.filter((item) => `${item["job"].jobname}(${item["job"].jobid}) - ${item["job"].retcode}`.includes(query)); + job["children"] = acutal_jobs.filter((item) => `${item["job"].jobname}(${item["job"].jobid}) - ${item["job"].retcode}`.includes(query)); jobsProvider.refresh(); }); inputBox.onDidAccept(() => { inputBox.hide(); }); + inputBox.onDidAccept(() => { + inputBox.hide(); + }); inputBox.show(); return inputBox; } From f8559cc7e03ec9c2c919df21bca299940307422c Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Wed, 18 Oct 2023 18:34:32 +0530 Subject: [PATCH 46/47] updated with changes Signed-off-by: SanthoshiBoyina --- .../__tests__/__unit__/job/actions.unit.test.ts | 1 - packages/zowe-explorer/src/job/actions.ts | 6 ------ 2 files changed, 7 deletions(-) diff --git a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts index 7d1a1c2940..8394ba051a 100644 --- a/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts +++ b/packages/zowe-explorer/__tests__/__unit__/job/actions.unit.test.ts @@ -1483,7 +1483,6 @@ describe("Job Actions Unit Tests - Filter Jobs", () => { null, null, setJobObjects(createIJobObject(), "ZOWEUSR1", "JOB04945", "CC 0000"), - setJobObjects(createIJobObject(), "ZOWEUSR1", "JOB04945", "CC 0000"), null ); const node2 = new Job( diff --git a/packages/zowe-explorer/src/job/actions.ts b/packages/zowe-explorer/src/job/actions.ts index 7648d96ff9..305f3c247a 100644 --- a/packages/zowe-explorer/src/job/actions.ts +++ b/packages/zowe-explorer/src/job/actions.ts @@ -569,22 +569,16 @@ export async function filterJobs(jobsProvider: IZoweTree, job: return; } const acutal_jobs = job["children"]; - const acutal_jobs = job["children"]; const inputBox = await vscode.window.createInputBox(); inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); - inputBox.placeholder = localize("filterJobs.prompt.message", "Enter local filter..."); inputBox.onDidChangeValue((query) => { query = query.toUpperCase(); job["children"] = acutal_jobs.filter((item) => `${item["job"].jobname}(${item["job"].jobid}) - ${item["job"].retcode}`.includes(query)); - job["children"] = acutal_jobs.filter((item) => `${item["job"].jobname}(${item["job"].jobid}) - ${item["job"].retcode}`.includes(query)); jobsProvider.refresh(); }); inputBox.onDidAccept(() => { inputBox.hide(); }); - inputBox.onDidAccept(() => { - inputBox.hide(); - }); inputBox.show(); return inputBox; } From 14ebbc88d6ef14416df7da3b3bb73ed0ea4c5fe0 Mon Sep 17 00:00:00 2001 From: SanthoshiBoyina Date: Thu, 19 Oct 2023 20:15:18 +0530 Subject: [PATCH 47/47] Resolved conflicts Signed-off-by: SanthoshiBoyina --- packages/zowe-explorer/src/globals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zowe-explorer/src/globals.ts b/packages/zowe-explorer/src/globals.ts index b8d95dfc77..3624a6a84e 100644 --- a/packages/zowe-explorer/src/globals.ts +++ b/packages/zowe-explorer/src/globals.ts @@ -35,7 +35,7 @@ export let DS_DIR: string; export let CONFIG_PATH; // set during activate export let ISTHEIA = false; // set during activate export let LOG: imperative.Logger; -export const COMMAND_COUNT = 114; +export const COMMAND_COUNT = 115; export const MAX_SEARCH_HISTORY = 5; export const MAX_FILE_HISTORY = 10; export const MS_PER_SEC = 1000;