Skip to main content
Version: v1.8.x

Developing a new plug-in

Before you begin this tutorial, make sure that you completed the Extending an existing plug-in tutorial.

Overview#

This tutorial demonstrates how to create a brand new Zoweâ„¢ CLI plug-in that uses Zowe CLI Node.js programmatic APIs.

At the end of this tutorial, you will have created a data set diff utility plug-in for Zowe CLI, from which you can pipe your plugin's output to a third-party utility for a side-by-side diff of data set member contents.

Side by Side Diff

Completed source for this tutorial can be found on the develop-a-plugin branch of the zowe-cli-sample-plugin repository.

Cloning the sample plug-in source#

Clone the sample repo, delete the irrelevant source, and create a brand new plug-in. Follow these steps:

  1. cd into your zowe-tutorial folder
  2. git clone https://github.com/zowe/zowe-cli-sample-plugin files-util
  3. cd files-util
  4. Delete the .git (hidden) folder.
  5. Delete all content within the src/api, src/cli, and docs folders.
  6. Delete all content within the __tests__/__system__/api, __tests__/__system__/cli, __tests__/api, and __tests__/cli folders
  7. git init
  8. git add .
  9. git commit -m "initial"

Changing package.json#

Use a unique npm name for your plugin. Change package.json name field as follows:

"name": "@brightside/files-util",

Issue the command npm install against the local repository.

Adjusting Imperative CLI Framework configuration#

Change imperative.ts to contain the following:

import { IImperativeConfig } from "@brightside/imperative";
const config: IImperativeConfig = {
commandModuleGlobs: ["**/cli/*/*.definition!(.d).*s"],
rootCommandDescription: "Files utilty plugin for Zowe CLI",
envVariablePrefix: "FILES_UTIL_PLUGIN",
defaultHome: "~/.files_util_plugin",
productDisplayName: "Files Util Plugin",
name: "files-util"
};
export = config;

Here we adjusted the description and other fields in the imperative JSON configuration to be relevant to this plug-in.

Adding third-party packages#

We'll use the following packages to create a programmatic API:

  • npm install --save diff
  • npm install -D @types/diff

Creating a Node.js programmatic API#

In files-util/src/api, create a file named DataSetDiff.ts. The content of DataSetDiff.ts should be the following:

import { AbstractSession } from "@brightside/imperative";
import { Download, IDownloadOptions, IZosFilesResponse } from "@brightside/core";
import * as diff from "diff";
import { readFileSync } from "fs";
export class DataSetDiff {
public static async diff(session: AbstractSession, oldDataSet: string, newDataSet: string) {
let error;
let response: IZosFilesResponse;
const options: IDownloadOptions = {
extension: "dat",
};
try {
response = await Download.dataSet(session, oldDataSet, options);
} catch (err) {
error = "oldDataSet: " + err;
throw error;
}
try {
response = await Download.dataSet(session, newDataSet, options);
} catch (err) {
error = "newDataSet: " + err;
throw error;
}
const regex = /\.|\(/gi; // Replace . and ( with /
const regex2 = /\)/gi; // Replace ) with .
// convert the old data set name to use as a path/file
let file = oldDataSet.replace(regex, "/");
file = file.replace(regex2, ".") + "dat";
// Load the downloaded contents of 'oldDataSet'
const oldContent = readFileSync(`${file}`).toString();
// convert the new data set name to use as a path/file
file = newDataSet.replace(regex, "/");
file = file.replace(regex2, ".") + "dat";
// Load the downloaded contents of 'oldDataSet'
const newContent = readFileSync(`${file}`).toString();
return diff.createTwoFilesPatch(oldDataSet, newDataSet, oldContent, newContent, "Old", "New");
}
}

Exporting your API#

In files-util/src, change index.ts to contain the following:

export * from "./api/DataSetDiff";

Checkpoint#

At this point, you should be able to rebuild the plug-in without errors via npm run build. You included third party dependencies, created a programmatic API, and customized this new plug-in project. Next, you'll define the command to invoke your programmatic API.

Defining commands#

In files-util/src/cli, create a folder named diff. Within the diff folder, create a file Diff.definition.ts. Its content should be as follows:

import { ICommandDefinition } from "@brightside/imperative";
import { DataSetsDefinition } from "./data-sets/DataSets.definition";
const IssueDefinition: ICommandDefinition = {
name: "diff",
summary: "Diff two data sets content",
description: "Uses open source diff packages to diff two data sets content",
type: "group",
children: [DataSetsDefinition]
};
export = IssueDefinition;

Also within the diff folder, create a folder named data-sets. Within the data-sets folder create DataSets.definition.ts and DataSets.handler.ts.

DataSets.definition.ts should contain:

import { ICommandDefinition } from "@brightside/imperative";
export const DataSetsDefinition: ICommandDefinition = {
name: "data-sets",
aliases: ["ds"],
summary: "data sets to diff",
description: "diff the first data set with the second",
type: "command",
handler: __dirname + "/DataSets.handler",
positionals: [
{
name: "oldDataSet",
description: "The old data set",
type: "string"
},
{
name: "newDataSet",
description: "The new data set",
type: "string"
}
],
profile: {
required: ["zosmf"]
}
};

DataSets.handler.ts should contain the following:

import { ICommandHandler, IHandlerParameters, TextUtils, Session } from "@brightside/imperative";
import { DataSetDiff } from "../../../api/DataSetDiff";
export default class DataSetsDiffHandler implements ICommandHandler {
public async process(params: IHandlerParameters): Promise<void> {
const profile = params.profiles.get("zosmf");
const session = new Session({
type: "basic",
hostname: profile.host,
port: profile.port,
user: profile.user,
password: profile.pass,
base64EncodedAuth: profile.auth,
rejectUnauthorized: profile.rejectUnauthorized,
});
const resp = await DataSetDiff.diff(session, params.arguments.oldDataSet, params.arguments.newDataSet);
params.response.console.log(resp);
}
}

Trying your command#

Be sure to build your plug-in via npm run build.

Install your plug-in into Zowe CLI via zowe plugins install.

Issue the following command. Replace the data set names with valid mainframe data set names on your system:

Pipe Output

The raw diff output is displayed as a command response:

Raw Diff Output

Bringing together new tools!#

The advantage of Zowe CLI and of the CLI approach in mainframe development is that it allows for combining different developer tools for new and interesting uses.

diff2html is a free tool to generate HTML side-by-side diffs to help see actual differences in diff output.

Install the diff2html CLI via npm install -g diff2html-cli. Then, pipe your Zowe CL plugin's output into diff2html to generate diff HTML and launch a web browser that contains the content in the screen shot at the top of this file.

  • zowe files-util diff data-sets "kelda16.work.jcl(iefbr14)" "kelda16.work.jcl(iefbr15)" | diff2html -i stdin

Next steps#

Try the Implementing profiles in a plug-in tutorial to learn about using profiles with your plug-in.