Skip to content

Commit

Permalink
Merge pull request #12 from duboisp/master
Browse files Browse the repository at this point in the history
Moved the sub-document in its own collection
  • Loading branch information
GormFrank authored Mar 27, 2020
2 parents 10d481c + ae63129 commit 58f059e
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 91 deletions.
229 changes: 154 additions & 75 deletions controllers/subscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const NotifyClient = require('notifications-node-client').NotifyClient; // https

const dbConn = module.parent.exports.dbConn;

const notifyCached = [];
let notifyCached = [],
notifyCachedIndexes = [];

const processEnv = process.env,
_errorPage = processEnv.errorPage || "https://canada.ca",
Expand All @@ -19,9 +20,15 @@ const processEnv = process.env,
_sErrorsJSO = processEnv.sErrorsJSO || { statusCode: 500, err: 1 },
_notifyEndPoint = processEnv.notifyEndPoint || "https://api.notification.alpha.canada.ca",
_confirmBaseURL = processEnv.confirmBaseURL || "https://apps.canada.ca/x-notify/subs/confirm/",
nbMinutesBF = processEnv.notSendBefore || 25; // Default of 25 minutes.


nbMinutesBF = processEnv.notSendBefore || 25, // Default of 25 minutes.
_topicCacheLimit = processEnv.topicCacheLimit || 50,
_notifyCacheLimit = processEnv.notifyCacheLimit || 40,
_flushAccessCode = processEnv.flushAccessCode,
_flushAccessCode2 = processEnv.flushAccessCode2;

let topicCached = [],
topicCachedIndexes = [];

//
// Add email to the newSubscriberEmail
//
Expand All @@ -41,73 +48,59 @@ exports.addEmail = async ( req, res, next ) => {
return;
}

// Select Topic where email $nin "subs"
const colTopic = dbConn.collection( "topics" );
// Get the topic
const topic = await getTopic( topicId );

try {
const topic = await colTopic.findOne(
{ _id: topicId, subs: { $nin: [ email ] } },
{ projection: {
_id: 0,
templateId: 1,
notifyKey: 1,
confirmURL: 1
}
} ); // fyi - return null when there is no result.

// If email found, try to resend
if (! topic ) {

return resendEmailNotify( email, topicId, currDate )
.then( () => {
res.json( _successJSO );
} )
.catch( ( e ) => {
// answer a negative JSON response + reason code
console.log( e );
res.json( _sErrorsJSO );
} );
if ( !topic ) {
res.json( _sErrorsJSO );
return true;
}

// Generate an simple Unique Code
const confirmCode = Math.floor(Math.random() * 999999) + "" + currDate.getMilliseconds(),
tId = topic.templateId,
nKey = topic.notifyKey;

// Insert in subsToConfirm
await dbConn.collection( "subsUnconfirmed" ).insertOne( {
email: email,
subscode: confirmCode,
topicId: topicId,
notBefore: currDate.setMinutes( currDate.getMinutes() + nbMinutesBF ),
createAt: currDate,
tId: tId,
nKey: nKey,
cURL: topic.confirmURL
});

// Update - Add to topic subs array
await colTopic.updateOne(
{ _id: topicId },
// Check if the email is in the "SubsExist"
await dbConn.collection( "subsExist" ).insertOne(
{
$push: {
subs: email
}
e: email,
t: topicId
}).then( () => {

// The email is not subscribed for that topic
// Generate an simple Unique Code
const confirmCode = Math.floor(Math.random() * 999999) + "" + currDate.getMilliseconds(),
tId = topic.templateId,
nKey = topic.notifyKey;

// Insert in subsToConfirm
dbConn.collection( "subsUnconfirmed" ).insertOne( {
email: email,
subscode: confirmCode,
topicId: topicId,
notBefore: currDate.setMinutes( currDate.getMinutes() + nbMinutesBF ),
createAt: currDate,
tId: tId,
nKey: nKey,
cURL: topic.confirmURL
});

// Send confirm email - async
sendNotifyConfirmEmail( email, confirmCode, tId, nKey );

res.json( _successJSO );
}).catch( () => {

// The email was either subscribed-pending or subscribed confirmed
resendEmailNotify( email, topicId, currDate );

res.json( _successJSO );
});

// Send confirm email
sendNotifyConfirmEmail( email, confirmCode, tId, nKey );

res.json( _successJSO );

} catch ( e ) {

// The query has not ran
console.log( e );


// Topic requested don't exist
res.json( _sErrorsJSO );
}


};


Expand Down Expand Up @@ -191,9 +184,18 @@ exports.removeEmail = ( req, res, next ) => {
// findOneAndDeleted in subsConfirmedEmail document
dbConn.collection( "subsConfirmed" )
.findOneAndDelete( { email: email, subscode: subscode } )
.then( ( docSubs ) => {
.then( async ( docSubs ) => {


const topicId = docSubs.value.topicId;
const topic = await getTopic( topicId );

if ( !topic ) {
res.redirect( _errorPage );
return true;
}

const unsubLink = topic.unsubURL;

// subs_logs entry - this can be async
dbConn.collection( "subs_logs" ).updateOne(
Expand All @@ -214,23 +216,27 @@ exports.removeEmail = ( req, res, next ) => {
console.log( e );
});

// update topics
dbConn.collection( "topics" ).findOneAndUpdate(
{ _id: topicId },
{
$pull: {
subs: email
}
},
// Create entry in unsubs
dbConn.collection( "subsUnsubs" ).insertOne(
{
c: currDate,
e: email,
t: topicId
});

// Remove from subsExits
await dbConn.collection( "subsExist" ).findOneAndDelete(
{
projection: { unsubURL: 1 }
}).then( ( docTp ) => {
e:email,
t: topicId
}).then( ( ) => {

// Redirect to Generic page to confirm the email is removed
res.redirect( docTp.value.unsubURL );
res.redirect( unsubLink );

}).catch( ( e ) => {
console.log( e );
res.redirect( _errorPage );
});

} ).catch( () => {
Expand All @@ -247,6 +253,34 @@ exports.getAll = (confirmCode, email, phone) => {



//
// Flush the topic and notify cache
//
// @return; an HTML blob
//
exports.flushCache = ( req, res, next ) => {

const { accessCode, topicId } = req.params;

if ( accessCode !== _flushAccessCode || topicId !== _flushAccessCode2 ||
!_flushAccessCode || !_flushAccessCode2 ) {

res.json( _sErrorsJSO );
}

// Flush topic
topicCachedIndexes = [];
topicCached = [];

// Flush notify client
notifyCachedIndexes = [];
notifyCached = [];

// Return success
res.json( _successJSO );

};

//
// Resend email notify
//
Expand All @@ -255,7 +289,7 @@ resendEmailNotify = ( email, topicId, currDate ) => {
// Find email in
return dbConn.collection( "subsUnconfirmed" )
.findOneAndUpdate(
{ email: email, notBefore: { $lt: currDate.getTime() } },
{ topicId: topicId, email: email, notBefore: { $lt: currDate.getTime() } },
{
$set: {
notBefore: currDate.setMinutes( currDate.getMinutes() + nbMinutesBF )
Expand Down Expand Up @@ -311,11 +345,19 @@ sendNotifyConfirmEmail = async ( email, confirmCode, templateId, NotifyKey ) =>
// There is 1 personalisation, the confirm links
// /subs/confirm/:subscode/:email

let notifyClient = notifyCached[ templateId ];
let notifyClient = notifyCached[ NotifyKey ];


if ( !notifyClient ) {
notifyClient = new NotifyClient( _notifyEndPoint, NotifyKey );
notifyCached[ templateId ] = notifyClient;
notifyCached[ NotifyKey ] = notifyClient;
notifyCachedIndexes.push( NotifyKey );

// Limit the cache to the last x instance of Notify
if ( notifyCachedIndexes.length > _notifyCacheLimit ) {
delete notifyCached[ notifyCachedIndexes.shift() ];
}

}


Expand Down Expand Up @@ -356,7 +398,44 @@ sendNotifyConfirmEmail = async ( email, confirmCode, templateId, NotifyKey ) =>
});
}

//
// Get topic info
//

// Get the topic
getTopic = ( topicId ) => {

let topic = topicCached[ topicId ];

if ( !topic ) {

topic = dbConn.collection( "topics" ).findOne(
{ _id: topicId },
{ projection: {
_id: 1,
templateId: 1,
notifyKey: 1,
confirmURL: 1,
unsubURL: 1
}
} ).catch( (e) => {
console.log( e );
return false;
});

topicCached[ topicId ] = topic;
topicCachedIndexes.push( topicId );

// Limit the cache to the last x topics
if ( topicCachedIndexes.length > _topicCacheLimit ) {
delete topicCached[ topicCachedIndexes.shift() ];
}

}

return topic;

}



12 changes: 6 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ For each topic you need:

## Topic naming convention

<2-3 letter group name abbreviation>-<short topic name><2 letter language>
`{2-3 letter group name abbreviation}-{short topic name}{2 letter language}`

### database command sample

```
db.topics.insertOne( {
_id: "test2",
subs: [],
templateId: "<template id available in the template in Notify>",
notifyKey: "<A valid Notify API key>",
confirmURL: "https://canada.ca/en.html",
Expand Down Expand Up @@ -64,6 +63,11 @@ You need to:
* Ask the client to send us a Live Notify API Key via an encrypted communication
* Replace the Notify API key

### Updating an existing topic

1. Run the appropriate MongoDB query
2. Flush the cache: /api/v0.1/t-manager/{Private access code 1}/{Private access code 2}/flush-cache

### Topic

A topic could be compared to a mailing list.
Expand All @@ -72,10 +76,6 @@ A topic could be compared to a mailing list.

Raw string identifier for a topic. Could be a keyword.

`subs`

Array of strings which are emails subscribed to the list. Emails part of that list include confirmed and unconfirmed emails.

**Updates when:** A user fills out the form to subscribe to a topic.

`templateId`
Expand Down
2 changes: 1 addition & 1 deletion server.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ MongoClient.connect( processEnv.MONGODB_URI || '', {} ).then( ( mongoInstance )
* Admin routes.
*/
// app.get('/subs/remove_unconfirm/:subscode/:email', subsController.removeUnconfirmEmail);

app.get('/api/v0.1/t-manager/:accessCode/:topicId/flush-cache', subsController.flushCache);

/**
* Error Handler.
Expand Down
Loading

0 comments on commit 58f059e

Please sign in to comment.