Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions api/v1/helpers/nouns/samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'];

Expand All @@ -33,4 +36,10 @@ module.exports = {
fieldsWithJsonArrayType,
fieldsWithEnum,
fieldsToExclude,
publishEvents: true,
associatedModels: {
aspect: Aspect,
subject: Subject,
},

}; // exports
8 changes: 8 additions & 0 deletions api/v1/helpers/verbs/doDelete.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions api/v1/helpers/verbs/doPatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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));
})
Expand Down
9 changes: 9 additions & 0 deletions api/v1/helpers/verbs/doPost.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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));
})
Expand Down
9 changes: 9 additions & 0 deletions api/v1/helpers/verbs/doPut.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions api/v1/helpers/verbs/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ function checkDuplicateRLinks(rLinkArr) {
// ----------------------------------------------------------------------------

module.exports = {

realtimeEvents,

publisher,
Expand Down
52 changes: 4 additions & 48 deletions db/model/sample.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we test for whether a sample is published on realtime create and delete?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is another story for this.

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.
Expand Down
35 changes: 26 additions & 9 deletions realtime/redisPublisher.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

case insensitive?

},
};
const aspOpts = {
where: {
name: aspName,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

case insensitive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aspectName and subjectName are always from the "sample.name" field from the db and the "sample.name" field is formed from its subject and its aspect in the db. So the case always matches when we do the find here.

},
};
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);
}
}
Expand Down
26 changes: 24 additions & 2 deletions tests/realtime/redisPublisher.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

/**
* tests/realtime/setupSocketIO.js
* tests/realtime/redisPublisher.js
*/
'use strict';

Expand Down Expand Up @@ -81,6 +81,29 @@ describe('redis Publisher', () => {
})
.catch(done);
});

it('when tried to publish sample without aspect,'+
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any other tests we can add here to make sure that we aren't breaking any of the existing realtime event publishing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sample object the is finally passed to the real-time emitter should have the following fields/objects to not to break anything

  1. absolutePath - as a field copied over from subject absolutePath
  2. subject object always attached - having tags field
  3. aspect object always attached having tags field
    I have added tests to check for these.

' 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: ', () => {
Expand Down Expand Up @@ -115,5 +138,4 @@ describe('redis Publisher', () => {
.catch(done);
});
});

});