diff --git a/src/server/db/MongoServerDao.js b/src/server/db/MongoServerDao.js index 48d12df..20fe8be 100644 --- a/src/server/db/MongoServerDao.js +++ b/src/server/db/MongoServerDao.js @@ -783,5 +783,70 @@ module.exports = Class( 'MongoServerDao' ) } ); }, -} ); + + /** + * Set arbitrary data on a document + * + * @param {number} qid quote/document id + * @param {string} key field key + * @param {*} value field value + * @param {function(?Error)} callback completion callback + * + * @return {undefined} + */ + 'public setDocumentField'( qid, key, value, callback ) + { + this._collection.update( + { id: qid }, + { '$set': { [key]: value } }, + + // create record if it does not yet exist + { upsert: true }, + + // on complete + function( err ) + { + callback && callback( err ); + return; + } + ); + }, + + + /** + * Retrieve arbitrary data on a document + * + * @param {number} qid quote/document id + * @param {string} key field key + * @param {function(?Error)} callback completion callback + * + * @return {undefined} + */ + 'public getDocumentField'( qid, key, callback ) + { + this._collection.find( + { id: qid }, + { limit: 1 }, + function( err, cursor ) + { + if ( err !== null ) + { + callback( err, null ); + return; + } + + cursor.toArray( function( err, data ) + { + if ( err !== null ) + { + callback( err, null ); + return; + } + + callback( null, data[ key ] ); + } ); + } + ); + }, +} ); diff --git a/src/server/service/RatingServiceSubmitNotify.js b/src/server/service/RatingServiceSubmitNotify.js index 7a2c689..cfcf210 100644 --- a/src/server/service/RatingServiceSubmitNotify.js +++ b/src/server/service/RatingServiceSubmitNotify.js @@ -32,6 +32,10 @@ const RatingService = require( './RatingService' ); * This information is currently stored in `__prem_avail_count`. In the * future, it may be worth accepting a parameter to configure this at * runtime. + * + * Notification status will persist using the provided DAO. The next time + * such a notification is requested, it will only occur if the flag is not + * set. */ module.exports = Trait( 'RatingServiceSubmitNotify' ) .extend( RatingService, @@ -42,15 +46,23 @@ module.exports = Trait( 'RatingServiceSubmitNotify' ) */ 'private _dapi': null, + /** + * Data store for notification flag + * @type {ServerDao} + */ + 'private _notifyDao': null, + /** * Initialize mixin with DataApi to trigger * - * @param {DataApi} dapi DataApi to trigger + * @param {DataApi} dapi DataApi to trigger + * @param {ServerDao} dao data store for notification flag */ - __mixin( dapi ) + __mixin( dapi, dao ) { - this._dapi = dapi; + this._dapi = dapi; + this._notifyDao = dao; }, @@ -73,9 +85,51 @@ module.exports = Trait( 'RatingServiceSubmitNotify' ) if ( avail === 0 ) { - this._dapi.request( { quote_id: quote_id }, () => {} ); + this._getNotifyState( quote_id, notified => + { + if ( notified === true ) + { + return; + } + + this._dapi.request( { quote_id: quote_id }, () => {} ); + this._setNotified( quote_id ); + } ); } this.__super( data, actions, program, quote ); }, + + + /** + * Get value of notification flag + * + * @param {number} quote_id id of quote + * @param {function(boolean)} callback callback to call when complete + * + * @return {undefined} + */ + 'private _getNotifyState'( quote_id, callback ) + { + this._notifyDao.getDocumentField( + quote_id, + 'submitNotified', + ( err, value ) => callback( value ) + ); + }, + + + /** + * Set notification flag + * + * @param {number} quote_id id of quote + * + * @return {undefined} + */ + 'private _setNotified'( quote_id ) + { + this._notifyDao.setDocumentField( + quote_id, 'submitNotified', true + ); + }, } ); diff --git a/test/server/service/RatingServiceSubmitNotifyTest.js b/test/server/service/RatingServiceSubmitNotifyTest.js index dc0605b..2a23107 100644 --- a/test/server/service/RatingServiceSubmitNotifyTest.js +++ b/test/server/service/RatingServiceSubmitNotifyTest.js @@ -50,18 +50,39 @@ describe( 'RatingServiceSubmitNotify', () => [ { prem_avail_count: [ 0 ], + prev_called: false, expected_request: true, }, { prem_avail_count: [ 2 ], + prev_called: false, expected_request: false, }, { // this shouldn't happen; ignore all but first index prem_avail_count: [ 2, 2 ], + prev_called: false, expected_request: false, }, - ].forEach( ( { prem_avail_count, expected_request }, i ) => + + // save as above, but already saved + { + prem_avail_count: [ 0 ], + prev_called: true, + expected_request: false, + }, + { + prem_avail_count: [ 2 ], + prev_called: true, + expected_request: false, + }, + { + // this shouldn't happen; ignore all but first index + prem_avail_count: [ 2, 2 ], + prev_called: true, + expected_request: false, + }, + ].forEach( ( { prem_avail_count, expected_request, prev_called }, i ) => it( `sends request on post process if no premiums (#${i})`, done => { const { @@ -91,7 +112,7 @@ describe( 'RatingServiceSubmitNotify', () => }, } )(); - const sut = RatingService.use( Sut( dapi ) )( + const sut = RatingService.use( Sut( dapi, dao ) )( logger, dao, server, raters ); @@ -101,6 +122,27 @@ describe( 'RatingServiceSubmitNotify', () => let save_called = false; dao.setWorksheets = () => save_called = true; + // whether the notify flag is actually set + let notify_saved = false; + + // request for notification status + dao.getDocumentField = ( qid, key, callback ) => + { + expect( qid ).to.equal( quote_id ); + expect( key ).to.equal( 'submitNotified' ); + + callback( null, prev_called ); + }; + + dao.setDocumentField = ( qid, key, value, callback ) => + { + expect( qid ).to.equal( quote_id ); + expect( key ).to.equal( 'submitNotified' ); + expect( value ).to.equal( true ); + + notify_saved = true; + }; + stub_rate_data.__prem_avail_count = prem_avail_count; sut.request( request, response, quote, 'something', () => @@ -108,6 +150,11 @@ describe( 'RatingServiceSubmitNotify', () => expect( requested ).to.equal( expected_request ); expect( save_called ).to.be.true; + // only save notification status if we're notifying + expect( notify_saved ).to.equal( + !prev_called && expected_request + ); + done(); } ); } )