Skip to main content
Version: v1.5.x

Developing a new plug-in

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.