Skip to content

Commit

Permalink
Merge branch 'main' into 2.12.0-release
Browse files Browse the repository at this point in the history
  • Loading branch information
JillieBeanSim authored Nov 1, 2023
2 parents 539cbf2 + 0592bd1 commit e5cd32f
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 36 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
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,7 @@ describe("ProfilesUtils unit tests", () => {

it("should skip creating directories and files that already exist", async () => {
const blockMocks = createBlockMocks();
jest.spyOn(profUtils.ProfilesUtils, "getCredentialManagerOverride").mockReturnValue("@zowe/cli");
blockMocks.mockGetDirectValue.mockReturnValue("@zowe/cli");
blockMocks.mockExistsSync.mockReturnValue(true);
const fileJson = blockMocks.mockFileRead;
Expand Down Expand Up @@ -777,6 +778,23 @@ describe("ProfilesUtils unit tests", () => {
expect(profUtils.ProfilesUtils.getCredentialManagerOverride()).toBe("My Custom Credential Manager");
expect(zoweLoggerTraceSpy).toBeCalledTimes(1);
});

it("should return default manager if the override file does not exist", () => {
const zoweLoggerTraceSpy = jest.spyOn(ZoweLogger, "trace");
const zoweLoggerInfoSpy = jest.spyOn(ZoweLogger, "info");

jest.spyOn(fs, "readFileSync").mockImplementation(() => {
throw new Error("test");
});
try {
profUtils.ProfilesUtils.getCredentialManagerOverride();
} catch (err) {
expect(err).toBe("test");
}

expect(zoweLoggerTraceSpy).toBeCalledTimes(1);
expect(zoweLoggerInfoSpy).toBeCalledTimes(1);
});
});

describe("setupCustomCredentialManager", () => {
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
26 changes: 17 additions & 9 deletions packages/zowe-explorer/src/utils/ProfilesUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,19 @@ export class ProfilesUtils {
*/
public static getCredentialManagerOverride(): string {
ZoweLogger.trace("ProfilesUtils.getCredentialManagerOverride called.");
const settingsFile = path.join(getZoweDir(), "settings", "imperative.json");
const imperativeConfig = JSON.parse(fs.readFileSync(settingsFile).toString());
const credentialManagerOverride = imperativeConfig?.overrides[imperative.CredentialManagerOverride.CRED_MGR_SETTING_NAME];
if (typeof credentialManagerOverride === "string") {
return credentialManagerOverride;
try {
const settingsFilePath = path.join(getZoweDir(), "settings", "imperative.json");
const settingsFile = fs.readFileSync(settingsFilePath);
const imperativeConfig = JSON.parse(settingsFile.toString());
const credentialManagerOverride = imperativeConfig?.overrides[imperative.CredentialManagerOverride.CRED_MGR_SETTING_NAME];
if (typeof credentialManagerOverride === "string") {
return credentialManagerOverride;
}
return imperative.CredentialManagerOverride.DEFAULT_CRED_MGR_NAME;
} catch (err) {
ZoweLogger.info("imperative.json does not exist, returning the default override of @zowe/cli");
return imperative.CredentialManagerOverride.DEFAULT_CRED_MGR_NAME;
}
return imperative.CredentialManagerOverride.DEFAULT_CRED_MGR_NAME;
}

/**
Expand Down Expand Up @@ -542,6 +548,10 @@ export class ProfilesUtils {

public static async initializeZoweFolder(): Promise<void> {
ZoweLogger.trace("ProfilesUtils.initializeZoweFolder called.");
// ensure the Secure Credentials Enabled value is read
// set globals.PROFILE_SECURITY value accordingly
const credentialManagerMap = ProfilesUtils.getCredentialManagerOverride();
await globals.setGlobalSecurityValue(credentialManagerMap ?? globals.ZOWE_CLI_SCM);
// Ensure that ~/.zowe folder exists
// Ensure that the ~/.zowe/settings/imperative.json exists
// TODO: update code below once this imperative issue is resolved.
Expand All @@ -555,9 +565,6 @@ export class ProfilesUtils {
fs.mkdirSync(settingsPath);
}
ProfilesUtils.writeOverridesFile();
// set global variable of security value to existing override
// this will later get reverted to default in getProfilesInfo.ts if user chooses to
await ProfilesUtils.updateCredentialManagerSetting(ProfilesUtils.getCredentialManagerOverride());
// If not using team config, ensure that the ~/.zowe/profiles directory
// exists with appropriate types within
if (!imperative.ImperativeConfig.instance.config?.exists) {
Expand Down Expand Up @@ -609,6 +616,7 @@ export class ProfilesUtils {
} else {
settings = { ...defaultImperativeJson };
}
settings.overrides.CredentialManager = globals.PROFILE_SECURITY;
const newData = JSON.stringify(settings, null, 2);
ZoweLogger.debug(
localize("writeOverridesFile.updateFile", "Updating imperative.json Credential Manager to {0}.\n{1}", globals.PROFILE_SECURITY, newData)
Expand Down

0 comments on commit e5cd32f

Please sign in to comment.