/** * @name : lockService.js * @description :: Service responsible for locking mechanism * @author :: Sourav Dey */ var async = require('async') var path = require('path') var respUtil = require('response_util') var LOG = require('sb_logger_util') var configUtil = require('sb-config-util') var request = require('request') var messageUtils = require('./messageUtil') var utilsService = require('../service/utilsService') var lodash = require('lodash') var dbModel = require('./../utils/cassandraUtil').getConnections('lock_db') var Joi = require('joi') var filename = path.basename(__filename) var contentMessage = messageUtils.CONTENT var responseCode = messageUtils.RESPONSE_CODE var defaultLockExpiryTime = parseInt(configUtil.getConfig('LOCK_EXPIRY_TIME')) var contentProvider = require('sb_content_provider_util') function createLock (req, response) { var lockId = dbModel.uuid() var newDateObj = createExpiryTime() var data = req.body var rspObj = req.rspObj var contentBody = '' var versionKey = '' if (!req.get('x-device-id')) { rspObj.errCode = contentMessage.CREATE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.CREATE_LOCK.DEVICE_ID_MISSING rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } if (req.get('x-authenticated-userid') !== data.request.createdBy) { rspObj.errCode = contentMessage.CREATE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.CREATE_LOCK.UNAUTHORIZED rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(403).send(respUtil.errorResponse(rspObj)) } if (!data.request) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'createLockAPI', 'Error due to required params are missing', data.request)) rspObj.errCode = contentMessage.CREATE_LOCK.MISSING_CODE rspObj.errMsg = contentMessage.CREATE_LOCK.MISSING_MESSAGE rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } var result = validateCreateLockRequestBody(data.request) if (result.error) { rspObj.errCode = contentMessage.CREATE_LOCK.MISSING_CODE rspObj.errMsg = result.error.details[0].message rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } // Adding objectData in telemetry if (rspObj.telemetryData) { rspObj.telemetryData.object = utilsService.getObjectData(data.request.resourceId, 'contentLock', '', {}) } async.waterfall([ function (CBW) { checkResourceTypeValidation(req, function (res, body) { if (!res) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'createLockAPI', 'Error as resource type validation failed', 'res = ' + res + ', body = ' + body)) rspObj.errCode = contentMessage.CREATE_LOCK.FAILED_CODE rspObj.errMsg = body.message rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(412).send(respUtil.errorResponse(rspObj)) } contentBody = body versionKey = contentBody.contentdata.versionKey CBW() }) }, function (CBW) { dbModel.instance.lock.findOne({ resourceId: data.request.resourceId, resourceType: data.request.resourceType }, function (error, result) { if (error) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'createLockAPI', 'error while getting data from db', error)) rspObj.errCode = contentMessage.CREATE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.CREATE_LOCK.FAILED_MESSAGE rspObj.responseCode = responseCode.SERVER_ERROR return response.status(500).send(respUtil.errorResponse(rspObj)) } else if (result) { if (req.get('x-authenticated-userid') === result.createdBy && req.get('x-device-id') === result.deviceId && data.request.resourceType === result.resourceType) { rspObj.result.lockKey = result.lockId rspObj.result.expiresAt = result.expiresAt rspObj.result.expiresIn = defaultLockExpiryTime / 60 rspObj.result.versionKey = versionKey return response.status(200).send(respUtil.successResponse(rspObj)) } else if (req.get('x-authenticated-userid') === result.createdBy) { rspObj.errCode = contentMessage.CREATE_LOCK.SELF_LOCKED_CODE rspObj.errMsg = contentMessage.CREATE_LOCK.SAME_USER_ERR_MSG var statusCode = 400 } else { rspObj.errCode = contentMessage.CREATE_LOCK.LOCKED_CODE statusCode = 423 try { var user = JSON.parse(result.creatorInfo).name } catch (e) { user = 'another user' } rspObj.errMsg = contentMessage.CREATE_LOCK.ALREADY_LOCKED.replace(/{{Name}}/g, user) } rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(statusCode).send(respUtil.errorResponse(rspObj)) } else { var lockObject = new dbModel.instance.lock({ lockId: lockId, resourceId: data.request.resourceId, resourceType: data.request.resourceType, resourceInfo: data.request.resourceInfo, createdBy: data.request.createdBy, creatorInfo: data.request.creatorInfo, deviceId: req.get('x-device-id'), createdOn: new Date(), expiresAt: newDateObj }) lockObject.save({ ttl: defaultLockExpiryTime }, function (err, resp) { if (err) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'createLockAPI', 'error while saving lock data from db', err)) rspObj.errCode = contentMessage.CREATE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.CREATE_LOCK.FAILED_MESSAGE rspObj.responseCode = responseCode.SERVER_ERROR return response.status(500).send(respUtil.errorResponse(rspObj)) } else CBW() }) } }) }, function (CBW) { var ekStepReqData = { 'request': { 'content': { 'lockKey': lockId, 'versionKey': versionKey } } } contentProvider.updateContent(ekStepReqData, data.request.resourceId, req.headers, function (err, res) { if (err || res.responseCode !== responseCode.SUCCESS) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'createLockAPI', 'Updating content failed with lock key', 'err = ' + err + ', res = ' + res)) // Sending success CBW as content is already locked in db and ignoring content update error CBW(null, res) } else { versionKey = lodash.get(res.result.versionKey) CBW(null, res) } }) }, function () { rspObj.result.lockKey = lockId rspObj.result.expiresAt = newDateObj rspObj.result.expiresIn = defaultLockExpiryTime / 60 rspObj.result.versionKey = versionKey return response.status(200).send(respUtil.successResponse(rspObj)) } ]) } function refreshLock (req, response) { var lockId = '' var contentBody = '' var newDateObj = createExpiryTime() var data = req.body var rspObj = req.rspObj if (!req.get('x-device-id')) { rspObj.errCode = contentMessage.REFRESH_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.REFRESH_LOCK.DEVICE_ID_MISSING rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } if (!data.request) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'refreshLockAPI', 'Error due to required params are missing', data.request)) rspObj.errCode = contentMessage.REFRESH_LOCK.MISSING_CODE rspObj.errMsg = contentMessage.REFRESH_LOCK.MISSING_MESSAGE rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } var result = validateRefreshLockRequestBody(data.request) if (result.error) { rspObj.errCode = contentMessage.REFRESH_LOCK.MISSING_CODE rspObj.errMsg = result.error.details[0].message rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } // Adding objectData in telemetry if (rspObj.telemetryData) { rspObj.telemetryData.object = utilsService.getObjectData(data.request.resourceId, 'refreshLock', '', {}) } async.waterfall([ function (CBW) { checkResourceTypeValidation(req, function (res, body) { if (!res) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'refreshLockAPI', 'Error as resource type validation failed', 'res = ' + res + ', body = ' + body)) rspObj.errCode = contentMessage.REFRESH_LOCK.FAILED_CODE rspObj.errMsg = body.message rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(412).send(respUtil.errorResponse(rspObj)) } contentBody = body CBW() }) }, function (CBW) { dbModel.instance.lock.findOne({ resourceId: data.request.resourceId, resourceType: data.request.resourceType }, function (error, result) { if (error) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'refreshLockAPI', 'error while getting data from db for refreshing lock', error)) rspObj.errCode = contentMessage.REFRESH_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.REFRESH_LOCK.FAILED_MESSAGE rspObj.responseCode = responseCode.SERVER_ERROR return response.status(500).send(respUtil.errorResponse(rspObj)) } else if (result) { lockId = result.lockId if (result.createdBy !== req.get('x-authenticated-userid')) { rspObj.errCode = contentMessage.REFRESH_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.REFRESH_LOCK.UNAUTHORIZED rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(403).send(respUtil.errorResponse(rspObj)) } var options = { ttl: defaultLockExpiryTime, if_exists: true } dbModel.instance.lock.update( { resourceId: data.request.resourceId, resourceType: data.request.resourceType }, { lockId: result.lockId, resourceInfo: result.resourceInfo, createdBy: result.createdBy, creatorInfo: result.creatorInfo, deviceId: result.deviceId, createdOn: result.createdOn, expiresAt: newDateObj }, options, function (err) { if (err) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'refreshLockAPI', 'error while updating lock data from db', err)) rspObj.errCode = contentMessage.REFRESH_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.REFRESH_LOCK.FAILED_MESSAGE rspObj.responseCode = responseCode.SERVER_ERROR return response.status(500).send(respUtil.errorResponse(rspObj)) } CBW() }) } else { var requestBody = req.body requestBody.request.resourceInfo = JSON.stringify(contentBody.contentdata) requestBody.request.createdBy = req.get('x-authenticated-userid') requestBody.request.creatorInfo = JSON.stringify({'name': req.rspObj.userName, 'id': req.get('x-authenticated-userid')}) if (contentBody.contentdata.lockKey === data.request.lockId) { delete requestBody.request.lockId createLock(req, response) } else { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'refreshLockAPI', 'no data found from db for refreshing lock', data.request)) rspObj.errCode = contentMessage.REFRESH_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.REFRESH_LOCK.NOT_FOUND_FAILED_MESSAGE rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } } }) }, function () { rspObj.result.lockKey = lockId rspObj.result.expiresAt = newDateObj rspObj.result.expiresIn = defaultLockExpiryTime / 60 return response.status(200).send(respUtil.successResponse(rspObj)) } ]) } function retireLock (req, response) { var data = req.body var rspObj = req.rspObj if (!req.get('x-device-id')) { rspObj.errCode = contentMessage.RETIRE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.RETIRE_LOCK.DEVICE_ID_MISSING rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } if (!data.request) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'retireLockAPI', 'Error due to required params are missing', data.request)) rspObj.errCode = contentMessage.RETIRE_LOCK.MISSING_CODE rspObj.errMsg = contentMessage.RETIRE_LOCK.MISSING_MESSAGE rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } var result = validateCommonRequestBody(data.request) if (result.error) { rspObj.errCode = contentMessage.RETIRE_LOCK.MISSING_CODE rspObj.errMsg = result.error.details[0].message rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } // Adding objectData in telemetry if (rspObj.telemetryData) { rspObj.telemetryData.object = utilsService.getObjectData(data.request.resourceId, 'retireLock', '', {}) } async.waterfall([ function (CBW) { checkResourceTypeValidation(req, function (res, body) { if (!res) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'retireLockAPI', 'Error as resource type validation failed', 'res = ' + res + ', body = ' + body)) rspObj.errCode = contentMessage.RETIRE_LOCK.FAILED_CODE rspObj.errMsg = body.message rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(412).send(respUtil.errorResponse(rspObj)) } CBW() }) }, function (CBW) { dbModel.instance.lock.findOne({ resourceId: data.request.resourceId }, { resourceType: data.request.resourceType }, function (error, result) { if (error) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'retireLockAPI', 'error while getting data from db for retiring lock', error)) rspObj.errCode = contentMessage.RETIRE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.RETIRE_LOCK.FAILED_MESSAGE rspObj.responseCode = responseCode.SERVER_ERROR return response.status(500).send(respUtil.errorResponse(rspObj)) } else if (result) { if (result.createdBy !== req.get('x-authenticated-userid')) { rspObj.errCode = contentMessage.RETIRE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.RETIRE_LOCK.UNAUTHORIZED rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(403).send(respUtil.errorResponse(rspObj)) } dbModel.instance.lock.delete({ resourceId: data.request.resourceId }, { resourceType: data.request.resourceType }, function (err) { if (err) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'retireLockAPI', 'error while deleting lock data from db', err)) rspObj.errCode = contentMessage.RETIRE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.RETIRE_LOCK.FAILED_MESSAGE rspObj.responseCode = responseCode.SERVER_ERROR return response.status(500).send(respUtil.errorResponse(rspObj)) } else CBW() }) } else { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'retireLockAPI', 'no data found from db for retiring lock', data.request)) rspObj.errCode = contentMessage.RETIRE_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.RETIRE_LOCK.NOT_FOUND_FAILED_MESSAGE rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } }) }, function () { return response.status(200).send(respUtil.successResponse(rspObj)) } ]) } function listLock (req, response) { var data = req.body var rspObj = req.rspObj if (!req.get('x-device-id')) { rspObj.errCode = contentMessage.LIST_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.LIST_LOCK.DEVICE_ID_MISSING rspObj.responseCode = responseCode.CLIENT_ERROR return response.status(400).send(respUtil.errorResponse(rspObj)) } // Adding objectData in telemetry if (rspObj.telemetryData) { rspObj.telemetryData.object = utilsService.getObjectData(data, 'ListLockAPI', '', {}) } var query = {} if (lodash.get(data, 'request.filters.resourceId')) { if (typeof data.request.filters === 'string') { query = { resourceId: { '$in': [data.request.filters.resourceId] } } } else { query = { resourceId: { '$in': data.request.filters.resourceId } } } } dbModel.instance.lock.find(query, function (error, result) { if (error) { LOG.error(utilsService.getLoggerData(rspObj, 'ERROR', filename, 'ListLockAPI', 'error while fetching lock list data from db', error)) rspObj.errCode = contentMessage.LIST_LOCK.FAILED_CODE rspObj.errMsg = contentMessage.LIST_LOCK.FAILED_MESSAGE rspObj.responseCode = responseCode.SERVER_ERROR return response.status(500).send(respUtil.errorResponse(rspObj)) } else { rspObj.result.count = result.length rspObj.result.data = result return response.status(200).send(respUtil.successResponse(rspObj)) } }) } function validateCreateLockRequestBody (request) { var schema = Joi.object().keys({ resourceId: Joi.string().required(), resourceType: Joi.string().required(), resourceInfo: Joi.string().required(), createdBy: Joi.string().required(), creatorInfo: Joi.string().required() }) return Joi.validate(request, schema) } function validateRefreshLockRequestBody (request) { var schema = Joi.object().keys({ lockId: Joi.string().required(), resourceId: Joi.string().required(), resourceType: Joi.string().required() }) return Joi.validate(request, schema) } function validateCommonRequestBody (request) { var schema = Joi.object().keys({ resourceId: Joi.string().required(), resourceType: Joi.string().required() }) return Joi.validate(request, schema) } function createExpiryTime () { var dateObj = new Date() dateObj.setTime(new Date().getTime() + (defaultLockExpiryTime * 1000)) return dateObj } function checkResourceTypeValidation (req, CBW) { switch (lodash.lowerCase(req.body.request.resourceType)) { case 'content': var httpOptions = { url: configUtil.getConfig('CONTENT_SERVICE_LOCAL_BASE_URL') + '/v1/content/getContentLockValidation', headers: req.headers, method: 'POST', body: req.body, json: true } request(httpOptions, function (err, httpResponse, body) { if (err) { LOG.error(utilsService.getLoggerData(req.rspObj, 'ERROR', filename, 'checkResourceTypeValidation', 'error in lock service in checkResourceTypeValidation', err)) CBW(false, err) } else if (lodash.get(body, 'result.message')) { CBW(body.result.validation, body.result) } else { CBW(false, body) } }) break default: CBW(false, 'Resource type is not valid') } } module.exports = { createLock, refreshLock, retireLock, listLock }