Skip to content

Commit

Permalink
Merge pull request #48 from ganeshsacharya/stable
Browse files Browse the repository at this point in the history
Bugfixes & Enhancements
  • Loading branch information
ganeshsacharya authored Feb 26, 2024
2 parents 9ec2a9f + 0b6f5a4 commit b2b8df7
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 48 deletions.
99 changes: 68 additions & 31 deletions base/executor.class.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,26 @@ const ParameterProcessor = require('./parameterProcessor.class');
const { encrypt, decrypt } = require('../helper/encryption');
const { ENC_MODE, DEFAULT_LNG_KEY, ENC_ENABLED } = require('../helper/globalConstants');
const jwt = require('../helper/jwt');
const _ = require("lodash");
const multiReplace = require('string-multiple-replace');

class executor {
constructor() {
this.responseData = {};
}

async executeRequest(request) {

try {
this.setResponse('UNKNOWN_ERROR');


// Property finding factory
const findPropInRequest = baseHelper.deepFindPropMaker(request)

// Find the basic variables from the incoming request
// Initializng basic variables
const { lng_key: lngKey, access_token: accessToken, enc_state: encState } = request.headers;
const lngKey = findPropInRequest("lng_key")
const encState = findPropInRequest("enc_state")
const accessToken = findPropInRequest("access_token")

// Decide encryption mode. And enforce enc_state to be true if encryption is Strict
const { ENCRYPTION_MODE } = JSON.parse(process.env.ENCRYPTION);
Expand All @@ -32,9 +39,9 @@ class executor {
throw new Error();
}
const encryptionState = (ENCRYPTION_MODE == ENC_MODE.STRICT || (ENCRYPTION_MODE == ENC_MODE.OPTIONAL && encState == ENC_ENABLED));

// Set member variables
this.setMemberVariable('encryptionState', encryptionState);
this.setMemberVariable('encryptionState', this.encryptionState);
if (lngKey) this.setMemberVariable('lng_key', lngKey);

// Finalize methodName including custom route
Expand Down Expand Up @@ -80,14 +87,22 @@ class executor {
actionInstance.setMemberVariable('userObj', data);
}

// Parse the incoming request object and find if the metadata configs are present
// if present, group them under metadata key and make it action instance member
const metaConfig = process.env["METADATA"];
if(metaConfig && baseHelper.isJSON(metaConfig)) {
const metadata = baseHelper.populateMetadata(request, JSON.parse(metaConfig));
actionInstance.setMemberVariable('metadata', metadata);
}

// validate & process request parameters
const parameterProcessor = new ParameterProcessor();
const params = initInstance.getParameter();
const isFileExpected = baseHelper.isFileExpected(params);
let requestData = baseHelper.parseRequestData(request, isFileExpected);

// If encyption is enabled, then decrypt the request data
if (!isFileExpected && encryptionState) {
if (!isFileExpected &&this.encryptionState) {
requestData = decrypt(requestData.data);
if (typeof requestData === 'string')
requestData = JSON.parse(requestData);
Expand All @@ -111,28 +126,21 @@ class executor {
// Initiate and Execute method
this.responseData = await actionInstance.executeMethod();
const { responseString, responseOptions, packageName } = actionInstance.getResponseString();
const { responseCode, responseMessage } = this.getResponse(responseString, responseOptions, packageName);


// If encryption mode is enabled then encrypt the response data
if (encryptionState) {
// this.responseData = new URLSearchParams({data: encrypt(this.responseData)}).toString().replace("data=",'');
this.responseData = encrypt(this.responseData);
}

return {
responseCode,
responseMessage,
responseData: this.responseData
};
const response = this.getResponse(responseString, responseOptions, packageName);
return response;

} catch (e) {
console.log("Exception caught", e);
const { responseCode, responseMessage } = this.getResponse();
const response = this.getResponse(e === "NODE_VERSION_ERROR" ? e : "");
if (process.env.MODE == "DEV" && e.message) this.setDebugMessage(e.message);
return {
responseCode,
responseMessage,
responseData: {}
};
return response;
}
}

Expand Down Expand Up @@ -192,12 +200,14 @@ class executor {
const BASE_RESPONSE = require(path.resolve(process.cwd(), `src/global/i18n/response.js`)).RESPONSE;
const PROJECT_RESPONSE = require(`../i18n/response.js`).RESPONSE;

const CUSTOM_RESPONSE_TEMPLATE = require(path.resolve(process.cwd(), `src/config/responseTemplate.json`));

let RESP = { ...PROJECT_RESPONSE, ...BASE_RESPONSE };

if (packageName) {
try {
let packageVals = packageName.split('/');
const PACKAGE_RESPONSE = require(path.resolve(process.cwd(), `njs2_modules/${[...packageVals.slice(0, packageVals.length - 1)].join('/')}/contract/response.json`));
const PACKAGE_RESPONSE = require(path.resolve(process.cwd(), `node_modules/${[...packageVals.slice(0, packageVals.length - 1)].join('/')}/contract/response.json`));
RESP = { ...RESP, ...PACKAGE_RESPONSE };
} catch {
}
Expand All @@ -206,25 +216,52 @@ class executor {
if (!RESP[this.responseString]) {
RESP = RESP["RESPONSE_CODE_NOT_FOUND"];
} else {
RESP = RESP[this.responseString];
RESP = {...RESP[this.responseString]};
}

this.responseCode = RESP.responseCode;
this.responseMessage = this.lng_key && RESP.responseMessage[this.lng_key]
RESP.responseMessage = this.lng_key && RESP.responseMessage[this.lng_key]
? RESP.responseMessage[this.lng_key]
: RESP.responseMessage[DEFAULT_LNG_KEY];

if (this.responseOptions)
Object.keys(this.responseOptions).map(keyName => {
this.responseMessage = this.responseMessage.replace(keyName, this.responseOptions[keyName]);
RESP.responseData = this.responseData;

if(this.responseOptions)
Object.keys(this.responseOptions).map(keyName => {
RESP.responseMessage = RESP.responseMessage.replace(keyName, this.responseOptions[keyName]);
});

return this.parseResponseData(CUSTOM_RESPONSE_TEMPLATE,RESP);

}

parseResponseData(CUSTOM_RESPONSE_TEMPLATE,RESP){
try{
Object.entries(RESP).forEach(array => {
const [key,value] = array;
if(typeof value === 'object'){
RESP[key] = "{" + JSON.stringify(value) + "}";
}
});

const compiled = _.template(typeof CUSTOM_RESPONSE_TEMPLATE === 'string' ? CUSTOM_RESPONSE_TEMPLATE : JSON.stringify(CUSTOM_RESPONSE_TEMPLATE));

const resultTemplate = compiled(RESP);

const matcherObj = {
'"{{': '{',
'}}"': '}',
'"{[': '[',
']}"': ']'
}

const replacedString = multiReplace(resultTemplate, matcherObj);

return {
responseCode: this.responseCode,
responseMessage: this.responseMessage,
responseData: this.responseData
};
return typeof CUSTOM_RESPONSE_TEMPLATE === 'string' ? replacedString : JSON.parse(replacedString);
}catch(error){
throw new Error("parseResponseData Error:"+error);
}
}

}

module.exports = executor;
45 changes: 45 additions & 0 deletions helper/baseHelper.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,51 @@ class baseHelper {

return requestData ? requestData : {};
}

static deepFindPropMaker(obj) {
return (prop) => {
if (!obj || typeof obj !== 'object') return null;
if (obj.hasOwnProperty(prop) && obj[prop] !== null && obj[prop] !== undefined) {
return obj[prop];
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
let found = this.deepFindPropMaker(obj[key])(prop);
if (found) return found;
}
}
return null;
}
}

static populateMetadata(request, configs) {

if(configs.length == 0) return {}

const deepFindPropInRequestObject = this.deepFindPropMaker(request)

const keys = configs.map(key => key.trim());

let resultObj = {};

for (const key of keys) {
const value = deepFindPropInRequestObject(key);
if (value !== undefined) {
resultObj[key] = value;
}
}
return resultObj
}

static isJSON(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}

}

module.exports = baseHelper;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"node-cron": "^3.0.2",
"query-string": "^7.0.1",
"require-dir": "^1.2.0",
"serverless-http": "^3.2.0",
"socket.io": "^4.1.2"
},
"njs2-type": "base",
Expand Down
76 changes: 60 additions & 16 deletions template/frameworkStructure/lambda.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,70 @@
const serverless = require('./serverless');
const serverlessExpress = require('serverless-http')
const express = require('express')
const multer = require('multer');
const app = express()
const upload = multer();

const websockets = require('./websockets');
//const init = require('./src/library/roomHandler/init'); // Make sure to create this file and add defualt content.
const { Executor } = require("@njs2/base");

module.exports.handler = async (event) => {
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(upload.any());


app.all('*', async function (req, res) {
const {event} = req.apiGateway
let result = {}
try {
const requestType = event.stageVariables.requestType;
if (requestType === 'API') {
return await serverless.execute(event);
result = await lambdaExecutor(event)
} else if (requestType === 'Socket') {
return await websockets.handler(event);
} else if (requestType === 'mCron') {
// get the taskName
result = await websockets.handler(event);
} else if (requestType === 'scheduler') {
const taskName = event.stageVariables.taskName;
// require the file by taskName
const mCron = require(`./src/tasks/${taskName}.task.js`);
// call default of taskName
mCron();
} else if (requestType === 'cron') {
const cron = require(`./cron`);
cron();
const task = require(`./src/tasks/${taskName}.task.js`);
task();
}
res.send({...result})
} catch(error) {
console.error(error)
res.send({
responseCode: 100011,
responseMessage: "Something went wrong!",
responseData: {}
})
}
})

exports.handler = serverlessExpress(app)

async function lambdaExecutor(eventObject) {
try {
const { httpMethod, queryStringParameters, path, files, body, headers } = eventObject
// Neutralize input parameter received from express for Executor.executeRequest
let executorReq = {};
executorReq.httpMethod = httpMethod;
executorReq.queryStringParameters = queryStringParameters;
executorReq.body = body;
executorReq.pathParameters = {
proxy: path.length ? path.slice(1) : path
};
executorReq.headers = headers;
if (files && files.length) {
if (files.length > 1) throw new Error("Only one file upload at a time is allowed")
files.forEach(file => {
executorReq.body[file.fieldname] = {
type: 'file',
filename: file.originalname,
contentType: file.mimetype,
content: file.buffer
};
});
}
const executor = new Executor();
return await executor.executeRequest(executorReq);
} catch (error) {
console.error(error);
throw error
}
};
}
2 changes: 1 addition & 1 deletion template/frameworkStructure/serverless.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports.execute = async (event) => {
let fileCount = 0;
if (event.headers["Content-Type"] === "application/x-www-form-urlencoded") {
const querystring = require("querystring");
event.body = querystring.parse(event.body);
event.body = Object.assign({}, querystring.parse(event.body));
}

if (
Expand Down
1 change: 1 addition & 0 deletions template/frameworkStructure/src/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"END_POINT": "",
"LAMBDA_FUNCTION_NAME": ""
},
"METADATA": [],
"AUTH": {
"AUTH_MODE": "JWT",
"JWT_SECRET": "123",
Expand Down
5 changes: 5 additions & 0 deletions template/frameworkStructure/src/config/responseTemplate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"responseCode":"<%=responseCode%>",
"responseMessage":"<%=responseMessage%>",
"responseData":"<%=responseData%>"
}

0 comments on commit b2b8df7

Please sign in to comment.