Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MWPW-135527 Move common validation and error handler in Floodgate AIO #56

Merged
merged 3 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading