Skip to content

Commit 896b1d2

Browse files
Initial attempt at progress bar improvements
1 parent 9301210 commit 896b1d2

File tree

5 files changed

+170
-29
lines changed

5 files changed

+170
-29
lines changed

__tests__/api/BundleDeploy/BundleDeployer.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { BundleDeployer } from "../../../src/api/BundleDeploy/BundleDeployer";
1313
import { IHandlerParameters, TaskStage } from "@zowe/imperative";
1414
import * as DeployBundleDefinition from "../../../src/cli/deploy/bundle/DeployBundle.definition";
1515
import * as fse from "fs-extra";
16-
import { ZosmfSession, SubmitJobs, List } from "@zowe/cli";
16+
import { ZosmfSession, SubmitJobs, List, explainProvisionedInstanceExtended } from "@zowe/cli";
1717

1818

1919
const DEFAULT_PARAMTERS: IHandlerParameters = {
@@ -69,6 +69,9 @@ describe("BundleDeployer01", () => {
6969
});
7070
afterEach(() => {
7171
jest.restoreAllMocks();
72+
(DEFAULT_PARAMTERS.response.progress.startBar as jest.Mock).mockReset();
73+
(DEFAULT_PARAMTERS.response.progress.endBar as jest.Mock).mockReset();
74+
7275
});
7376
it("should complain with missing zOSMF profile for deploy", async () => {
7477
createSpy.mockImplementationOnce(() => { throw new Error( "Injected Create error" ); });
@@ -499,6 +502,24 @@ describe("BundleDeployer01", () => {
499502
parms.arguments.verbose = false;
500503
await testUndeployJCL(parms);
501504
});
505+
506+
it("should use task passed as parameter on deploy", async () => {
507+
submitSpy.mockImplementationOnce(() => [{ddName: "SYSTSPRT", stepName: "DFHDPLOY", data: "DFHRL2012I"}] );
508+
509+
let parms: IHandlerParameters;
510+
parms = DEFAULT_PARAMTERS;
511+
setCommonParmsForDeployTests(parms);
512+
parms.arguments.csdgroup = "12345678";
513+
514+
const bd = new BundleDeployer(parms);
515+
const task = { percentComplete: 0, stageName: TaskStage.NOT_STARTED, statusMessage: ""};
516+
const response = await bd.deployBundle(undefined, task);
517+
expect(task.stageName).toEqual(TaskStage.COMPLETE);
518+
expect(task.statusMessage).toEqual("Completed DFHDPLOY");
519+
expect(parms.response.progress.startBar).toHaveBeenCalledTimes(0);
520+
expect(parms.response.progress.endBar).toHaveBeenCalledTimes(0);
521+
522+
});
502523
});
503524

504525
async function runDeployTestWithError() {
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { SubtaskWithStatus } from "../../../src/api/BundlePush/SubtaskWithStatus";
2+
import { TaskStage, ITaskWithStatus, TaskProgress } from "@zowe/imperative";
3+
4+
5+
let parentTask: ITaskWithStatus;
6+
7+
describe("SubtaskWithStatus", () => {
8+
9+
beforeEach(() => {
10+
parentTask = {
11+
percentComplete: 0,
12+
statusMessage: "status",
13+
stageName: TaskStage.IN_PROGRESS
14+
};
15+
});
16+
it("Should fail with ticks < 0", () => {
17+
expect(() => {
18+
const task = new SubtaskWithStatus(parentTask, -1);
19+
}).toThrow("Ticks must be between 0 and 100");
20+
});
21+
22+
it("Should fail with ticks > 0", () => {
23+
expect(() => {
24+
const task = new SubtaskWithStatus(parentTask, 101);
25+
}).toThrow("Ticks must be between 0 and 100");
26+
});
27+
28+
it("Should set the parent task status", () => {
29+
const subTask = new SubtaskWithStatus(parentTask, 20);
30+
subTask.statusMessage = "Message from subtask";
31+
expect(parentTask.statusMessage).toEqual("Message from subtask");
32+
});
33+
34+
it("Should set the parent task stage", () => {
35+
const subTask = new SubtaskWithStatus(parentTask, 20);
36+
subTask.stageName = TaskStage.FAILED;
37+
expect(parentTask.stageName).toEqual(TaskStage.FAILED);
38+
});
39+
40+
it("Should not set the parent task to completed", () => {
41+
const subTask = new SubtaskWithStatus(parentTask, 20);
42+
subTask.stageName = TaskStage.COMPLETE;
43+
expect(parentTask.stageName).toEqual(TaskStage.IN_PROGRESS);
44+
});
45+
46+
it("Should not set the parent task to NOT_STARTED", () => {
47+
const subTask = new SubtaskWithStatus(parentTask, 20);
48+
subTask.stageName = TaskStage.NOT_STARTED;
49+
expect(parentTask.stageName).toEqual(TaskStage.IN_PROGRESS);
50+
});
51+
52+
it("should set percentComplete on parent task using scaled value", () => {
53+
const subTask = new SubtaskWithStatus(parentTask, 20);
54+
subTask.percentComplete = TaskProgress.FIFTY_PERCENT;
55+
expect(parentTask.percentComplete).toEqual(TaskProgress.TEN_PERCENT);
56+
});
57+
58+
it("should add to percentComplete on parent task using scaled value", () => {
59+
const subTask = new SubtaskWithStatus(parentTask, 40);
60+
parentTask.percentComplete = 50;
61+
subTask.percentComplete = TaskProgress.FIFTY_PERCENT;
62+
expect(parentTask.percentComplete).toEqual(TaskProgress.SEVENTY_PERCENT);
63+
});
64+
65+
it("should add to percentComplete on parent task using scaled value multiple times", () => {
66+
const subTask = new SubtaskWithStatus(parentTask, 40);
67+
parentTask.percentComplete = TaskProgress.FIFTY_PERCENT;
68+
subTask.percentComplete = 25;
69+
subTask.percentComplete = 50;
70+
subTask.percentComplete = 75;
71+
expect(parentTask.percentComplete).toEqual(TaskProgress.EIGHTY_PERCENT);
72+
});
73+
74+
it("should return subtask percentage complete", () => {
75+
const subTask = new SubtaskWithStatus(parentTask, 40);
76+
subTask.percentComplete = 50;
77+
expect(subTask.percentComplete).toEqual(50);
78+
});
79+
});

src/api/BundleDeploy/BundleDeployer.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export class BundleDeployer {
3232
private hlqsValidated: boolean = false;
3333
private jobId: string;
3434
private progressBar: ITaskWithStatus;
35+
private useResponseProgressBar = true;
3536

3637
/**
3738
* Constructor for a BundleDeployer.
@@ -41,6 +42,9 @@ export class BundleDeployer {
4142
*/
4243
constructor(params: IHandlerParameters) {
4344
this.params = params;
45+
this.progressBar = { percentComplete: 0,
46+
stageName: TaskStage.NOT_STARTED,
47+
statusMessage: ""};
4448
}
4549

4650
/**
@@ -50,7 +54,7 @@ export class BundleDeployer {
5054
* @throws ImperativeError
5155
* @memberof BundleDeployer
5256
*/
53-
public async deployBundle(session?: AbstractSession): Promise<string> {
57+
public async deployBundle(session?: AbstractSession, task?: ITaskWithStatus): Promise<string> {
5458

5559
// Validate that the parms are valid for Deploy
5660
this.validateDeployParms();
@@ -67,7 +71,7 @@ export class BundleDeployer {
6771
const jcl = this.getDeployJCL();
6872

6973
// Submit it
70-
return this.submitJCL(jcl, "DEPLOY", session);
74+
return this.submitJCL(jcl, "DEPLOY", session, task);
7175
}
7276

7377
/**
@@ -77,7 +81,7 @@ export class BundleDeployer {
7781
* @throws ImperativeError
7882
* @memberof BundleDeployer
7983
*/
80-
public async undeployBundle(session?: AbstractSession): Promise<string> {
84+
public async undeployBundle(session?: AbstractSession, task?: ITaskWithStatus): Promise<string> {
8185

8286
// Validate that the parms are valid for Undeploy
8387
this.validateUndeployParms();
@@ -94,7 +98,7 @@ export class BundleDeployer {
9498
const jcl = this.getUndeployJCL();
9599

96100
// Submit it
97-
return this.submitJCL(jcl, "UNDEPLOY", session);
101+
return this.submitJCL(jcl, "UNDEPLOY", session, task);
98102
}
99103

100104
/**
@@ -257,7 +261,7 @@ export class BundleDeployer {
257261

258262
// Have a look at the status message for the progress bar, has it been updated with
259263
// the jobid yet? If so, parse it out and refresh the message.
260-
if (this.jobId === "UNKNOWN") {
264+
if (this.jobId === "UNKNOWN" && this.progressBar.statusMessage) {
261265
const statusWords = this.progressBar.statusMessage.split(" ");
262266
if (statusWords.length >= 2) {
263267
if (statusWords[2] !== undefined && statusWords[2].indexOf("JOB") === 0) {
@@ -286,11 +290,17 @@ export class BundleDeployer {
286290
}
287291
}
288292

289-
private async submitJCL(jcl: string, action: string, session: any): Promise<string> {
293+
private async submitJCL(jcl: string, action: string, session: any, task?: ITaskWithStatus): Promise<string> {
290294
let spoolOutput: any;
291-
this.progressBar = { percentComplete: TaskProgress.TEN_PERCENT,
292-
statusMessage: "Submitting DFHDPLOY JCL for the " + action + " action",
293-
stageName: TaskStage.IN_PROGRESS };
295+
if (task) {
296+
this.progressBar = task;
297+
this.useResponseProgressBar = false;
298+
}
299+
300+
this.progressBar.percentComplete = TaskProgress.TEN_PERCENT;
301+
this.progressBar.statusMessage = "Submitting DFHDPLOY JCL for the " + action + " action";
302+
this.progressBar.stageName = TaskStage.IN_PROGRESS;
303+
294304
this.startProgressBar();
295305
this.jobId = "UNKNOWN";
296306

@@ -386,13 +396,13 @@ export class BundleDeployer {
386396
}
387397

388398
private startProgressBar() {
389-
if (this.params.arguments.verbose !== true) {
399+
if (this.params.arguments.verbose !== true && this.useResponseProgressBar === true) {
390400
this.params.response.progress.startBar({task: this.progressBar});
391401
}
392402
}
393403

394404
private endProgressBar() {
395-
if (this.params.arguments.verbose !== true) {
405+
if (this.params.arguments.verbose !== true && this.useResponseProgressBar === true) {
396406
this.params.response.progress.endBar();
397407
}
398408
}

src/api/BundlePush/BundlePusher.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@
1111

1212
"use strict";
1313

14-
import { IHandlerParameters, AbstractSession, ITaskWithStatus, TaskStage, Logger } from "@zowe/imperative";
14+
import { IHandlerParameters, AbstractSession, ITaskWithStatus, TaskStage, Logger, TaskProgress } from "@zowe/imperative";
1515
import { List, ZosmfSession, SshSession, Shell, Upload, IUploadOptions, ZosFilesAttributes, Create } from "@zowe/cli";
1616
import { BundleDeployer } from "../BundleDeploy/BundleDeployer";
1717
import { Bundle } from "../BundleContent/Bundle";
18+
import { SubtaskWithStatus } from "./SubtaskWithStatus";
1819

1920

2021
/**
@@ -221,32 +222,23 @@ export class BundlePusher {
221222
}
222223

223224
private async undeployExistingBundle(zosMFSession: AbstractSession, bd: BundleDeployer) {
224-
// End the current progress bar so that UNDEPLOY can create its own
225225
this.updateStatus("Undeploying any existing bundle from CICS");
226-
this.endProgressBar();
227-
228226

229227
const targetstateLocal = this.params.arguments.targetstate;
230228
this.params.arguments.targetstate = "DISCARDED";
231-
await bd.undeployBundle(zosMFSession);
229+
const subtask = new SubtaskWithStatus(this.progressBar, TaskProgress.THIRTY_PERCENT);
230+
await bd.undeployBundle(zosMFSession, subtask);
232231
this.params.arguments.targetstate = targetstateLocal;
233232

234233
// Resume the current progress bar
235-
this.endProgressBar();
236234
this.updateStatus("Undeployed existing bundle from CICS");
237-
this.startProgressBar();
238235
}
239236

240237
private async deployBundle(zosMFSession: AbstractSession, bd: BundleDeployer) {
241-
// End the current progress bar so that DEPLOY can create its own
242238
this.updateStatus("Deploying the bundle to CICS");
243-
this.endProgressBar();
244-
245-
await bd.deployBundle(zosMFSession);
246-
// Resume the current progress bar
247-
this.endProgressBar();
239+
const subtask = new SubtaskWithStatus(this.progressBar, TaskProgress.THIRTY_PERCENT);
240+
await bd.deployBundle(zosMFSession, subtask);
248241
this.updateStatus("Deployed existing bundle to CICS");
249-
this.startProgressBar();
250242
}
251243

252244
private sshOutput(data: string) {
@@ -312,6 +304,7 @@ export class BundlePusher {
312304
// architected .profile within the USSCONFIG structure.
313305
const setNodehomeCmd = "export PATH=\"$PATH:/usr/lpp/IBM/cnj/IBM/node-latest-os390-s390x/bin\"";
314306
await this.runSshCommandInRemoteDirectory(sshSession, this.params.arguments.bundledir, setNodehomeCmd + " && npm install");
307+
this.updateStatus("npm install complete", TaskProgress.TWENTY_PERCENT);
315308
}
316309

317310
private async runSshCommandInRemoteDirectory(sshSession: SshSession, directory: string, sshCommand: string) {
@@ -357,6 +350,7 @@ export class BundlePusher {
357350

358351
const uploadOptions: IUploadOptions = { recursive: true };
359352
uploadOptions.attributes = this.findZosAttributes();
353+
uploadOptions.task = new SubtaskWithStatus(this.progressBar, TaskProgress.TEN_PERCENT);
360354

361355
try {
362356
await Upload.dirToUSSDirRecursive(zosMFSession, this.localDirectory, this.params.arguments.bundledir, uploadOptions);
@@ -392,9 +386,8 @@ export class BundlePusher {
392386
return new ZosFilesAttributes(Bundle.getTemplateZosAttributesFile());
393387
}
394388

395-
private updateStatus(status: string) {
396-
const PERCENT3 = 3;
397-
this.progressBar.percentComplete += PERCENT3;
389+
private updateStatus(status: string, percentageIncrease = 3) {
390+
this.progressBar.percentComplete += percentageIncrease;
398391
this.progressBar.statusMessage = status;
399392

400393
if (this.params.arguments.verbose) {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { ITaskWithStatus, TaskProgress, TaskStage, ImperativeExpect, ImperativeError} from "@zowe/imperative";
2+
3+
4+
export class SubtaskWithStatus {
5+
6+
private parent: ITaskWithStatus;
7+
private ticks: number;
8+
private percentCompleteInternal: number;
9+
10+
constructor(parent: ITaskWithStatus, ticks: number) {
11+
this.parent = parent;
12+
this.ticks = ticks;
13+
this.percentCompleteInternal = 0;
14+
if (this.ticks < 0 || this.ticks > TaskProgress.ONE_HUNDRED_PERCENT) {
15+
throw new ImperativeError({msg: "Ticks must be between 0 and 100"});
16+
}
17+
}
18+
19+
public set statusMessage(statusMessage: string) {
20+
this.parent.statusMessage = statusMessage;
21+
}
22+
23+
public set stageName(stageName: TaskStage) {
24+
if (stageName !== TaskStage.COMPLETE && stageName !== TaskStage.NOT_STARTED) {
25+
this.parent.stageName = stageName;
26+
}
27+
}
28+
29+
public set percentComplete(percentComplete: number) {
30+
const delta = percentComplete - this.percentCompleteInternal;
31+
this.percentCompleteInternal = percentComplete;
32+
this.parent.percentComplete = this.parent.percentComplete + delta * (this.ticks / TaskProgress.ONE_HUNDRED_PERCENT);
33+
}
34+
35+
public get percentComplete() {
36+
return this.percentCompleteInternal;
37+
}
38+
}

0 commit comments

Comments
 (0)