Skip to content

Commit

Permalink
Cache aspect writers (#341)
Browse files Browse the repository at this point in the history
* populate aspect writers on the initial load to cache
* refactored the convertToISO to call .toISOString() only if the object has this function
* write permission for cached samples lookup aspectwriters in cache
* addition and deletion of aspect writers are carried forward to cached aspect
  • Loading branch information
shriramshankar authored and iamigo committed May 2, 2017
1 parent fb16ff8 commit 1b8cd36
Show file tree
Hide file tree
Showing 10 changed files with 802 additions and 104 deletions.
1 change: 0 additions & 1 deletion api/v1/controllers/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
const featureToggles = require('feature-toggles');
const sampleStore = require('../../../cache/sampleStore');
const sampleStoreInit = require('../../../cache/sampleStoreInit');
const sampleStorePersist = require('../../../cache/sampleStorePersist');
const apiErrors = require('../apiErrors');
const httpStatus = require('../constants').httpStatus;
const authUtils = require('../helpers/authUtils');
Expand Down
111 changes: 101 additions & 10 deletions api/v1/controllers/aspects.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* api/v1/controllers/aspects.js
*/
'use strict';
'use strict'; // eslint-disable-line strict

const featureToggles = require('feature-toggles');
const utils = require('./utils');
Expand All @@ -30,8 +30,7 @@ const doPost = require('../helpers/verbs/doPost');
const doPut = require('../helpers/verbs/doPut');
const u = require('../helpers/verbs/utils');
const httpStatus = require('../constants').httpStatus;
const ZERO = 0;
const ONE = 1;
const redisOps = require('../../../cache/redisOps');

/**
* Validates the given fields from request body or url.
Expand Down Expand Up @@ -164,7 +163,9 @@ module.exports = {
/**
* POST /aspects/{key}/writers
*
* Add one or more users to an aspect’s list of authorized writers
* Add one or more users to an aspect’s list of authorized writers. If
* the "enableRedisSampleStore" is turned on add the writers to the aspect
* stored in redis
*
* @param {IncomingMessage} req - The request object
* @param {ServerResponse} res - The response object
Expand All @@ -175,11 +176,40 @@ module.exports = {
const toPost = params.queryBody.value;
const options = {};
options.where = u.whereClauseForNameInArr(toPost);
let users;
userProps.model.findAll(options)
.then((usrs) => {
users = usrs;
if (featureToggles.isFeatureEnabled('enableRedisSampleStore')) {
return u.findByKey(helper, params)
.then((o) => u.isWritable(req, o,
featureToggles.isFeatureEnabled('enforceWritePermission')))
.then((o) => redisOps.getValue('aspect', o.name))
.then((cachedAspect) => {
if (cachedAspect) {
const userSet = new Set();
usrs.forEach((user) => {
userSet.add(user.dataValues.name);
});
cachedAspect.writers = cachedAspect.writers || [];
Array.from(userSet).forEach((user) => {
cachedAspect.writers.push(user);
});
return redisOps.hmSet('aspect', cachedAspect.name, cachedAspect);
}

// reject the promise if the aspect is not found in the cache
return Promise.reject(new apiErrors.ResourceNotFoundError());
});
}

return Promise.resolve(true);
})
.then(() => {
doPostAssoc(req, res, next, helper,
helper.belongsToManyAssoc.users, usrs);
});
helper.belongsToManyAssoc.users, users);
})
.catch((err) => u.handleError(next, err, helper.modelName));
}, // postAspectWriters

/**
Expand Down Expand Up @@ -230,29 +260,90 @@ module.exports = {
/**
* DELETE /aspects/{keys}/writers
*
* Deletes all the writers associated with this resource.
* Deletes all the writers associated with this resource. If the
* "enableRedisSampleStore" is turned on, delete all writers for this aspect
* from the cache too.
*
* @param {IncomingMessage} req - The request object
* @param {ServerResponse} res - The response object
* @param {Function} next - The next middleware function in the stack
*/
deleteAspectWriters(req, res, next) {
doDeleteAllAssoc(req, res, next, helper, helper.belongsToManyAssoc.users);
if (featureToggles.isFeatureEnabled('enableRedisSampleStore')) {
const params = req.swagger.params;
u.findByKey(helper, params)
.then((o) => u.isWritable(req, o,
featureToggles.isFeatureEnabled('enforceWritePermission')))
.then((o) => redisOps.getValue('aspect', o.name))
.then((cachedAspect) => {
if (cachedAspect) {
cachedAspect.writers = [];
return redisOps.hmSet('aspect', cachedAspect.name, cachedAspect);
}

// reject the promise if the aspect is not found in the cache
return Promise.reject(new apiErrors.ResourceNotFoundError());
})
.then(() => doDeleteAllAssoc(req, res, next, helper,
helper.belongsToManyAssoc.users))
.catch((err) => u.handleError(next, err, helper.modelName));
} else {
doDeleteAllAssoc(req, res, next, helper, helper.belongsToManyAssoc.users);
}
},

/**
* DELETE /aspects/{keys}/writers/userNameOrId
*
* Deletes a user from an aspect’s list of authorized writers.
* Deletes a user from an aspect’s list of authorized writers. If the
* "enableRedisSampleStore" feature is turned on, delete that user from the
* authorized list of writers stored in the cache for this aspect.
*
* @param {IncomingMessage} req - The request object
* @param {ServerResponse} res - The response object
* @param {Function} next - The next middleware function in the stack
*/
deleteAspectWriter(req, res, next) {
const userNameOrId = req.swagger.params.userNameOrId.value;
doDeleteOneAssoc(req, res, next, helper,
let aspectName;
let userName;
if (featureToggles.isFeatureEnabled('enableRedisSampleStore')) {
const params = req.swagger.params;
u.findByKey(helper, params)
.then((o) => u.isWritable(req, o,
featureToggles.isFeatureEnabled('enforceWritePermission')))
.then((o) => {
aspectName = o.name;
const options = {};
options.where = u.whereClauseForNameOrId(params.userNameOrId.value);
return u.findAssociatedInstances(helper,
params, helper.belongsToManyAssoc.users, options);
})
.then((o) => {
u.throwErrorForEmptyArray(o,
params.userNameOrId.value, userProps.modelName);

// the object "o" here is always an array of length 1
userName = o[0].dataValues.name;
return redisOps.getValue('aspect', aspectName);
})
.then((cachedAspect) => {
if (cachedAspect) {
cachedAspect.writers = cachedAspect.writers
.filter((writer) => writer !== userName);
return redisOps.hmSet('aspect', cachedAspect.name, cachedAspect);
}

// reject the promise if the aspect is not found in the cache
return Promise.reject(new apiErrors.ResourceNotFoundError());
})
.then(() => doDeleteOneAssoc(req, res, next, helper,
helper.belongsToManyAssoc.users, userNameOrId))
.catch((err) => u.handleError(next, err, helper.modelName));
} else {
doDeleteOneAssoc(req, res, next, helper,
helper.belongsToManyAssoc.users, userNameOrId);
}
},

/**
Expand Down
1 change: 0 additions & 1 deletion api/v1/controllers/samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
'use strict'; // eslint-disable-line strict

const featureToggles = require('feature-toggles');
const apiErrors = require('../apiErrors');
const helper = require('../helpers/nouns/samples');
const subHelper = require('../helpers/nouns/subjects');
const doDelete = require('../helpers/verbs/doDelete');
Expand Down
Loading

0 comments on commit 1b8cd36

Please sign in to comment.