Skip to content

Commit

Permalink
Merge pull request #2925 from crshnburn/zos-console-panel
Browse files Browse the repository at this point in the history
Contribute z/OS console panel
  • Loading branch information
JillieBeanSim authored Aug 20, 2024
2 parents ac466e9 + e50a578 commit ea0b0b0
Show file tree
Hide file tree
Showing 16 changed files with 406 additions and 8 deletions.
1 change: 1 addition & 0 deletions packages/zowe-explorer-api/src/vscode/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
*/

export * from "./WebView";
export * as HTMLTemplate from "./utils/HTMLTemplate";
10 changes: 6 additions & 4 deletions packages/zowe-explorer-api/src/vscode/ui/utils/HTMLTemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,24 @@
/**
* HTML template that is compiled with Mustache to load a WebView instance at runtime.
*/
const HTMLTemplate: string = `
const HTMLTemplate: string = /*html*/ `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; font-src data:; img-src data: vscode-resource: https:; script-src 'nonce-{{ nonce }}';
<meta
http-equiv="Content-Security-Policy"
content="default-src 'none'; font-src data: https:; img-src data: vscode-resource: https:; script-src 'nonce-{{ nonce }}';
style-src vscode-resource: 'unsafe-inline' http: https: data:;"
/>
<base href="{{ uris.resource.build }}">
{{{ style }}}
</head>
<body>
<noscript>You'll need to enable JavaScript to run this app.</noscript>
{{{ startup }}}
<div id="webviewRoot"></div>
<script type="module" nonce="{{ nonce }}" src="{{ uris.resource.script }}" />
</body>
Expand Down
2 changes: 2 additions & 0 deletions packages/zowe-explorer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen

### New features and enhancements

- Added new Zowe Explorer z/OS Console webview with access via VS Code command pallete to issue MVS Console commands.

### Bug fixes

- Fixed issue where creating a new team configuration file could cause Zowe Explorer to crash, resulting in all sessions disappearing from trees. [#2906](https://github.com/zowe/zowe-explorer-vscode/issues/2906)
Expand Down
12 changes: 12 additions & 0 deletions packages/zowe-explorer/__mocks__/@zowe/zos-console-for-zowe-sdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* 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 interface IIssueParms {}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jest.mock("util");
jest.mock("isbinaryfile");

async function createGlobalMocks() {
const mockReadProfilesFromDisk = jest.fn();
const mockReadProfilesFromDisk = jest.fn().mockReturnValue(Promise.resolve());
const globalMocks = {
mockLoadNamedProfile: jest.fn(),
mockMkdirSync: jest.fn(),
Expand All @@ -49,6 +49,7 @@ async function createGlobalMocks() {
mockCreateTreeView: jest.fn().mockReturnValue({ onDidCollapseElement: jest.fn() }),
mockExecuteCommand: jest.fn(),
mockRegisterCommand: jest.fn(),
mockRegisterWebviewViewProvider: jest.fn(),
mockOnDidCloseTextDocument: jest.fn(),
mockOnDidSaveTextDocument: jest.fn(),
mockOnDidChangeSelection: jest.fn(),
Expand Down Expand Up @@ -281,6 +282,10 @@ async function createGlobalMocks() {
value: globalMocks.mockRegisterCommand,
configurable: true,
});
Object.defineProperty(vscode.window, "registerWebviewViewProvider", {
value: globalMocks.mockRegisterWebviewViewProvider,
configurable: true,
});
Object.defineProperty(vscode.commands, "executeCommand", {
value: globalMocks.mockExecuteCommand,
configurable: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 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 { createInstanceOfProfile, createIProfile } from "../../../__mocks__/mockCreators/shared";
import { ZosConsoleViewProvider } from "../../../src/zosconsole/ZosConsolePanel";
import { Profiles } from "../../../src/Profiles";
import * as vscode from "vscode";

describe("ZosConsoleViewProvider", () => {
function createGlobalMocks(): any {
const newMocks = {
imperativeProfile: createIProfile(),
profileInstance: null,
testWebView: {},
};
newMocks.testWebView = {
webview: {
postMessage: jest.fn(),
asWebviewUri: jest.fn(),
onDidReceiveMessage: jest.fn(),
},
};
newMocks.profileInstance = createInstanceOfProfile(newMocks.imperativeProfile);
Object.defineProperty(Profiles, "getInstance", {
value: jest.fn().mockReturnValue(newMocks.profileInstance),
configurable: true,
});
Object.defineProperty(vscode.Uri, "joinPath", { value: jest.fn(), configurable: true });

return newMocks;
}
describe("resolveWebviewView", () => {
it("should submit command", () => {
const globalMocks = createGlobalMocks();
const myconsole = new ZosConsoleViewProvider({} as any);
myconsole.resolveWebviewView(globalMocks.testWebView, {} as any, { isCancellationRequested: false } as any);
expect(globalMocks.testWebView.webview.onDidReceiveMessage).toBeCalled();
});
});
});
15 changes: 15 additions & 0 deletions packages/zowe-explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@
"title": "%viewsContainers.activitybar%",
"icon": "resources/zowe.svg"
}
],
"panel": [
{
"id": "zowezosconsole",
"title": "Zowe Explorer z/OS Console",
"icon": ""
}
]
},
"views": {
Expand All @@ -57,6 +64,13 @@
"id": "zowe.jobs.explorer",
"name": "%zowe.jobs.explorer%"
}
],
"zowezosconsole": [
{
"id": "zosconsole",
"name": "z/OS Console",
"type": "webview"
}
]
},
"keybindings": [
Expand Down Expand Up @@ -2107,6 +2121,7 @@
"sinon": "^6.1.0"
},
"dependencies": {
"@vscode/codicons": "^0.0.35",
"@zowe/secrets-for-zowe-sdk": "^7.18.6",
"@zowe/zowe-explorer-api": "2.18.0-SNAPSHOT",
"dayjs": "^1.11.10",
Expand Down
3 changes: 2 additions & 1 deletion packages/zowe-explorer/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ProfilesUtils } from "./utils/ProfilesUtils";
import { initializeSpoolProvider } from "./SpoolProvider";
import { cleanTempDir, hideTempFolder, findRecoveredFiles } from "./utils/TempFolder";
import { SettingsConfig } from "./utils/SettingsConfig";
import { registerCommonCommands, registerCredentialManager, registerRefreshCommand, watchConfigProfile } from "./shared/init";
import { registerCommonCommands, registerCredentialManager, registerRefreshCommand, registerZosConsoleView, watchConfigProfile } from "./shared/init";
import { ZoweLogger } from "./utils/LoggerUtils";
import { ZoweSaveQueue } from "./abstract/ZoweSaveQueue";
import { PollDecorator } from "./utils/DecorationProviders";
Expand Down Expand Up @@ -60,6 +60,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<ZoweEx
const providers = await TreeProviders.initializeProviders(context, { ds: initDatasetProvider, uss: initUSSProvider, job: initJobsProvider });

registerCommonCommands(context, providers);
registerZosConsoleView(context);
ZoweExplorerExtender.createInstance(providers.ds, providers.uss, providers.job);
await SettingsConfig.standardizeSettings();
watchConfigProfile(context);
Expand Down
6 changes: 6 additions & 0 deletions packages/zowe-explorer/src/shared/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { DatasetTree } from "../dataset/DatasetTree";
import { USSTree } from "../uss/USSTree";
import { ZosJobsProvider } from "../job/ZosJobsProvider";
import { CertificateWizard } from "../utils/CertificateWizard";
import { ZosConsoleViewProvider } from "../zosconsole/ZosConsolePanel";

// Set up localization
nls.config({
Expand Down Expand Up @@ -265,6 +266,11 @@ export function watchConfigProfile(context: vscode.ExtensionContext): void {
});
}

export function registerZosConsoleView(context: vscode.ExtensionContext): void {
const provider = new ZosConsoleViewProvider(context.extensionUri);
context.subscriptions.push(vscode.window.registerWebviewViewProvider(ZosConsoleViewProvider.viewType, provider));
}

export function initSubscribers(context: vscode.ExtensionContext, theProvider: IZoweTree<IZoweTreeNode>): void {
ZoweLogger.trace("shared.init.initSubscribers called.");
const theTreeView = theProvider.getTreeView();
Expand Down
3 changes: 2 additions & 1 deletion packages/zowe-explorer/src/webviews/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@vscode/codicons": "^0.0.33",
"typescript": "^4.5.3",
"vite": "^4.5.3",
"vite-plugin-checker": "^0.6.4"
"vite-plugin-checker": "^0.6.4",
"vite-plugin-static-copy": "^0.17.1"
}
}
95 changes: 95 additions & 0 deletions packages/zowe-explorer/src/webviews/src/zos-console/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { Dropdown, Option, TextArea, TextField } from "@vscode/webview-ui-toolkit";
import { VSCodeDropdown, VSCodeTextArea, VSCodeTextField } from "@vscode/webview-ui-toolkit/react";
import { useEffect, useState } from "preact/hooks";

declare const vscode: any;

export function App() {
const [consoleContent, setConsoleContent] = useState("");
useEffect(() => {
window.addEventListener("message", (event) => {
// Prevent users from sending data into webview outside of extension/webview context
const eventUrl = new URL(event.origin);
const isWebUser =
(eventUrl.protocol === document.location.protocol && eventUrl.hostname === document.location.hostname) ||
eventUrl.hostname.endsWith(".github.dev");
const isLocalVSCodeUser = eventUrl.protocol === "vscode-webview:";

if (!isWebUser && !isLocalVSCodeUser) {
return;
}

const message = event.data;
const profileList = document.getElementById("systems") as Option;

switch (message.type) {
case "commandResult":
setConsoleContent(consoleContent + `> ${message.cmd} (${message.profile})\n${message.result}`);
break;
case "optionsList":
for (const profile in message.profiles) {
const option = document.createElement("vscode-option");
option.textContent = message.profiles[profile];
if (message.profiles[profile] === message.defaultProfile) {
option.setAttribute("selected", "true");
}
profileList.appendChild(option);
}
break;
}
});
const consoleResponse = document.getElementById("output") as TextArea;
consoleResponse.control.scrollTop = consoleResponse.control.scrollHeight;
});

const sendCommand = (e: KeyboardEvent) => {
const consoleField = document.getElementById("command-input") as TextField;
const profileList = document.getElementById("systems") as Dropdown;
if (e.key === "Enter") {
if (consoleField!.value === "clear") {
setConsoleContent("");
} else {
vscode.postMessage({
command: "opercmd",
profile: profileList.options[profileList.selectedIndex].text,
text: consoleField.value,
});
}
consoleField.value = "";
}
};

return (
<div className="box">
<div style="display: flex; align-items: center; gap: 10px;">
<VSCodeTextField
id="command-input"
name="command-input"
type="text"
placeholder="Enter command here..."
onKeyDown={sendCommand}
style={{
width: "100%",
"font-family": "Consolas,monospace",
}}
>
<span slot="start" className="codicon codicon-chevron-right"></span>
</VSCodeTextField>
<VSCodeDropdown id="systems"></VSCodeDropdown>
</div>
<VSCodeTextArea
id="output"
readonly
resize="none"
value={consoleContent}
style={{
width: "100%",
height: "100%",
overflow: "auto",
display: "flex",
"font-family": "Consolas,monospace",
}}
></VSCodeTextArea>
</div>
);
}
12 changes: 12 additions & 0 deletions packages/zowe-explorer/src/webviews/src/zos-console/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>z/OS Console</title>
</head>
<body>
<div id="webviewRoot"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>
4 changes: 4 additions & 0 deletions packages/zowe-explorer/src/webviews/src/zos-console/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { render } from "preact";
import { App } from "./App";

render(<App />, document.getElementById("webviewRoot")!);
13 changes: 13 additions & 0 deletions packages/zowe-explorer/src/webviews/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import preact from "@preact/preset-vite";
import * as path from "path";
import { readdirSync } from "fs";
import checker from "vite-plugin-checker";
import { viteStaticCopy } from "vite-plugin-static-copy";

// https://vitejs.dev/config/

Expand All @@ -40,6 +41,18 @@ export default defineConfig({
checker({
typescript: true,
}),
viteStaticCopy({
targets: [
{
src: "../../../node_modules/@vscode/codicons/dist/codicon.css",
dest: "codicons/",
},
{
src: "../../../node_modules/@vscode/codicons/dist/codicon.ttf",
dest: "codicons/",
},
],
}),
],
root: path.resolve(__dirname, "src"),
build: {
Expand Down
Loading

0 comments on commit ea0b0b0

Please sign in to comment.