Skip to content

Commit

Permalink
MWPW-135527 Move common validation and error handler in Floodgate AIO (
Browse files Browse the repository at this point in the history
…#56)

* MWPW-135527 Move common validation and error handler in Floodgate AIO actions

Moved the common validation to FgAction and actions updated to refer to FgAction

* Minor update

* Updated for review comment

---------

Co-authored-by: Raghu A <[email protected]>
  • Loading branch information
raga-adbe-gh and Raghu A authored Sep 14, 2023
1 parent 3d6758a commit f9c4c83
Show file tree
Hide file tree
Showing 10 changed files with 585 additions and 436 deletions.
206 changes: 206 additions & 0 deletions actions/FgAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/* ***********************************************************************
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 2023 Adobe
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe and its suppliers, if any. The intellectual
* and technical concepts contained herein are proprietary to Adobe
* and its suppliers and are protected by all applicable intellectual
* property laws, including trade secret and copyright laws.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe.
************************************************************************* */
const openwhisk = require('openwhisk');
const { getAioLogger, actInProgress } = require('./utils');
const appConfig = require('./appConfig');
const FgUser = require('./fgUser');
const FgStatus = require('./fgStatus');

const FG_PROOCESS_ACTION = 'fgProcessAction';
const logger = getAioLogger();
const BAD_REQUEST_SC = 400;
const AUTH_FAILED_SC = 401;
const GEN_ERROR_SC = 500;
const ALL_OK_SC = 200;

/**
* The common parameter validation, user check,
*/
class FgAction {
constructor(action, params) {
this.action = action || FG_PROOCESS_ACTION;
appConfig.setAppConfig(params);
this.spToken = params.spToken;
// Defaults
this.fgUser = null;
}

init({ fgStatusParams, skipUserDetails = false, ow }) {
const statsParams = { action: this.action, ...fgStatusParams };
if (!skipUserDetails) {
this.fgUser = new FgUser({ at: this.spToken });
statsParams.userDetails = this.fgUser.getUserDetails();
}
this.fgStatus = new FgStatus(statsParams);
this.ow = ow || openwhisk();
}

getActionParams() {
return {
action: this.action,
appConfig,
fgStatus: this.fgStatus,
fgUser: this.fgUser
};
}

/**
* Validates parameters for storing statues
* @param {*} statParams - These are parameters those are must for action to start tracking.
* @returns Response validation status like { ok: <status as true/false>, details: <status message> }
*/
async validateStatusParams(statParams = []) {
const resp = { ok: false, message: 'Status Params Validation' };
logger.debug(resp.message);
const conf = appConfig.getPayload();
const valFailed = statParams.find((p) => !conf[p]) !== undefined;
if (valFailed) {
resp.message = 'Could not determine the project path. Try reloading the page and trigger the action again.';
return resp;
}
resp.ok = true;
return resp;
}

/**
* Parameter validators for action
* @param {*} Options with
* reqParams - These are parameter which are required for the action to function.
* @returns Response validation status like { ok: <status as true/false>, details: <status message> }
*/
async validateParams(reqParams = []) {
const resp = { ok: false, message: 'Params Validation.' };
logger.debug(resp.message);
let stepMsg;
const conf = appConfig.getPayload();
const valFailed = reqParams.find((p) => !conf[p]) !== undefined;
if (valFailed) {
stepMsg = `Required data is not available to proceed with FG ${this.action}.`;
resp.message = stepMsg;
await this.fgStatus?.updateStatusToStateLib({
status: FgStatus.PROJECT_STATUS.FAILED,
statusMessage: stepMsg
});
return resp;
}
resp.ok = true;
return resp;
}

/**
* User validations for action
*/
async validateUser() {
const resp = { ok: false, message: 'User Validation' };
logger.debug(resp.message);
let stepMsg;
const storeValue = await this.fgStatus.getStatusFromStateLib();
if (this.fgUser && !await this.fgUser.isUser()) {
stepMsg = 'Unauthorized Access! Please contact Floodgate Administrators.';
storeValue.action.status = FgStatus.PROJECT_STATUS.FAILED;
storeValue.action.message = stepMsg;
resp.details = storeValue;
return resp;
}
resp.ok = true;
return resp;
}

/**
* Check if the action is in progress.
* @param {*} options with checkActivation flag if the flag is set then activation
* is check is skipped
* @returns object with ok true
*/
async actionInProgressCheck({ checkActivation = false }) {
const resp = { ok: false, message: 'Action In Progress' };
logger.debug(resp.message);
let stepMsg;
const storeValue = await this.fgStatus.getStatusFromStateLib();
const svStatus = storeValue?.action?.status;
const actId = storeValue?.action?.activationId;
const fgInProg = FgStatus.isInProgress(svStatus);

if (!appConfig.getSkipInProgressCheck() && fgInProg) {
if (!checkActivation || await actInProgress(this.ow, actId, FgStatus.isInProgress(svStatus))) {
stepMsg = `A ${this.action} project with activationid: ${storeValue?.action?.activationId} is already in progress.
Not triggering this action. And the previous action can be retrieved by refreshing the console page`;
storeValue.action.status = FgStatus.PROJECT_STATUS.FAILED;
storeValue.action.message = stepMsg;
resp.message = stepMsg;
resp.details = storeValue;
return resp;
}
}
resp.ok = true;
return resp;
}

/**
* Validation for action for params/user/action
* @param {*} opts options for validation
* Returns null if no error else the payload is returned
*/
async validateAction({
statParams,
actParams,
checkUser = false,
checkStatus = false,
checkActivation = false
}) {
const OKVAL = { ok: true };
let vStat = statParams ? await this.validateStatusParams(statParams) : OKVAL;
if (!vStat.ok) {
return {
code: BAD_REQUEST_SC,
payload: vStat.message,
};
}

vStat = actParams ? await this.validateParams(actParams) : OKVAL;
if (!vStat.ok) {
return {
code: BAD_REQUEST_SC,
payload: vStat.message,
};
}

vStat = checkUser ? await this.validateUser() : OKVAL;
if (!vStat.ok) {
return {
code: AUTH_FAILED_SC,
payload: vStat.details,
};
}

vStat = checkStatus ? await this.actionInProgressCheck({ checkActivation }) : OKVAL;
if (!vStat.ok) {
return {
code: GEN_ERROR_SC,
payload: vStat.details,
};
}

return { code: ALL_OK_SC };
}

logStart() {
logger.info(`${this.action} action for ${this.fgStatus?.getStoreKey()} triggered by ${JSON.stringify(this.fgUser?.getUserDetails() || 'FG')}`);
}
}

module.exports = FgAction;
127 changes: 52 additions & 75 deletions actions/copy/copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,93 +18,70 @@
// eslint-disable-next-line import/no-extraneous-dependencies
const openwhisk = require('openwhisk');
const {
getAioLogger, actInProgress, COPY_ACTION
getAioLogger, COPY_ACTION
} = require('../utils');
const appConfig = require('../appConfig');
const sharepointAuth = require('../sharepointAuth');
const FgStatus = require('../fgStatus');
const FgUser = require('../fgUser');
const FgAction = require('../FgAction');

// This returns the activation ID of the action that it called
async function main(args) {
const logger = getAioLogger();
let payload;
const {
spToken, adminPageUri, projectExcelPath, rootFolder
} = args;
appConfig.setAppConfig(args);
const userDetails = sharepointAuth.getUserDetails(spToken);
const fgStatus = new FgStatus({ action: COPY_ACTION, userDetails });
logger.info(`Copy action for ${fgStatus.getStoreKey()} triggered by ${JSON.stringify(userDetails)}`);
let respPayload;
const valParams = {
statParams: ['rootFolder', 'projectExcelPath'],
actParams: ['adminPageUri'],
checkUser: true,
checkStatus: true,
checkActivation: true
};
const ow = openwhisk();
// Initialize action
const fgAction = new FgAction(COPY_ACTION, args);
fgAction.init({ ow });
const { fgStatus } = fgAction.getActionParams();

try {
if (!rootFolder || !projectExcelPath) {
payload = 'Could not determine the project path. Try reloading the page and trigger the action again.';
logger.error(payload);
} else if (!adminPageUri) {
payload = 'Required data is not available to proceed with FG Copy action.';
logger.error(payload);
payload = await fgStatus.updateStatusToStateLib({
// Validations
const vStat = await fgAction.validateAction(valParams);
if (vStat && vStat.code !== 200) {
return vStat;
}

fgAction.logStart();

respPayload = await fgStatus.updateStatusToStateLib({
status: FgStatus.PROJECT_STATUS.STARTED,
statusMessage: 'Triggering copy action'
});
return ow.actions.invoke({
name: 'milo-fg/copy-worker',
blocking: false, // this is the flag that instructs to execute the worker asynchronous
result: false,
params: args
}).then(async (result) => {
logger.info(result);
// attaching activation id to the status
respPayload = await fgStatus.updateStatusToStateLib({
status: FgStatus.PROJECT_STATUS.IN_PROGRESS,
activationId: result.activationId
});
return {
code: 200,
payload: respPayload
};
}).catch(async (err) => {
respPayload = await fgStatus.updateStatusToStateLib({
status: FgStatus.PROJECT_STATUS.FAILED,
statusMessage: payload
statusMessage: `Failed to invoke actions ${err.message}`
});
} else {
const ow = openwhisk();
const storeValue = await fgStatus.getStatusFromStateLib();
const actId = storeValue?.action?.activationId;
const svStatus = storeValue?.action?.status;
const fgUser = new FgUser({ at: args.spToken });
if (!await fgUser.isUser()) {
payload = 'Unauthorized Access! Please contact Floodgate Administrators.';
storeValue.action.status = FgStatus.PROJECT_STATUS.FAILED;
storeValue.action.message = payload;
payload = storeValue;
} else if (!appConfig.getSkipInProgressCheck() &&
await actInProgress(ow, actId, FgStatus.isInProgress(svStatus))) {
payload = `A copy action project with activationid: ${storeValue?.action?.activationId} is already in progress.
Not triggering this action. And the previous action can be retrieved by refreshing the console page`;
storeValue.action.status = FgStatus.PROJECT_STATUS.FAILED;
storeValue.action.message = payload;
payload = storeValue;
} else {
payload = await fgStatus.updateStatusToStateLib({
status: FgStatus.PROJECT_STATUS.STARTED,
statusMessage: 'Triggering copy action'
});
return ow.actions.invoke({
name: 'milo-fg/copy-worker',
blocking: false, // this is the flag that instructs to execute the worker asynchronous
result: false,
params: args
}).then(async (result) => {
logger.info(result);
// attaching activation id to the status
payload = await fgStatus.updateStatusToStateLib({
status: FgStatus.PROJECT_STATUS.IN_PROGRESS,
activationId: result.activationId
});
return {
code: 200,
payload
};
}).catch(async (err) => {
payload = await fgStatus.updateStatusToStateLib({
status: FgStatus.PROJECT_STATUS.FAILED,
statusMessage: `Failed to invoke actions ${err.message}`
});
logger.error('Failed to invoke actions', err);
return {
code: 500,
payload
};
});
}
logger.error('Failed to invoke actions', err);
return {
code: 500,
payload
payload: respPayload
};
}
});
} catch (err) {
payload = fgStatus.updateStatusToStateLib({
respPayload = fgStatus.updateStatusToStateLib({
status: FgStatus.PROJECT_STATUS.FAILED,
statusMessage: `Failed to invoke actions ${err.message}`
});
Expand All @@ -113,7 +90,7 @@ async function main(args) {

return {
code: 500,
payload,
payload: respPayload,
};
}

Expand Down
Loading

0 comments on commit f9c4c83

Please sign in to comment.