Skip to content
Open
14 changes: 14 additions & 0 deletions packages/vsce/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@
"title": "Show Region Logs",
"category": "Zowe Explorer for IBM CICS TS"
},
{
"command": "cics-extension-for-zowe.showBundleDirectory",
"title": "Show Bundle Directory",
"category": "Zowe Explorer for IBM CICS TS"
},
{
"command": "cics-extension-for-zowe.showRegionParameters",
"title": "Show SIT Parameters",
Expand Down Expand Up @@ -333,6 +338,10 @@
"command": "cics-extension-for-zowe.showRegionLogs",
"when": "never"
},
{
"command": "cics-extension-for-zowe.showBundleDirectory",
"when": "config.zowe.cics.showAllCommandsInPalette"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we use this feature toggle anymore...

Suggested change
"when": "config.zowe.cics.showAllCommandsInPalette"
"when": "never"

},
{
"command": "cics-extension-for-zowe.clearFilter",
"when": "never"
Expand Down Expand Up @@ -506,6 +515,11 @@
"command": "cics-extension-for-zowe.showRegionLogs",
"group": "1.CICSResourceNode.inquire"
},
{
"when": "view == cics-view && (viewItem =~ /^CICSResourceNode.CICSBundle.(ENABLED|DISABLED).*/)",
"command": "cics-extension-for-zowe.showBundleDirectory",
"group": "1.CICSResourceNode.inquire"
},
{
"when": "view == cics-view && viewItem =~ /CICSRegion.*.active/ && !listMultiSelection",
"command": "cics-extension-for-zowe.showRegionParameters",
Expand Down
2 changes: 2 additions & 0 deletions packages/vsce/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { viewMoreCommand } from "./viewMoreCommand";
import { setCICSRegionCommand } from "./setCICSRegionCommand";
import { getInspectResourceCommand } from "./inspectResourceCommand";
import { getCopyNameCommand, getCopyUserAgentHeaderCommand } from "./copyCommand";
import { showBundleDirectory } from "./showBundleDirectoryCommand";

export const getCommands = (treeDataProv: CICSTree, treeview: TreeView<any>, context: ExtensionContext) => {
return [
Expand Down Expand Up @@ -94,5 +95,6 @@ export const getCommands = (treeDataProv: CICSTree, treeview: TreeView<any>, con

getCopyNameCommand(),
getCopyUserAgentHeaderCommand(),
showBundleDirectory(treeview),
];
};
89 changes: 89 additions & 0 deletions packages/vsce/src/commands/showBundleDirectoryCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* 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.
*
*/

import { commands, TreeView, window } from "vscode";
import { CICSLogger } from "../utils/CICSLogger";
import { ProfileManagement } from "../utils/profileManagement";
import { IProfileLoaded } from "@zowe/imperative";
import * as vscode from "vscode";
import type { IZoweUSSTreeNode }
from "@zowe/zowe-explorer-api";
import { doesProfileSupportConnectionType, findRelatedZosProfiles, promptUserForProfile } from "../utils/commandUtils";

/**
* Creates a minimal USS tree node compatible with IZoweUSSTreeNode interface
*
* @param path - The full path of the USS directory
* @param profileName - The name of the profile to use
* @param profile - The profile object
* @returns A minimal implementation of IZoweUSSTreeNode
*/
function createUSSTreeNode(path: string, profileName: string, profile: IProfileLoaded): IZoweUSSTreeNode {
const directoryName = path.split("/").pop() || path;
return {
label: directoryName,
fullPath: path,
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
contextValue: "directory",
getLabel: () => directoryName,
getChildren: async () => [] as IZoweUSSTreeNode[],

Check failure on line 37 in packages/vsce/src/commands/showBundleDirectoryCommand.ts

View workflow job for this annotation

GitHub Actions / lint

Async method 'getChildren' has no 'await' expression
getProfileName: () => profileName,
getProfile: () => profile,

} as any as IZoweUSSTreeNode;
}


export function showBundleDirectory(treeview: TreeView<any>) {
return commands.registerCommand("cics-extension-for-zowe.showBundleDirectory", async (selectedBundle) => {
if (!selectedBundle) {
window.showErrorMessage(`No Bundle is selected from cics tree`);
return;
}
const bundleDir = selectedBundle.getContainedResource().resource.attributes?.bundledir;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contained Resource is optional, but attributes is not

Suggested change
const bundleDir = selectedBundle.getContainedResource().resource.attributes?.bundledir;
const bundleDir = selectedBundle.getContainedResource()?.resource.attributes.bundledir;

if (!bundleDir) {
window.showErrorMessage(`Could not find bundle directory for ${selectedBundle.getLabel()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
window.showErrorMessage(`Could not find bundle directory for ${selectedBundle.getLabel()
window.showErrorMessage(`Could not find bundle directory for ${selectedBundle.getContainedResourceName()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The label could have other info in that's not necessary here, status info for example

}.`);
return;
}
const allProfiles = await ProfileManagement.getProfilesCache().fetchAllProfiles();
const zosProfiles = allProfiles.filter((element) => doesProfileSupportConnectionType(element, "uss"));
let chosenProfileName: string;

const matchingZosProfile = await findRelatedZosProfiles(selectedBundle.profile, zosProfiles);

if (matchingZosProfile) {
chosenProfileName = matchingZosProfile.name;
} else { // we couldn't find a matching profile - prompt the user with all zos profiles
chosenProfileName = await promptUserForProfile(zosProfiles);
CICSLogger.debug(`User picked z/OS profile: ${chosenProfileName}`);
if (chosenProfileName === null) {
window.showErrorMessage("Could not find any profiles that will access USS (for instance z/OSMF).");
return;
} else if (chosenProfileName === undefined) { // the user cancelled the quick pick
return;
}
}
try { // Get the profile object from the name
const chosenProfile = zosProfiles.find(profile => profile.name === chosenProfileName);
if (!chosenProfile) {
window.showErrorMessage(`Could not find profile ${chosenProfileName}`);
return;
}
const ussNode = createUSSTreeNode(bundleDir, chosenProfileName, chosenProfile);
await commands.executeCommand("zowe.uss.filterBy", ussNode);

} catch (error) {
CICSLogger.error(`Failed to show bundle directory in USS view: ${error}`);
window.showErrorMessage(`Unable to open bundle directory in USS view.`);
}
});
}
95 changes: 2 additions & 93 deletions packages/vsce/src/commands/showLogsCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,94 +10,13 @@
*/

import { CicsCmciConstants } from "@zowe/cics-for-zowe-sdk";
import { IProfileLoaded } from "@zowe/imperative";
import { Gui, ZoweVsCodeExtension } from "@zowe/zowe-explorer-api";
import * as vscode from "vscode";
import { TreeView, commands, window } from "vscode";
import { CICSRegionTree } from "../trees/CICSRegionTree";
import { CICSLogger } from "../utils/CICSLogger";
import { toArray } from "../utils/commandUtils";
import { doesProfileSupportConnectionType, findRelatedZosProfiles, promptUserForProfile, toArray } from "../utils/commandUtils";
import { ProfileManagement } from "../utils/profileManagement";
import { runGetResource } from "../utils/resourceUtils";

// fetching a base profile can throw an error if no nesting or base profile is available
export async function fetchBaseProfileWithoutError(profile: IProfileLoaded): Promise<IProfileLoaded | undefined> {
let baseForProfile = undefined;
try {
baseForProfile = await ProfileManagement.getProfilesCache().fetchBaseProfile(profile.name);
} catch (ex) {
// no problem - no base profile, we'll return undefined
}
return baseForProfile;
}

export async function findRelatedZosProfiles(cicsProfile: IProfileLoaded, zosProfiles: IProfileLoaded[]): Promise<IProfileLoaded> {
const baseForCicsProfile = await fetchBaseProfileWithoutError(cicsProfile);

// sort profiles with zosmf ones first to make zosmf the default
// also filter so we only automatically pick z/os connections that have credentials associated
zosProfiles = zosProfiles.sort((prof) => (prof.profile.type === "zosmf" ? -1 : 1)).filter((prof) => prof.profile.user);

// filter out profiles that are not in the same base as the cics profile
const matchingProfiles: IProfileLoaded[] = [];
if (baseForCicsProfile) {
for (const profile of zosProfiles) {
if (baseForCicsProfile && baseForCicsProfile.name === (await fetchBaseProfileWithoutError(profile))?.name) {
matchingProfiles.push(profile);
}
}
}

if (matchingProfiles.length > 0) {
CICSLogger.debug(`Located matching z/OS profile by base profile: ${matchingProfiles[0]?.name}`);
return matchingProfiles[0];
}

// we couldn't find anything within a profile group
// filter down to just profiles that have the same hostname as our cics connection
const cicsHostProfile = zosProfiles.filter((profile) => cicsProfile.profile.host === profile.profile.host)[0];
if (cicsHostProfile) {
CICSLogger.debug(`Located matching z/OS profile by hostname ${cicsHostProfile?.name}`);
return cicsHostProfile;
}

return undefined;
}

async function promptUserForProfile(zosProfiles: IProfileLoaded[]): Promise<string> {
const profileNames = zosProfiles.map((profile) => profile.name);

if (profileNames.length === 0) {
return null;
}
// ask the user to pick from the profiles passed in
const quickPickOptions: vscode.QuickPickOptions = {
placeHolder: vscode.l10n.t("Select a profile to access the logs"),
ignoreFocusOut: true,
canPickMany: false,
};
const chosenProfileName = await Gui.showQuickPick(profileNames, quickPickOptions);
if (chosenProfileName === undefined) {
return chosenProfileName;
}
// if the profile they picked doesn't have credentials, prompt the user for them
const chosenProfileLoaded = zosProfiles.filter((profile) => profile.name === chosenProfileName)[0];
const chosenProfile = chosenProfileLoaded.profile;
if (!(chosenProfile.user || chosenProfile.certFile || chosenProfile.tokenValue)) {
CICSLogger.debug(`Prompting for credentials for ${chosenProfileName}`);
await ZoweVsCodeExtension.updateCredentials(
{
profile: chosenProfileLoaded,
rePrompt: false,
},
ProfileManagement.getExplorerApis()
);
}
// call fetchAllProfiles again otherwise we get an expect error about requiring a session to be defined
const requestTheProfileAgain = (await ProfileManagement.getProfilesCache().fetchAllProfiles()).filter((prof) => prof.name === chosenProfileName)[0];
return requestTheProfileAgain.name;
}

export async function getJobIdForRegion(selectedRegion: CICSRegionTree): Promise<string> {
// when we have a CICSRGN table we have jobid, but not when we have a
// MAS table. we get either CICSRGN or MAS depending on where we are in the
Expand All @@ -121,16 +40,6 @@ export async function getJobIdForRegion(selectedRegion: CICSRegionTree): Promise
return jobid;
}

export function doesConnectionSupportJes(profile: IProfileLoaded) {
try {
ZoweVsCodeExtension.getZoweExplorerApi().getJesApi(profile);
return true;
} catch (ex) {
// profile does not support JES API
}
return false;
}

export function getShowRegionLogs(treeview: TreeView<any>) {
return commands.registerCommand("cics-extension-for-zowe.showRegionLogs", async (node: CICSRegionTree) => {
const selectedRegion = node ?? treeview.selection[0];
Expand All @@ -149,7 +58,7 @@ export function getShowRegionLogs(treeview: TreeView<any>) {

const allProfiles = await ProfileManagement.getProfilesCache().fetchAllProfiles();
// do not include the FTP profile because it doesn't support spools for running jobs.
const zosProfiles = allProfiles.filter((element) => !["zftp"].includes(element.type) && doesConnectionSupportJes(element));
const zosProfiles = allProfiles.filter((element) => !["zftp"].includes(element.type) && doesProfileSupportConnectionType(element, "jes"));

let chosenProfileName: string;

Expand Down
Loading
Loading