@zowe/imperative
Version:
framework for building configurable CLIs
269 lines • 11.3 kB
JavaScript
"use strict";
/*
* 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.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Operations = void 0;
/* eslint-disable deprecation/deprecation */
const Operation_1 = require("./Operation");
const utilities_1 = require("../../utilities");
const TaskProgress_1 = require("./TaskProgress");
/**
* @deprecated
* The Operations class extends Operation and is used to create a 'string' of operations that must
* be completed in serial order.
*
* The implementing class is responsible (in defineOperations) for creating each operation (and
* invoking addNextOperation) to add the operations to the list. You must also call "defineOperations"
* from your constructor. This allows you to setup anything you need (in your constructor) before defining
* the list of operations.
*
* You can initiate the operation by invoking performOperation, which will iterate through each operation
* defined and perform them in sequence. If a operation chooses to diverge, then
*/
class Operations extends Operation_1.Operation {
/**
* Building an Operations object
* @param {string} opName: Operations Name
* @param {boolean} critical: if the operation is critical
*/
constructor(opName, critical) {
super(opName, critical);
/**
* The list of operations that should be executed in sequence
*/
this.mOperationList = [];
/**
* The current operation that is being executed.
* @type {number}
*/
this.mCurrentOperation = 0;
/**
* In the event of an undo request, we will prepare a list that will be traversed to complete
* each undo function.
* @type {Array}
*/
this.mOperationUndoList = [];
/**
* The current operation undo index.
* @type {number}
*/
this.mOperationUndoIndex = 0;
}
/**
* The public interface to Operations. This method allows consumers to initiate the operation sequence.
* @returns {IOperationResult}: The operation results
*/
executeOperation(inputParameters, operationComplete) {
/**
* Execute the next operation in the list.
*/
this.mInputParameters = inputParameters;
this.mOperationsCompleteCallback = operationComplete;
this.executeNextOperation(inputParameters);
}
/**
* Set the status message for the overall Operations object,
* which takes precedence over the sub-operations' messages
* @param message - the message, including any templates you want replaced like %s or "{{myObject}}"
* @param args - variable args as allowed by printf-like syntax or {myObject: this.myObject}
* @returns {string} the final translated and formatted string (in case you want to log it etc.)
*/
setStatusMessage(message, ...args) {
if (args) {
message = utilities_1.TextUtils.formatMessage(message, ...args);
}
this.mOverallStatusMessage = message;
return message;
}
get statusMessage() {
if (this.mOverallStatusMessage == null) {
return this.mOperationList[this.mCurrentOperation].statusMessage;
}
else {
return this.mOverallStatusMessage;
}
}
/**
* What percent complete is the Operations as a whole?s
* @returns {number} percentComplete weighted against how many operations are complete
*/
get percentComplete() {
const percentPerOp = TaskProgress_1.TaskProgress.ONE_HUNDRED_PERCENT / this.mOperationList.length;
const currentOpPercentComplete = this.mOperationList[this.mCurrentOperation].percentComplete == null
? 0 : this.mOperationList[this.mCurrentOperation].percentComplete;
return Math.ceil(percentPerOp * this.mCurrentOperation + // how many operations completed so far (each 100%)
percentPerOp * (currentOpPercentComplete / TaskProgress_1.TaskProgress.ONE_HUNDRED_PERCENT));
// what is the percent complete of the current operation in the list? weight that against number of ops
}
/**
* Log that the set of operations have completed.
*/
logOperationResults() {
this.log.info("The set of operations have completed.");
}
/**
* Count the total number of base operation objects
* @returns {number}
*/
get totalOperations() {
let count = 0;
for (const subOp of this.mOperationList) {
count += subOp.totalOperations;
}
return count;
}
/**
* Add a new operation to the list of operations to be executed.
*
* @param {Operation} newOperation: Operation to be added to the list of sequential operations
*/
addNextOperation(newOperation) {
this.mOperationList.push(newOperation);
}
/**
* This is a placeholder and is never directly called.
*/
execute() {
// this.executeOperation();
}
/**
* Perform all undos if the undo function is called for an Operations
*/
undo() {
this.executeNextUndo();
}
/**
* Log that this is a set of operations that are about to begin.
*/
logOperationBeginMessages() {
this.log.info("This is a set of operations.");
}
/**
* Callback invoked when the operation completes. Passed the result object of the operation.
*
* @param {IOperationResult} operationResult: The result of the operation
* @param {any} output: The output from the operation
*/
operationCompleted(output, operationResult) {
this.log.debug("Operation in list completed: " + operationResult.operationName);
this.addResult(operationResult);
/**
* Check if the current operations path should continue, reasons for discontinuing may be that
* a failure occurred, or a path diverged and we do not need to continue the operations here.
*/
if (!operationResult.continuePath) {
this.log.debug("Operation (" + operationResult.operationName + ") chose to quit current path");
/**
* If an operation failed, then we must check if other completed operations must be undone
* If that is the case, we must process the completed operations in reverse order starting
* with the operation prior to the operation that just 'completed'. That operation will
* have already undone itself in the execute method.
*/
if (operationResult.operationFailed) {
/**
* Indicate that the overall list of operations failed, this will drive (if present) the parents
* operations undo list.
*/
this.mOperationResult.continuePath = false;
this.setOperationFailed();
this.prepareForUndo();
}
else {
this.mOperationsCompleteCallback(output, this.mOperationResults);
}
}
else {
/**
* Increment the current operation counter and invoke the next operation in the list.
*/
this.mCurrentOperation++;
this.executeNextOperation(output);
}
}
/**
* If more operations are available, execute the next operation in the list
* @param {any} input: The output from the previous operation
*/
executeNextOperation(input) {
/**
* Invoke the current operation and specify the callback method.
*/
this.log.debug("Current operation index: " + this.mCurrentOperation +
" out of: " + this.mOperationList.length);
if (this.mCurrentOperation >= this.mOperationList.length) {
this.log.info("All operations in the list have completed.");
this.mOperationsCompleteCallback(input, this.mOperationResults);
}
else {
// TODO - why did I do this? I have no idea...
// /**
// * Execute the next operation in the list and pass the output of the previous operation to the
// * next operation as input OR the input to the string of operations
// */
// let inputParameters: T = this.mInputParameters;
// if (this.mCurrentOperation > 0) {
// inputParameters = this.mOperationList[this.mCurrentOperation - 1].operationResult.output;
// }
this.mOperationList[this.mCurrentOperation].executeOperation(input, this.operationCompleted.bind(this));
}
}
/**
* Prepare a list of operations to 'undo', we will traverse the list calling the undo function of
* each operation. Only operations that are marked as 'undoa-able' will be called.
*
* TODO - we could probably fire off all undo functions here async, but for now they are serial
*/
prepareForUndo() {
this.log.debug("Building list of undo operation actions");
let currentOperationResult = this.mOperationResults;
while (!(currentOperationResult == null)) {
if (currentOperationResult.operationUndoPossible && !currentOperationResult.operationUndoAttempted) {
this.log.debug("Adding operation (" + currentOperationResult.operationName +
") to undo list.");
this.mOperationUndoList.push(currentOperationResult.operationObject);
this.mOperationUndoIndex++;
}
currentOperationResult = currentOperationResult.nextOperationResult;
}
if (this.mOperationUndoIndex > 0) {
this.log.debug("There are operations to undo, invoking undo functions now.");
this.executeNextUndo();
}
else {
this.mOperationsCompleteCallback(Operation_1.Operation.NO_OUTPUT, this.mOperationResults);
}
}
/**
* Execute the next undo in the list. If the undo index is 0 when this function is called, then we can
* invoke the callers callback as all undos are complete.
*/
executeNextUndo() {
this.log.debug("Execute next undo entered. Current index: " + this.mOperationUndoIndex);
if (this.mOperationUndoIndex === 0) {
this.log.debug("Invoke the callers operation complete callback.");
this.mOperationsCompleteCallback(null, this.mOperationResults);
}
else {
this.mOperationUndoIndex--;
this.log.debug("Decrementing undo index and performing next undo. Index: "
+ this.mOperationUndoIndex);
this.mOperationUndoList[this.mOperationUndoIndex].performUndo(this.undoOpComplete.bind(this));
}
}
/**
* Undo operation completed callback. Probably unnecessary.
*/
undoOpComplete() {
this.executeNextUndo();
}
}
exports.Operations = Operations;
//# sourceMappingURL=Operations.js.map