From 8033e59c4d4d683565b7420e78c0f50107708c30 Mon Sep 17 00:00:00 2001 From: Shriram Date: Wed, 22 Mar 2017 14:55:26 -0700 Subject: [PATCH] realtime events for put, patch, and delete moved to api layer for samples (#298) --- api/v1/helpers/nouns/samples.js | 9 ++++++ api/v1/helpers/verbs/doDelete.js | 8 +++++ api/v1/helpers/verbs/doPatch.js | 9 ++++++ api/v1/helpers/verbs/doPost.js | 9 ++++++ api/v1/helpers/verbs/doPut.js | 9 ++++++ api/v1/helpers/verbs/utils.js | 1 + db/model/sample.js | 52 +++----------------------------- realtime/redisPublisher.js | 35 +++++++++++++++------ tests/realtime/redisPublisher.js | 26 ++++++++++++++-- 9 files changed, 99 insertions(+), 59 deletions(-) diff --git a/api/v1/helpers/nouns/samples.js b/api/v1/helpers/nouns/samples.js index 06654df827..53885fefeb 100644 --- a/api/v1/helpers/nouns/samples.js +++ b/api/v1/helpers/nouns/samples.js @@ -12,6 +12,9 @@ 'use strict'; const Sample = require('../../../../db/index').Sample; +const Aspect = require('../../../../db/index').Aspect; +const Subject = require('../../../../db/index').Subject; + const m = 'sample'; const fieldsWithJsonArrayType = ['relatedLinks']; @@ -33,4 +36,10 @@ module.exports = { fieldsWithJsonArrayType, fieldsWithEnum, fieldsToExclude, + publishEvents: true, + associatedModels: { + aspect: Aspect, + subject: Subject, + }, + }; // exports diff --git a/api/v1/helpers/verbs/doDelete.js b/api/v1/helpers/verbs/doDelete.js index 60bdcd9efb..29af1e806c 100644 --- a/api/v1/helpers/verbs/doDelete.js +++ b/api/v1/helpers/verbs/doDelete.js @@ -12,6 +12,8 @@ 'use strict'; // eslint-disable-line strict const u = require('./utils'); +const publisher = u.publisher; +const event = u.realtimeEvents; const httpStatus = require('../../constants').httpStatus; const featureToggles = require('feature-toggles'); const constants = require('../../../../cache/sampleStore').constants; @@ -58,6 +60,12 @@ function doDelete(req, res, next, props) { ); } + // publish the delete event to the redis channel + if (props.publishEvents) { + publisher.publishSample(o, props.associatedModels.subject, + event.sample.del); + } + // when a resource is deleted, delete all its associations too u.deleteAllAssociations(o, assocNames); u.logAPI(req, resultObj, o); diff --git a/api/v1/helpers/verbs/doPatch.js b/api/v1/helpers/verbs/doPatch.js index dc253a495d..56acd60814 100644 --- a/api/v1/helpers/verbs/doPatch.js +++ b/api/v1/helpers/verbs/doPatch.js @@ -13,6 +13,8 @@ const featureToggles = require('feature-toggles'); const u = require('./utils'); +const publisher = u.publisher; +const event = u.realtimeEvents; const httpStatus = require('../../constants').httpStatus; const constants = require('../../../../cache/sampleStore').constants; const redisModelSample = require('../../../../cache/models/samples'); @@ -66,6 +68,13 @@ function doPatch(req, res, next, props) { .then((retVal) => { resultObj.dbTime = new Date() - resultObj.reqStartTime; u.logAPI(req, resultObj, retVal); + + // publish the update event to the redis channel + if (props.publishEvents) { + publisher.publishSample(retVal, + props.associatedModels.subject, event.sample.upd); + } + return res.status(httpStatus.OK) .json(u.responsify(retVal, props, req.method)); }) diff --git a/api/v1/helpers/verbs/doPost.js b/api/v1/helpers/verbs/doPost.js index c5e6eef55e..069aedd847 100644 --- a/api/v1/helpers/verbs/doPost.js +++ b/api/v1/helpers/verbs/doPost.js @@ -12,6 +12,8 @@ 'use strict'; // eslint-disable-line strict const u = require('./utils'); +const publisher = u.publisher; +const event = u.realtimeEvents; const httpStatus = require('../../constants').httpStatus; const constants = require('../../../../cache/sampleStore').constants; const redisModelSample = require('../../../../cache/models/samples'); @@ -47,6 +49,13 @@ function doPost(req, res, next, props) { postPromise.then((o) => { resultObj.dbTime = new Date() - resultObj.reqStartTime; u.logAPI(req, resultObj, o); + + // publish the update event to the redis channel + if (props.publishEvents) { + publisher.publishSample(o, props.associatedModels.subject, + event.sample.add, props.associatedModels.aspect); + } + return res.status(httpStatus.CREATED) .json(u.responsify(o, props, req.method)); }) diff --git a/api/v1/helpers/verbs/doPut.js b/api/v1/helpers/verbs/doPut.js index 140848e9db..fd6605c201 100644 --- a/api/v1/helpers/verbs/doPut.js +++ b/api/v1/helpers/verbs/doPut.js @@ -13,6 +13,8 @@ const featureToggles = require('feature-toggles'); const u = require('./utils'); +const publisher = u.publisher; +const event = u.realtimeEvents; const httpStatus = require('../../constants').httpStatus; const constants = require('../../../../cache/sampleStore').constants; const redisModelSample = require('../../../../cache/models/samples'); @@ -75,6 +77,13 @@ function doPut(req, res, next, props) { putPromise.then((o) => { resultObj.dbTime = new Date() - resultObj.reqStartTime; u.logAPI(req, resultObj, o); + + // publish the update event to the redis channel + if (props.publishEvents) { + publisher.publishSample(o, props.associatedModels.subject, + event.sample.upd); + } + res.status(httpStatus.OK).json(u.responsify(o, props, req.method)); }) .catch((err) => u.handleError(next, err, props.modelName)); diff --git a/api/v1/helpers/verbs/utils.js b/api/v1/helpers/verbs/utils.js index a157347245..e06f723c18 100644 --- a/api/v1/helpers/verbs/utils.js +++ b/api/v1/helpers/verbs/utils.js @@ -714,6 +714,7 @@ function checkDuplicateRLinks(rLinkArr) { // ---------------------------------------------------------------------------- module.exports = { + realtimeEvents, publisher, diff --git a/db/model/sample.js b/db/model/sample.js index 6c07519583..ddfa0cc31c 100644 --- a/db/model/sample.js +++ b/db/model/sample.js @@ -224,8 +224,10 @@ module.exports = function sample(seq, dataTypes) { .then((o) => resolve(o)) .catch((err) => { if (isBulk) { - /* adding isFailed:true to differentiate failed results from - success results in bulk upsert */ + /* + * adding isFailed:true to differentiate failed results from + * success results in bulk upsert + */ resolve({ explanation: err, isFailed: true }); } else { reject(err); @@ -319,52 +321,6 @@ module.exports = function sample(seq, dataTypes) { ); }, // hooks.beforeCreate - /** - * Publishes the created sample to redis channel *including* the values - * from its aspect association. - * - * @param {Sample} inst - The newly-created instance - */ - afterCreate(inst /* , opts */) { - let samp; - Sample.findOne({ - where: { - name: { - $iLike: inst.getDataValue('name'), - }, - }, - }) - .then((found) => { - samp = found; - return common.sampleAspectAndSubjectArePublished(seq, samp); - }) - .then((published) => { - if (published) { - // augment the sample instance with the subject instance to enable - // filtering by subjecttags in the realtime socketio module - common.augmentSampleWithSubjectAspectInfo(seq, samp) - .then(() => common.publishChange(samp, eventName.add)); - } - }); - }, - - /** - * Publishes the delete to redis subscriber - * - * @param {Sample} inst - The Sample instance which was just deleted - */ - afterDelete(inst /* , opts */) { - return common.sampleAspectAndSubjectArePublished(seq, inst) - .then((published) => { - if (published) { - // augument the sample instance with the subject instance to enable - // filtering by subjecttags in the realtime socketio module - common.augmentSampleWithSubjectAspectInfo(seq, inst) - .then(() => common.publishChange(inst, eventName.del)); - } - }); - }, // hooks.afterDelete - /** * Update isDeleted. * Publishes the deleted sample to redis channel. diff --git a/realtime/redisPublisher.js b/realtime/redisPublisher.js index 58b0aacbc8..bb6277870b 100644 --- a/realtime/redisPublisher.js +++ b/realtime/redisPublisher.js @@ -92,32 +92,49 @@ function publishObject(inst, event, changedKeys, ignoreAttributes) { * a absolutePath field added to it before the sample is published to the redis * channel. * @param {Object} sampleInst - The sample instance to be published - * @param {Model} model - The subject model used to get the related subject - * instance + * @param {Model} subjectModel - The subject model to get the related + * subject instance * @param {String} event - Type of the event that is being published + * @param {Model} aspectModel - The aspect model to get the related + * aspect instance * @returns {Promise} - which resolves to a sample object */ -function publishSample(sampleInst, model, event) { +function publishSample(sampleInst, subjectModel, event, aspectModel) { const eventType = event || getSampleEventType(sampleInst); const sample = sampleInst.get ? sampleInst.get() : sampleInst; - const subName = sample.name.split('|')[0]; - const options = {}; - options.where = { absolutePath: subName }; - return model.findOne(options) + const nameParts = sample.name.split('|'); + const subName = nameParts[0]; + const aspName = nameParts[1]; + const subOpts = { + where: { + absolutePath: subName, + }, + }; + const aspOpts = { + where: { + name: aspName, + }, + }; + const getAspect = aspectModel ? aspectModel.findOne(aspOpts) : + Promise.resolve(sample.aspect); + return getAspect + .then((asp) => { + sample.aspect = asp.get ? asp.get() : asp; + return subjectModel.findOne(subOpts); + }) .then((sub) => { if (sub) { + /* *pass the sample instance to the publishObject function only if the *aspect and subject are published */ if (sample.aspect && sample.aspect.isPublished && sub.isPublished) { - // attach subject to the sample sample.subject = sub.get(); // attach absolutePath field to the sample sample.absolutePath = subName; - publishObject(sample, eventType); } } diff --git a/tests/realtime/redisPublisher.js b/tests/realtime/redisPublisher.js index 80141308b0..423bfb74be 100644 --- a/tests/realtime/redisPublisher.js +++ b/tests/realtime/redisPublisher.js @@ -7,7 +7,7 @@ */ /** - * tests/realtime/setupSocketIO.js + * tests/realtime/redisPublisher.js */ 'use strict'; @@ -81,6 +81,29 @@ describe('redis Publisher', () => { }) .catch(done); }); + + it('when tried to publish sample without aspect,'+ + ' aspect should be attached', (done) => { + Sample.findById(sampId) + .then((sam) => { + const sampInst = sam.get(); + delete sampInst.aspect; + return publisher.publishSample(sam, Subject, sampleEvent.upd, Aspect); + }) + .then((pubObj) => { + expect(pubObj.aspect).to.not.equal(null); + expect(pubObj.aspect.name).to.equal(humidity.name); + expect(pubObj.aspect.tags.length).to.equal(0); + expect(pubObj.subject).to.not.equal(null); + expect(pubObj.subject.name).to.equal(subjectNA.name); + expect(pubObj.subject.tags.length).to.equal(0); + expect(pubObj.absolutePath).to.equal(subjectNA.name); + expect(pubObj.aspect.tags.length).to.equal(0); + + done(); + }) + .catch(done); + }); }); describe('getSampleEventType function tests: ', () => { @@ -115,5 +138,4 @@ describe('redis Publisher', () => { .catch(done); }); }); - });