Skip to content

Commit

Permalink
Merge pull request #2538 from zowe/fix/sort/equal-values
Browse files Browse the repository at this point in the history
fix(ds/sort): Fallback to sorting by name when values are equal
  • Loading branch information
JillieBeanSim authored Nov 1, 2023
2 parents 7ecb12e + 1c0ca39 commit 0592bd1
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,15 @@ import * as fs from "fs";
import * as zowe from "@zowe/cli";
import { DatasetTree } from "../../../src/dataset/DatasetTree";
import { ZoweDatasetNode } from "../../../src/dataset/ZoweDatasetNode";
import { DatasetFilterOpts, Gui, IZoweDatasetTreeNode, ProfilesCache, ValidProfileEnum } from "@zowe/zowe-explorer-api";
import {
DatasetFilterOpts,
DatasetSortOpts,
Gui,
IZoweDatasetTreeNode,
ProfilesCache,
SortDirection,
ValidProfileEnum,
} from "@zowe/zowe-explorer-api";
import { ZoweExplorerApiRegister } from "../../../src/ZoweExplorerApiRegister";
import { Profiles } from "../../../src/Profiles";
import * as utils from "../../../src/utils/ProfilesUtils";
Expand Down Expand Up @@ -1761,6 +1769,43 @@ describe("Dataset Tree Unit Tests - Function datasetFilterPrompt", () => {

expect(await testTree.datasetFilterPrompt(testTree.mSessionNodes[1])).not.toBeDefined();
});

it("updates stats with modified date and user ID if provided in API", async () => {
const globalMocks = createGlobalMocks();
const blockMocks = await createBlockMocks(globalMocks);

const testTree = new DatasetTree();
testTree.mSessionNodes.push(blockMocks.datasetSessionNode);
const newNode = new ZoweDatasetNode("TEST.PDS", vscode.TreeItemCollapsibleState.Collapsed, testTree.mSessionNodes[1], blockMocks.session);
testTree.mSessionNodes[1].children = [newNode];
const updateStatsSpy = jest.spyOn(ZoweDatasetNode.prototype, "updateStats");
const getDatasetsSpy = jest.spyOn((ZoweDatasetNode as any).prototype, "getDatasets");
getDatasetsSpy.mockResolvedValueOnce([
{
success: true,
commandResponse: null,
apiResponse: {
items: [
{
m4date: "2023-10-31",
mtime: "12:00",
msec: "30",
member: "HI",
user: "SOMEUSR",
},
{
changed: "2023-10-31 03:00:00",
member: "BYE",
id: "SOMEUSR",
},
],
},
},
]);
await testTree.mSessionNodes[1].children[0].getChildren();

expect(updateStatsSpy).toHaveBeenCalled();
});
});
describe("Dataset Tree Unit Tests - Function editSession", () => {
async function createBlockMocks() {
Expand Down Expand Up @@ -2716,6 +2761,10 @@ describe("Dataset Tree Unit Tests - Sorting and Filtering operations", () => {
const nodeC = new ZoweDatasetNode("C", vscode.TreeItemCollapsibleState.Collapsed, pds, createISession());
nodeC.stats = { user: "someUser", modifiedDate: new Date("2022-03-15T16:30:00") };
pds.children = [nodeA, nodeB, nodeC];
pds.sort = {
method: DatasetSortOpts.Name,
direction: SortDirection.Ascending,
};
session.children = [pds];

return {
Expand Down Expand Up @@ -2785,6 +2834,22 @@ describe("Dataset Tree Unit Tests - Sorting and Filtering operations", () => {
expect(nodes.pds.children?.map((c: IZoweDatasetTreeNode) => c.label)).toStrictEqual(["B", "C", "A"]);
});

it("sorts by last modified date: handling 2 nodes with same date", async () => {
const mocks = getBlockMocks();
const nodes = nodesForSuite();
mocks.showQuickPick.mockResolvedValueOnce({ label: "$(fold) Sort Direction" });
mocks.showQuickPick.mockResolvedValueOnce({ label: "Descending" });
mocks.showQuickPick.mockResolvedValueOnce({ label: "$(calendar) Date Modified" });
// insert node with same date modified
const nodeD = new ZoweDatasetNode("D", vscode.TreeItemCollapsibleState.Collapsed, nodes.pds, createISession());
nodeD.stats = { user: "someUser", modifiedDate: new Date("2022-03-15T16:30:00") };
nodes.pds.children = [...(nodes.pds.children ?? []), nodeD];
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", "D", "C", "B"]);
});

it("sorts by user ID", async () => {
const mocks = getBlockMocks();
const nodes = nodesForSuite();
Expand Down
77 changes: 51 additions & 26 deletions packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,22 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod
return this.getProfile() ? this.getProfile().name : undefined;
}

public updateStats(item: any): void {
if ("m4date" in item) {
const { m4date, mtime, msec }: { m4date: string; mtime: string; msec: string } = item;
this.stats = {
user: item.user,
modifiedDate: dayjs(`${m4date} ${mtime}:${msec}`).toDate(),
};
} else if ("id" in item || "changed" in item) {
// missing keys from API response; check for FTP keys
this.stats = {
user: item.id,
modifiedDate: item.changed ? dayjs(item.changed).toDate() : undefined,
};
}
}

/**
* Retrieves child nodes of this ZoweDatasetNode
*
Expand Down Expand Up @@ -167,8 +183,10 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod

// Loops through all the returned dataset members and creates nodes for them
for (const item of response.apiResponse.items ?? response.apiResponse) {
const existing = this.children.find((element) => element.label.toString() === item.dsname);
const dsEntry = item.dsname ?? item.member;
const existing = this.children.find((element) => element.label.toString() === dsEntry);
if (existing) {
existing.updateStats(item);
elementChildren[existing.label.toString()] = existing;
// Creates a ZoweDatasetNode for a PDS
} else if (item.dsorg === "PO" || item.dsorg === "PO-E") {
Expand Down Expand Up @@ -262,19 +280,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod
}

// get user and last modified date for sorting, if available
if ("m4date" in item) {
const { m4date, mtime, msec }: { m4date: string; mtime: string; msec: string } = item;
temp.stats = {
user: item.user,
modifiedDate: 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,
modifiedDate: item.changed ? dayjs(item.changed).toDate() : undefined,
};
}
temp.updateStats(item);
elementChildren[temp.label.toString()] = temp;
}
}
Expand Down Expand Up @@ -332,22 +338,41 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod
const sortLessThan = sort.direction == SortDirection.Ascending ? -1 : 1;
const sortGreaterThan = sortLessThan * -1;

const sortByName = (nodeA: IZoweDatasetTreeNode, nodeB: IZoweDatasetTreeNode): number =>
(nodeA.label as string) < (nodeB.label as string) ? sortLessThan : sortGreaterThan;

if (!a.stats && !b.stats) {
return (a.label as string) < (b.label as string) ? sortLessThan : sortGreaterThan;
return sortByName(a, b);
}

switch (sort.method) {
case DatasetSortOpts.LastModified:
a.description = dayjs(a.stats?.modifiedDate).format("YYYY/MM/DD HH:mm:ss");
b.description = dayjs(b.stats?.modifiedDate).format("YYYY/MM/DD HH:mm:ss");
return a.stats?.modifiedDate < b.stats?.modifiedDate ? sortLessThan : sortGreaterThan;
case DatasetSortOpts.UserId:
a.description = a.stats?.user;
b.description = b.stats?.user;
return a.stats?.user < b.stats?.user ? sortLessThan : sortGreaterThan;
case DatasetSortOpts.Name:
return (a.label as string) < (b.label as string) ? sortLessThan : sortGreaterThan;
if (sort.method === DatasetSortOpts.LastModified) {
const dateA = dayjs(a.stats?.modifiedDate);
const dateB = dayjs(b.stats?.modifiedDate);

a.description = dateA.isValid() ? dateA.format("YYYY/MM/DD HH:mm:ss") : undefined;
b.description = dateB.isValid() ? dateB.format("YYYY/MM/DD HH:mm:ss") : undefined;

// for dates that are equal down to the second, fallback to sorting by name
if (dateA.isSame(dateB, "second")) {
return sortByName(a, b);
}

return dateA.isBefore(dateB, "second") ? sortLessThan : sortGreaterThan;
} else if (sort.method === DatasetSortOpts.UserId) {
const userA = a.stats?.user ?? "";
const userB = b.stats?.user ?? "";

a.description = userA;
b.description = userB;

if (userA === userB) {
return sortByName(a, b);
}

return userA < userB ? sortLessThan : sortGreaterThan;
}

return sortByName(a, b);
};
}

Expand Down

0 comments on commit 0592bd1

Please sign in to comment.