diff --git a/src/.env.sample b/src/.env.sample index 0ea611ac6..8054ca5d3 100644 --- a/src/.env.sample +++ b/src/.env.sample @@ -95,4 +95,10 @@ ENABLE_LOG = true # Api doc url -API_DOC_URL = '/api-doc' \ No newline at end of file +API_DOC_URL = '/api-doc' + +#Internal cache expiry time +INTERNAL_CACHE_EXP_TIME = 86400 + +#Redis Cache expiry Time +REDIS_CACHE_EXP_TIME = 86400 \ No newline at end of file diff --git a/src/app.js b/src/app.js index 906b12bdd..9263e000f 100644 --- a/src/app.js +++ b/src/app.js @@ -13,7 +13,6 @@ const path = require('path') const i18next = require('i18next') const Backend = require('i18next-fs-backend') const middleware = require('i18next-http-middleware') - let environmentData = require('./envVariables')() if (!environmentData.success) { diff --git a/src/configs/cache.js b/src/configs/cache.js new file mode 100644 index 000000000..0d44b336b --- /dev/null +++ b/src/configs/cache.js @@ -0,0 +1,6 @@ +const { RedisConfig, InternalCache, RedisHelper } = require('elevate-node-cache') +module.exports = () => { + RedisConfig.config(process.env.REDIS_HOST) + InternalCache.init(process.env.INTERNAL_CACHE_EXP_TIME) + RedisHelper.init(process.env.REDIS_CACHE_EXP_TIME) +} diff --git a/src/configs/index.js b/src/configs/index.js index 07705c832..8282d4f07 100644 --- a/src/configs/index.js +++ b/src/configs/index.js @@ -8,3 +8,5 @@ require('./mongodb')() require('./kafka')() + +require('./cache')() diff --git a/src/constants/common.js b/src/constants/common.js index 38febd6b2..2020af4e4 100644 --- a/src/constants/common.js +++ b/src/constants/common.js @@ -5,13 +5,23 @@ * Description : All commonly used constants through out the service */ -const successResponse = ({ statusCode = 500, responseCode = 'OK', message, result = [], meta = {} }) => { +const FormsData = require('@db/forms/queries') +const utils = require('@generics/utils') +const successResponse = async ({ statusCode = 500, responseCode = 'OK', message, result = [], meta = {} }) => { + const formVersionData = (await utils.internalGet('formVersion')) || false + let versions = {} + if (formVersionData) { + versions = formVersionData + } else { + versions = await FormsData.findAllTypeFormVersion() + await utils.internalSet('formVersion', versions) + } return { statusCode, responseCode, message, result, - meta, + meta: { ...meta, formsVersion: versions }, } } @@ -43,4 +53,6 @@ module.exports = { PUBLISHED_STATUS: 'published', LIVE_STATUS: 'live', MENTOR_EVALUATING: 'mentor', + internalCacheExpirationTime: process.env.INTERNAL_CACHE_EXP_TIME, // In Seconds + RedisCacheExpiryTime: process.env.REDIS_CACHE_EXP_TIME, } diff --git a/src/db/entities/query.js b/src/db/entities/query.js index 8729e9c54..21e819144 100644 --- a/src/db/entities/query.js +++ b/src/db/entities/query.js @@ -92,4 +92,15 @@ module.exports = class UserEntityData { } }) } + + static async findOne(_id) { + try { + const filter = { + _id: ObjectId(_id), + } + return await Entities.findOne(filter) + } catch (error) { + return error + } + } } diff --git a/src/db/forms/model.js b/src/db/forms/model.js index e7e244f32..76ee0fa31 100644 --- a/src/db/forms/model.js +++ b/src/db/forms/model.js @@ -11,6 +11,7 @@ const Schema = mongoose.Schema const formSchema = new Schema({ type: { type: String, + index: { unique: true }, required: true, }, subType: { diff --git a/src/db/forms/queries.js b/src/db/forms/queries.js index 27e19decb..c53731026 100644 --- a/src/db/forms/queries.js +++ b/src/db/forms/queries.js @@ -5,6 +5,7 @@ * Description : Users database operations */ +const utils = require('@generics/utils') const Forms = require('./model') module.exports = class FormsData { @@ -19,8 +20,8 @@ module.exports = class FormsData { }) } - static findOneForm(type, subType, action, ver, templateName) { - const filter = { type, subType, action, ver, 'data.templateName': templateName } + static findOneForm(type) { + const filter = { type } const projection = {} return new Promise(async (resolve, reject) => { try { @@ -32,12 +33,30 @@ module.exports = class FormsData { }) } + static findAllTypeFormVersion() { + const projection = { + type: 1, + ver: 1, + } + return new Promise(async (resolve, reject) => { + try { + const formData = await Forms.find({}, projection) + let versions = {} + formData.forEach((version) => { + versions[version.type] = version.ver + }) + resolve(versions) + } catch (error) { + reject(error) + } + }) + } + static updateOneForm(update, options = {}) { const filter = { type: update.type, subType: update.subType, action: update.action, - ver: update.ver, 'data.templateName': update.data.templateName, } return new Promise(async (resolve, reject) => { @@ -58,4 +77,15 @@ module.exports = class FormsData { } }) } + + static async checkVersion(bodyData) { + try { + const filter = { type: bodyData.type } + const projection = { type: 1, ver: 1 } + const formData = await Forms.findOne(filter, projection) + return utils.compareVersion(formData.ver, bodyData.ver) + } catch (err) { + return err + } + } } diff --git a/src/envVariables.js b/src/envVariables.js index c9520946c..eb3652762 100644 --- a/src/envVariables.js +++ b/src/envVariables.js @@ -119,6 +119,14 @@ let enviromentVariables = { message: 'Required kafka topic', optional: false, }, + INTERNAL_CACHE_EXP_TIME: { + message: 'Internal Cache Expiry Time', + optional: false, + }, + REDIS_CACHE_EXP_TIME: { + message: 'Redis Cache Expiry Time', + optional: false, + }, } let success = true diff --git a/src/generics/utils.js b/src/generics/utils.js index a0626a7ac..33dccdd14 100644 --- a/src/generics/utils.js +++ b/src/generics/utils.js @@ -11,6 +11,7 @@ const momentTimeZone = require('moment-timezone') const moment = require('moment') const path = require('path') const md5 = require('md5') +const { RedisHelper, InternalCache } = require('elevate-node-cache') const hash = (str) => { const salt = bcryptJs.genSaltSync(10) @@ -119,6 +120,41 @@ function md5Hash(value) { return md5(value) } +function compareVersion(dbValue, apiValue) { + if (dbValue == apiValue) { + return false + } else { + dbValue = dbValue.split('.') + apiValue = apiValue.split('.') + for (let i = 0; i < dbValue.length; i++) { + if (dbValue[i] > apiValue[i]) { + return false + } + } + return true + } +} + +function internalSet(key, value) { + return InternalCache.setKey(key, value) +} +function internalGet(key) { + return InternalCache.getKey(key) +} +function internalDel(key) { + return InternalCache.delKey(key) +} + +function redisSet(key, value, exp) { + return RedisHelper.setKey(key, value, exp) +} +function redisGet(key) { + return RedisHelper.getKey(key) +} +function redisDel(key) { + return RedisHelper.deleteKey(key) +} + module.exports = { hash: hash, getCurrentMonthRange: getCurrentMonthRange, @@ -131,4 +167,11 @@ module.exports = { getTimeZone, utcFormat: utcFormat, md5Hash: md5Hash, + compareVersion: compareVersion, + internalSet: internalSet, + internalDel: internalDel, + internalGet: internalGet, + redisSet: redisSet, + redisGet: redisGet, + redisDel: redisDel, } diff --git a/src/globalConfig.json b/src/globalConfig.json index 140400d12..0cd41e00d 100644 --- a/src/globalConfig.json +++ b/src/globalConfig.json @@ -1 +1 @@ -{ "mongoUri": "mongodb://127.0.0.1:60964/", "mongoDBName": "jest" } +{ "mongoUri": "mongodb://127.0.0.1:51929/", "mongoDBName": "jest" } diff --git a/src/locales/en.json b/src/locales/en.json index bb0680bc2..e62a3c712 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -59,5 +59,6 @@ "SESSION_DURATION_TIME": "Session cannot be created ! Session duration should be less than 24 hours.", "SESSION__MINIMUM_DURATION_TIME": "End time should be atleast 30 minutes after start time.", "PROFILE_FTECHED_SUCCESSFULLY": "Profile fetched successfully.", - "UPCOMING_SESSION_FETCHED": "Upcoming session fetched successfully." + "UPCOMING_SESSION_FETCHED": "Upcoming session fetched successfully.", + "UPDATE_FORM_VERSION": "Update form version." } diff --git a/src/package.json b/src/package.json index aa1fe114b..44e7dfe08 100644 --- a/src/package.json +++ b/src/package.json @@ -24,6 +24,7 @@ "cors": "^2.8.5", "crypto": "^1.0.1", "dotenv": "^10.0.0", + "elevate-node-cache": "^1.0.2", "express": "^4.17.1", "express-validator": "^5.3.1", "files-cloud-storage": "^1.2.2", diff --git a/src/services/helper/entity.js b/src/services/helper/entity.js index 2d39213d5..cd505eaec 100644 --- a/src/services/helper/entity.js +++ b/src/services/helper/entity.js @@ -1,10 +1,11 @@ // Dependencies const ObjectId = require('mongoose').Types.ObjectId -const utilsHelper = require('@generics/utils') + const httpStatusCode = require('@generics/http-status') const common = require('@constants/common') const entitiesData = require('@db/entities/query') +const utils = require('@generics/utils') module.exports = class EntityHelper { /** * Create entity. @@ -28,6 +29,8 @@ module.exports = class EntityHelper { }) } await entitiesData.createEntity(bodyData) + const key = 'entity_' + bodyData.type + await utils.internalDel(key) return common.successResponse({ statusCode: httpStatusCode.created, message: 'ENTITY_CREATED_SUCCESSFULLY', @@ -65,6 +68,15 @@ module.exports = class EntityHelper { responseCode: 'CLIENT_ERROR', }) } + let key = '' + if (bodyData.type) { + key = 'entity_' + bodyData.type + await utils.internalDel(key) + } else { + const entities = await entitiesData.findOne(_id) + key = 'entity_' + entities.type + await utils.internalDel(key) + } return common.successResponse({ statusCode: httpStatusCode.accepted, message: 'ENTITY_UPDATED_SUCCESSFULLY', @@ -87,7 +99,12 @@ module.exports = class EntityHelper { bodyData.deleted = false } try { - const entities = await entitiesData.findAllEntities(bodyData) + const key = 'entity_' + bodyData.type + let entities = (await utils.internalGet(key)) || false + if (!entities) { + entities = await entitiesData.findAllEntities(bodyData) + await utils.internalSet(key, entities) + } return common.successResponse({ statusCode: httpStatusCode.ok, message: 'ENTITY_FETCHED_SUCCESSFULLY', @@ -122,6 +139,10 @@ module.exports = class EntityHelper { responseCode: 'CLIENT_ERROR', }) } + const entities = await entitiesData.findOne(_id) + let key = 'entity_' + entities.type + await utils.internalDel(key) + return common.successResponse({ statusCode: httpStatusCode.accepted, message: 'ENTITY_DELETED_SUCCESSFULLY', diff --git a/src/services/helper/form.js b/src/services/helper/form.js index 5f3da3eb2..fc5170633 100644 --- a/src/services/helper/form.js +++ b/src/services/helper/form.js @@ -1,10 +1,7 @@ -const ObjectId = require('mongoose').Types.ObjectId - -const utilsHelper = require('@generics/utils') const httpStatusCode = require('@generics/http-status') const common = require('@constants/common') const formsData = require('@db/forms/queries') - +const utils = require('@generics/utils') module.exports = class FormsHelper { /** * Create Form. @@ -16,13 +13,7 @@ module.exports = class FormsHelper { static async create(bodyData) { try { - const form = await formsData.findOneForm( - bodyData.type, - bodyData.subType, - bodyData.action, - bodyData.ver, - bodyData.data.templateName - ) + const form = await formsData.findOneForm(bodyData.type) if (form) { return common.failureResponse({ message: 'FORM_ALREADY_EXISTS', @@ -31,6 +22,8 @@ module.exports = class FormsHelper { }) } await formsData.createForm(bodyData) + await utils.redisDel('formVersion') + return common.successResponse({ statusCode: httpStatusCode.created, message: 'FORM_CREATED_SUCCESSFULLY', @@ -50,24 +43,36 @@ module.exports = class FormsHelper { static async update(bodyData) { try { - const result = await formsData.updateOneForm(bodyData) - if (result === 'ENTITY_ALREADY_EXISTS') { - return common.failureResponse({ - message: 'FORM_ALREADY_EXISTS', - statusCode: httpStatusCode.bad_request, - responseCode: 'CLIENT_ERROR', + const checkVersion = await formsData.checkVersion(bodyData) + if (checkVersion) { + const result = await formsData.updateOneForm(bodyData) + + if (result === 'ENTITY_ALREADY_EXISTS') { + return common.failureResponse({ + message: 'FORM_ALREADY_EXISTS', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } else if (result === 'ENTITY_NOT_FOUND') { + return common.failureResponse({ + message: 'FORM_NOT_FOUND', + statusCode: httpStatusCode.bad_request, + responseCode: 'CLIENT_ERROR', + }) + } + await utils.redisDel('formVersion') + + return common.successResponse({ + statusCode: httpStatusCode.accepted, + message: 'FORM_UPDATED_SUCCESSFULLY', }) - } else if (result === 'ENTITY_NOT_FOUND') { + } else { return common.failureResponse({ - message: 'FORM_NOT_FOUND', + message: 'UPDATE_FORM_VERSION', statusCode: httpStatusCode.bad_request, responseCode: 'CLIENT_ERROR', }) } - return common.successResponse({ - statusCode: httpStatusCode.accepted, - message: 'FORM_UPDATED_SUCCESSFULLY', - }) } catch (error) { throw error } @@ -87,9 +92,9 @@ module.exports = class FormsHelper { bodyData.type, bodyData.subType, bodyData.action, - bodyData.ver, bodyData.templateName ) + if (!form) { return common.failureResponse({ message: 'FORM_NOT_FOUND', diff --git a/src/services/helper/mentors.js b/src/services/helper/mentors.js index b18ac4078..3ed642529 100644 --- a/src/services/helper/mentors.js +++ b/src/services/helper/mentors.js @@ -60,7 +60,6 @@ module.exports = class MentorsHelper { }) } } catch (err) { - console.log(err) return err } } @@ -73,28 +72,41 @@ module.exports = class MentorsHelper { * @returns {JSON} - profile details */ static async profile(id) { - const mentorsDetails = await userProfile.details('', id) - if (mentorsDetails.data.result.isAMentor) { - const _id = mentorsDetails.data.result._id - const filterSessionAttended = { userId: _id, isSessionAttended: true } - const totalSessionsAttended = await sessionAttendees.countAllSessionAttendees(filterSessionAttended) - const filterSessionHosted = { userId: _id, status: 'completed', isStarted: true } - const totalSessionHosted = await sessionsData.findSessionHosted(filterSessionHosted) - return common.successResponse({ - statusCode: httpStatusCode.ok, - message: 'PROFILE_FTECHED_SUCCESSFULLY', - result: { - sessionsAttended: totalSessionsAttended, - sessionsHosted: totalSessionHosted, - ...mentorsDetails.data.result, - }, - }) - } else { - return common.failureResponse({ - statusCode: httpStatusCode.bad_request, - message: 'MENTORS_NOT_FOUND', - responseCode: 'CLIENT_ERROR', - }) + try { + let mentorsDetails = (await utils.redisGet(id)) || false + + if (!mentorsDetails) { + if (ObjectId.isValid(id)) { + mentorsDetails = await userProfile.details('', id) + await utils.redisSet(id, mentorsDetails) + } else { + mentorsDetails = await userProfile.details('', id) + } + } + if (mentorsDetails.data.result.isAMentor) { + const _id = mentorsDetails.data.result._id + const filterSessionAttended = { userId: _id, isSessionAttended: true } + const totalSessionsAttended = await sessionAttendees.countAllSessionAttendees(filterSessionAttended) + const filterSessionHosted = { userId: _id, status: 'completed', isStarted: true } + const totalSessionHosted = await sessionsData.findSessionHosted(filterSessionHosted) + return common.successResponse({ + statusCode: httpStatusCode.ok, + message: 'PROFILE_FTECHED_SUCCESSFULLY', + result: { + sessionsAttended: totalSessionsAttended, + sessionsHosted: totalSessionHosted, + ...mentorsDetails.data.result, + }, + }) + } else { + return common.failureResponse({ + statusCode: httpStatusCode.bad_request, + message: 'MENTORS_NOT_FOUND', + responseCode: 'CLIENT_ERROR', + }) + } + } catch (err) { + return err } } diff --git a/src/test/mentees.spec.js b/src/test/mentees.spec.js index 8fae24a90..de2a4f978 100644 --- a/src/test/mentees.spec.js +++ b/src/test/mentees.spec.js @@ -73,7 +73,9 @@ describe('Sessions controller and helper test', () => { gender: 'MALE', image: 'https://aws-bucket-storage-name.s3.ap-south-1.amazonaws.com/https://cloudstorage.com/container/abc.png', }, - meta: {}, + meta: { + formsVersion: {}, + }, } const userProfileApiResponse = { diff --git a/src/test/mentors.spec.js b/src/test/mentors.spec.js index c6df35d00..469c5daa6 100644 --- a/src/test/mentors.spec.js +++ b/src/test/mentors.spec.js @@ -13,6 +13,7 @@ describe('Sessions controller and helper test', () => { let sessionAttended let sessionsData let userProfile + let { RedisHelper } = require('elevate-node-cache') beforeAll(async () => { await loadMongo() @@ -76,7 +77,9 @@ describe('Sessions controller and helper test', () => { gender: 'MALE', image: 'https://aws-bucket-storage-name.s3.ap-south-1.amazonaws.com/https://cloudstorage.com/container/abc.png', }, - meta: {}, + meta: { + formsVersion: {}, + }, } const userProfileApiResponse = { @@ -134,6 +137,11 @@ describe('Sessions controller and helper test', () => { }, } + const mockRedisClientGetKey = jest.spyOn(RedisHelper, 'getKey') + mockRedisClientGetKey.mockResolvedValueOnce(false) + + const mockRedisClientSetKey = jest.spyOn(RedisHelper, 'setKey') + mockRedisClientSetKey.mockResolvedValueOnce(true) const mockUserDetails = jest.spyOn(userProfile, 'details') mockUserDetails.mockResolvedValueOnce(userProfileApiResponse) @@ -202,6 +210,10 @@ describe('Sessions controller and helper test', () => { }, } + const mockRedisClient = jest.spyOn(RedisHelper, 'getKey') + mockRedisClient.mockResolvedValueOnce(false) + const mockRedisClientSetKey = jest.spyOn(RedisHelper, 'setKey') + mockRedisClientSetKey.mockResolvedValueOnce(true) const mockUserDetails = jest.spyOn(userProfile, 'details') mockUserDetails.mockResolvedValueOnce(userProfileApiResponse)