-
Notifications
You must be signed in to change notification settings - Fork 6
Add extension that runs REXX over ZOWE CLI #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.DS_Store | ||
node_modules | ||
*.vsix | ||
*.zip |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
// Use IntelliSense to learn about possible attributes. | ||
// Hover to view descriptions of existing attributes. | ||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"name": "Run Extension", | ||
"type": "extensionHost", | ||
"request": "launch", | ||
"runtimeExecutable": "${execPath}", | ||
"args": ["--extensionDevelopmentPath=${workspaceFolder}"], | ||
} | ||
] | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"editor.formatOnSave": true | ||
} |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,65 @@ | ||||||
# VS Code extension for the example.com company | ||||||
|
||||||
This is an example of an extension a company `example.com` may want to create to supplement Code4z with custom, company specific functionality. | ||||||
|
||||||
## Making your custom ISPF apps available in VS Code | ||||||
|
||||||
If you have custom ISPF panels for your internal processes you may want to make their functionality available in VS Code as part of your DevOps modernization. | ||||||
|
||||||
The following sections outline the steps to take. | ||||||
|
||||||
### Update your ISPF application to run in plain TSO | ||||||
|
||||||
1. Update your ISPF application to accept arguments instead of relying on screen inputs | ||||||
1. Update the application to store its output to a dataset, or print it to the terminal instead of showing the result on an ISPF screen | ||||||
1. Test your updated application by running it in TSO without ISPF and check its output. | ||||||
1. Run your updated application through ZOWE CLI via the following command `zowe tso issue command "exec 'PUBLIC.REXX(REPOUT)' 'ARG1 ARG2'"`, where `PUBLIC.REXX(REPOUT)` is your REXX application and `ARG1` `ARG2` are the arguments that are passed to it. The syntax above works both in Windows CMD, Powershell as well as bourne compatible UNIX shells. | ||||||
|
||||||
### Use the basic-report command in this extension | ||||||
|
||||||
1. Update the `REXX_EXEC` constant in [basic-report.js](commands/basic-report.js#L6) to point to your application. | ||||||
1. Start the extension by pressing `F5`. This will start a new VS Code window with this extension. | ||||||
1. In this new VS Code window open the _Command Palette_ by pressing `F1` | ||||||
1. Type `example.com` in the command palette input box | ||||||
|
||||||
Now you should see  | ||||||
|
||||||
1. Select the `Basic Report on a Dataset` command | ||||||
1. After a short moment an editor with the output of your applications will open | ||||||
|
||||||
If you would like to try this out with a basic REXX program, you can use the included [basic-report.rexx](commands/basic-report.rexx) sample. The output should look similar to  | ||||||
|
||||||
### Explore the ehnanced-report | ||||||
|
||||||
The [basic report](commands/basic-report.js) is only 30 lines long. It is as simple as possible to get started quickly. To make the extension real there is a lot more to do. For example: | ||||||
|
||||||
- Input validation | ||||||
- Error checking | ||||||
- Remembering last entry | ||||||
- Adding a progress bar | ||||||
- Storeing the report to a dataset and retrieving it from there | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
- Adding a VS Code Output channel to diagnose issues | ||||||
- Adding a setting for the location of the REXX exec instead of hard coding it in the extension code | ||||||
|
||||||
All of these ehnancements have been added to the [enhanced report](commands/enhanced-report.js) with its corresponding [enhanced-report.rexx](commands/enhanced-report.rexx) REXX exec. This adds a little over 100 lines of code and illustrated many other useful VS Code APIs as well as adding typescript checking via [JS Doc](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) annotations. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
### Build the extension | ||||||
|
||||||
The extension can be build by running the two following commands | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
``` | ||||||
# Install development dependencies - typescript, types, and vsce | ||||||
npm install | ||||||
# Package the extension | ||||||
npm run package | ||||||
``` | ||||||
|
||||||
### Next steps | ||||||
|
||||||
A few ideas about what you may want to try next: | ||||||
|
||||||
- Remember last 10 user inputs and let them choose (in addition to typing a new one) | ||||||
- Submit a JOB and retrieve its output instead of running a REXX exec | ||||||
- Use the ZOWE SDK instead of ZOWE CLI (remove run-time dependency) | ||||||
- Execute REXX exec over SSH or submit a JOB over FTP if you do not have ZOWE CLI available | ||||||
- Copy the REXX exec to the mainframe before a command runs (in case the REXX does not exist) - self deploy |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// @ts-nocheck | ||
const vscode = require('vscode'); | ||
const util = require('node:util'); | ||
const execFile = util.promisify(require('node:child_process').execFile); | ||
|
||
const REXX_EXEC = "PUBLIC.REXX(BASIC)"; | ||
|
||
const simpleReport = context => async () => { | ||
await vscode.workspace.fs.createDirectory(context.globalStorageUri); | ||
const reportUri = vscode.Uri.joinPath(context.globalStorageUri, "report.txt"); | ||
|
||
const dsn = await vscode.window.showInputBox({ | ||
placeHolder: 'Please enter a name of a PDS to inquire' | ||
}); | ||
|
||
const output = await execRexx(dsn); | ||
await vscode.workspace.fs.writeFile(reportUri, Buffer.from(output, 'utf-8')); | ||
|
||
const document = await vscode.workspace.openTextDocument(reportUri); | ||
await vscode.window.showTextDocument(document); | ||
} | ||
|
||
async function execRexx(dsn) { | ||
const { stdout, stderr } = await execFile('zowe', ["tso", "issue", "command", `exec '${REXX_EXEC}' '${dsn}'`]); | ||
return stdout; | ||
} | ||
|
||
module.exports = { | ||
simpleReport | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* REXX */ | ||
parse arg dsn | ||
|
||
say "===========================================================" | ||
say "|" | ||
say "| REPORT FROM RUNNING 'LISTDS' MEMBER ON:" | ||
say "|" | ||
say "| " dsn | ||
say "|" | ||
say "===========================================================" | ||
say " " | ||
|
||
"LISTDS '"dsn"' MEMBERS" | ||
|
||
EXIT |
137 changes: 137 additions & 0 deletions
137
code4z/example-com-extension/commands/enhanced-report.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
const vscode = require('vscode'); | ||
const util = require('node:util'); | ||
const execFile = util.promisify(require('node:child_process').execFile); | ||
|
||
const CONFIG_PREFIX = 'example-com'; | ||
const REPORT_EXEC = 'reportExec'; | ||
|
||
/** | ||
* @type {vscode.ExtensionContext} | ||
*/ | ||
let context; | ||
|
||
/** | ||
* @type {vscode.LogOutputChannel} | ||
*/ | ||
let log; | ||
|
||
/** | ||
* @param {vscode.ExtensionContext} ctx | ||
* @param {vscode.LogOutputChannel} lg | ||
*/ | ||
|
||
function registerReportCommand(ctx, lg) { | ||
context = ctx; | ||
log = lg; | ||
return reportCommand; | ||
} | ||
|
||
async function reportCommand() { | ||
await vscode.workspace.fs.createDirectory(context.globalStorageUri); | ||
const exec = vscode.workspace.getConfiguration(`${CONFIG_PREFIX}`).get(REPORT_EXEC); | ||
if (!exec) { | ||
const response = await vscode.window.showInformationMessage("Report exec setting missing, canceling action", 'Open Settings'); | ||
if (response == 'Open Settings') { | ||
vscode.commands.executeCommand('workbench.action.openSettings', `${CONFIG_PREFIX}.${REPORT_EXEC}`); | ||
} | ||
return; | ||
} | ||
let arg = await vscode.window.showInputBox({ | ||
placeHolder: 'Please enter a name of a PDS to inquire', | ||
value: context.globalState.get('arg'), | ||
validateInput: isValidDsn | ||
}); | ||
if (!arg) { | ||
vscode.window.showInformationMessage("No dataset name provided, canceling action"); | ||
return; | ||
} | ||
arg = arg.toUpperCase(); | ||
context.globalState.update('arg', arg); | ||
const reportFileName = `report_on_${arg.toUpperCase()}.txt`; | ||
const reportFileUri = vscode.Uri.joinPath(context.globalStorageUri, reportFileName); | ||
// log.show(); | ||
await vscode.window.withProgress({ | ||
title: `Reporting on ${arg}`, | ||
location: vscode.ProgressLocation.Notification, | ||
cancellable: false | ||
}, async (progress, _token) => { | ||
progress.report({ increment: 20, message: `Execuring Report` }); | ||
const reportOutputDsn = await executeReport(exec, arg); | ||
if (!reportOutputDsn) { | ||
vscode.window.showInformationMessage("No report output provided"); | ||
return; | ||
} | ||
progress.report({ increment: 30, message: `Downloading Report` }); | ||
await downloadReport(reportOutputDsn, reportFileUri); | ||
|
||
}); | ||
|
||
const reportUri = vscode.Uri.from({ scheme: ReportProvider.scheme, path: reportFileName }); | ||
await openReport(reportUri); | ||
} | ||
|
||
/** | ||
* @param {string} exec rexx or clist to execute | ||
* @param {string} arg argument to the report exec | ||
*/ | ||
|
||
async function executeReport(exec, arg) { | ||
const { stdout, stderr } = await execFile('zowe', ["tso", "issue", "command", `exec '${exec}' '${arg}'`]); | ||
log.info(`Executing REXX exec: ${exec} ${arg}`); | ||
stdout.split("\n").forEach(line => line && log.info(line)); | ||
stderr.split("\n").forEach(line => line && log.error(line)); | ||
const dsnLine = stdout.split("\n").find(line => line.match(/^DSN=/)); | ||
const dsn = dsnLine?.replace('DSN=', ''); | ||
return dsn; | ||
} | ||
/** | ||
* @param {string} reportDsn dataset from which to download report | ||
* @param {vscode.Uri} reportUri file uri where to store the downloaded report | ||
*/ | ||
|
||
async function downloadReport(reportDsn, reportUri) { | ||
const { stdout, stderr } = await execFile('zowe', ["files", "download", "data-set", reportDsn, "-f", reportUri.fsPath]); | ||
log.info(`Downloading report from ${reportDsn} to ${reportUri}`); | ||
stdout.split("\n").forEach(line => line && log.info(line)); | ||
stderr.split("\n").forEach(line => line && log.error(line)); | ||
} | ||
/** | ||
* @implements {vscode.TextDocumentContentProvider} | ||
*/ | ||
class ReportProvider { | ||
static scheme = "com-example+report"; | ||
/** | ||
* @param {vscode.Uri} uri | ||
* @param {vscode.CancellationToken} token | ||
*/ | ||
async provideTextDocumentContent(uri, token) { | ||
const fileUri = vscode.Uri.joinPath(context.globalStorageUri, uri.path); | ||
const content = await vscode.workspace.fs.readFile(fileUri); | ||
return content.toString(); | ||
} | ||
} | ||
|
||
/** | ||
* @param {vscode.Uri} reportUri | ||
*/ | ||
|
||
async function openReport(reportUri) { | ||
const document = await vscode.workspace.openTextDocument(reportUri); | ||
await vscode.window.showTextDocument(document); | ||
} | ||
/** | ||
* | ||
* @param {string} input | ||
*/ | ||
function isValidDsn(input) { | ||
const datasetPattern = /^([A-Za-z@#$][0-9A-za-z@#$]{0,7}\.)+([A-Za-z@#$][0-9A-Za-z@#$]{0,7})$/; | ||
if (input.length <= 44 && input.match(datasetPattern)) { | ||
return; | ||
} | ||
return 'Please, enter a valid dataset name'; | ||
} | ||
|
||
module.exports = { | ||
registerReportCommand, | ||
ReportProvider | ||
} |
35 changes: 35 additions & 0 deletions
35
code4z/example-com-extension/commands/enhanced-report.rexx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* REXX */ | ||
PARSE ARG dsname | ||
OUTPUT = userid()||'.PUBLIC.JCLOUT(REPORT)' | ||
|
||
flower.0 = 0 | ||
call prnt "===========================================================" | ||
call prnt "|" | ||
call prnt "| REPORT FROM RUNNING 'LISTDS' MEMBER ON:" | ||
call prnt "|" | ||
call prnt "| " dsname | ||
call prnt "|" | ||
call prnt "===========================================================" | ||
call prnt " " | ||
|
||
X = OUTTRAP("memlst.") | ||
"LISTDS '"dsname"' MEMBERS" | ||
X = OUTTRAP("OFF") | ||
|
||
"ALLOC FI(SYSUT2) DA('"||OUTPUT||"') SHR REUSE" | ||
"EXECIO 0 DISKW SYSUT2 (OPEN" | ||
"EXECIO * DISKW SYSUT2 (STEM flower." | ||
"EXECIO * DISKW SYSUT2 (STEM memlst." | ||
"EXECIO 0 DISKW SYSUT2 (FINIS" | ||
"FREE FI(SYSUT2)" | ||
|
||
SAY "DSN='"||OUTPUT||"'" | ||
EXIT | ||
|
||
prnt: | ||
arg text | ||
i = flower.0 | ||
i = i+1 | ||
flower.i = text | ||
flower.0 = i | ||
return |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
const vscode = require('vscode'); | ||
const url = 'https://techdocs.broadcom.com/us/en/ca-mainframe-software/devops/code4z/2-0.html'; | ||
|
||
async function openDocsCommand() { | ||
const docsUrl = vscode.Uri.parse(url); | ||
vscode.env.openExternal(docsUrl); | ||
} | ||
|
||
module.exports = { | ||
openDocsCommand | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const vscode = require('vscode'); | ||
const { registerReportCommand, ReportProvider } = require("./commands/enhanced-report"); | ||
const { openDocsCommand } = require('./commands/open-docs'); | ||
const { simpleReport } = require('./commands/basic-report'); | ||
|
||
/** | ||
* @param {vscode.ExtensionContext} context | ||
*/ | ||
function activate(context) { | ||
const log = vscode.window.createOutputChannel('Example.com', { log: true }); | ||
|
||
// Report on Dataset | ||
const reportDisp = vscode.commands.registerCommand('com.example.report.on.dataset', registerReportCommand(context, log)); | ||
const provider = new ReportProvider; | ||
const providerRegistration = vscode.workspace.registerTextDocumentContentProvider(ReportProvider.scheme, provider); | ||
context.subscriptions.push(reportDisp, providerRegistration); | ||
|
||
// Simple Report | ||
const simpleReportDisp = vscode.commands.registerCommand('com.example.simple.report', simpleReport(context)); | ||
context.subscriptions.push(simpleReportDisp); | ||
|
||
// Open Docs | ||
const openDocsDisp = vscode.commands.registerCommand('com.example.open.docs', openDocsCommand); | ||
context.subscriptions.push(openDocsDisp); | ||
|
||
console.log('Congrats, your com.example extension is now active!'); | ||
} | ||
|
||
function deactivate() { } | ||
|
||
module.exports = { | ||
activate, | ||
deactivate | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.