Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…backend into feat/assignTaskMiddleware
  • Loading branch information
vinayak-trivedi committed Sep 27, 2022
2 parents bcb2144 + 74cb191 commit 3dbe896
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 1 deletion.
3 changes: 2 additions & 1 deletion routes/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ const { createTask, updateTask, updateSelfTask } = require("../middlewares/valid
const authorizeRoles = require("../middlewares/authorizeRoles");
const { APPOWNER, SUPERUSER } = require("../constants/roles");
const assignTask = require("../middlewares/assignTask");
const cache = require("../utils/cache");

router.get("/", tasks.fetchTasks);
router.get("/", cache(), tasks.fetchTasks);
router.get("/self", authenticate, tasks.getSelfTasks);
router.get("/overdue", authenticate, authorizeRoles([SUPERUSER]), tasks.overdueTasks);
router.post("/", authenticate, authorizeRoles([APPOWNER, SUPERUSER]), createTask, tasks.addNewTask);
Expand Down
113 changes: 113 additions & 0 deletions utils/cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
const CACHE_EXPIRY_TIME_MIN = 2;
const CACHE_SIZE_MB = 10;
const minutesToMilliseconds = (minutes) => minutes * 60000;

/**
* Cache pool to get and store API responses
* @param {Object} [opt] options for cache pool
* @param {number} opt.maximumSize Maximum size of the cache pool in Megabytes (MB)
*/
const cachePool = (opt = { maximumSize: CACHE_SIZE_MB }) => {
const cacheStore = new Map();
let hits = 0;

/**
* Get an API Respnose from cacheStore.
* @param {string} key
* @returns {null | object}
*/
const get = (key) => {
const cachedData = cacheStore.get(key);

if (!cachedData) {
return null;
}

const isCacheDataExpired = new Date().getTime() > cachedData.expiry;

// If data is expired remove it from store, time to get a fresh copy.
if (isCacheDataExpired) {
cacheStore.delete(key);
return null;
}

hits += 1;
return cachedData.response;
};

/**
* Add API response to cacheStore.
* @param {string} key
* @param {Object} value Value to be stored inside the cache.
* @param {number} value.priority Priority of the api
* @param {number} value.expiry Expiry time of api
* @param {number} value.size Size of api response in byte
* @returns {number} : statusCode
*/
const set = async (key, value) => {
try {
cacheStore.set(key, value);
return true;
} catch (err) {
return false;
}
};

return { get, set, hits, cacheStore };
};

// Initialize cache pool.
const pool = cachePool();

/**
* Caching middleware for API resposnes.
* @param {Object} [data] Options to handle individual api cache.
* @param {number} data.priority The priority of api in cache store.
* @param {number} data.expiry Cache expiry time of api in minutes.
* @returns {function} middleware function to help cache api response.
*/
const cache = (data = { priority: 2, expiry: CACHE_EXPIRY_TIME_MIN }) => {
return async (req, res, next) => {
const key = "__cache__" + req.method + req.originalUrl;
const cacheData = pool.get(key);

if (cacheData) {
res.send(cacheData);
} else {
/**
* As we do not have data in our cache we call the next middleware,
* intercept the response being sent from middleware and store it in cache.
* */
const chunks = [];
const oldWrite = res.write;
const oldEnd = res.end;

res.write = (chunk, ...args) => {
chunks.push(chunk);
return oldWrite.apply(res, [chunk, ...args]);
};

res.end = (chunk, ...args) => {
if (chunk) {
chunks.push(chunk);
}

const apiResponse = Buffer.concat(chunks).toString();

const cacheValue = {
priority: data.priority,
response: apiResponse,
expiry: new Date().getTime() + minutesToMilliseconds(data.expiry),
size: Buffer.byteLength(apiResponse),
};

pool.set(key, cacheValue);
return oldEnd.apply(res, [chunk, ...args]);
};

next();
}
};
};

module.exports = cache;

0 comments on commit 3dbe896

Please sign in to comment.