Skip to content

Commit

Permalink
Refactor API, store reg settings, log Trello issue
Browse files Browse the repository at this point in the history
  • Loading branch information
srmoss committed Dec 16, 2016
1 parent 2ba26b4 commit 550e218
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 77 deletions.
44 changes: 16 additions & 28 deletions lib/api/notifications-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,56 +24,44 @@ function configure (app, wares, ctx) {
});
}

if(app.enabled('azurepush')) {
//Register device with Azure Notification Hub
if(app.enabled('azurepush') && ctx.azurepush) {
// Register device with Azure Notification Hub
api.post('/notifications/azure/register', function (req, res) {
ctx.azurepush.register(req.body, function registerCallback(error, response) {
if (error) {
console.log('Unable to register for Azure push notifications', error);
console.error('Unable to register for Azure push notifications', error);
res.sendStatus(500);
} else {
console.log('Registered for Azure push notifications');
console.info('Registered for Azure push notifications');
res.json(response);
}
});
});

//Unregister device with Azure Notification Hub
// Unregister device with Azure Notification Hub
api.post('/notifications/azure/unregister', function (req, res) {
ctx.azurepush.unregister(req.body, function unregisterCallback(error) {
if (error) {
console.log('Unable to unregister for Azure push notifications', error);
console.error('Unable to unregister for Azure push notifications', error);
res.sendStatus(500);
} else {
console.log('Unregistered for Azure push notifications');
console.info('Unregistered for Azure push notifications');
res.sendStatus(200);
}
});
});

//Acknowledge notifications
// TODO: Move to azurepush (now has ctx)
// Acknowledge notifications
api.post('/notifications/azure/ack', function (req, res) {
if (req && req.body && req.body.level) {
var level = Number(req.body.level);
var group = req.body.group || 'default';
var time = req.body.time && Number(req.body.time);

//Convert minutes to milliseconds
if (time) {
time = time * 60 * 1000;
ctx.azurepush.ack(req.body, function ackCallback(error, response) {
if (error) {
console.error('Unable to Ack Azure push notification', error);
res.sendStatus(500);
} else {
console.info('Ack Azure push notification, level: ', response.level, ', group: ', response.group, ', time: ', response.time);
res.sendStatus(200);
}

ctx.notifications.ack(level, group, time, true);

console.log('Ack Azure push notification, level: ', level, ', group: ', group, ', time: ', time);
res.sendStatus(200);
}
else {
//No level, required parameter
console.log('Unable to Ack Azure push notification', "Level required");
res.sendStatus(500);
}
});
});
}

Expand Down
244 changes: 195 additions & 49 deletions lib/plugins/azurepush.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,104 @@
var azure = require('azure-sb');
var crypto = require('crypto');
var levels = require('../levels');
var ObjectID = require('mongodb').ObjectID;
var trello = require('node-trello');
var trelloService = new trello('daaebf0eb5b5e19d9c84398d332c6b7f', 'e976d63638f2ecd2edec6f8fb56a46cb5cf2100b11101f4edf189376892a6415');

function init (env, ctx) {
var azurepush = { };

var notificationHubService = setupAzurePush(env);

azurepush.send = function send(notify, callback) {
//Set app specific sound for alerts, otherwise use generic notification sound (nothing in the sound attribute)
var sound = '';
if (notify.level == levels.URGENT)
{
sound = 'alarm2.mp3';
}
else if (notify.level == levels.WARN)
{
sound = 'alarm.mp3';
}

// TODO: We should generalize this across push platforms, pushover does the same thing
// TODO: Send a more meaningful category
var payload = {
eventName: notify.eventName
, group: notify.group
, key: notify.key
, level: notify.level
, message: notify.message
, sound: sound
, title: notify.title
, category: "event"
}

// Set tag based on level of the notification
// Set tag and query based on level of the notification
var tag, tagType = '-';
var query;
if (notify.isAnnouncement) {
tagType = tagType + 'announcement';
query = {'settings.announcement': true};
} else if (levels.isAlarm(notify.level)) {
tagType = tagType + 'alert';
query = {'settings.alert': true};
} else {
tagType = tagType + 'info';
query = {'settings.info': true};
}

tag = env.settings.azureTag + tagType;

// Send template notification, registration maps to platform specific notification
// http://azure.github.io/azure-sdk-for-node/azure-sb/latest/NotificationHubService.html#send
// send(tags, payload, [optionsopt], callback)
notificationHubService.send(tag, payload, callback);
// See if anyone is actually registered
ctx.store.collection(env.azurepush_collection).find(query).toArray(function findAnnouncement(err, docs) {
if (docs.length > 0) {
// At least one device has registered for this type of notification, actually send
// Azure handles only sending to the scpeific devices, we just need to send the level-specific notification

// Set app specific sound for alerts, otherwise use generic notification sound (nothing in the sound attribute)
var sound = '';
if (notify.level == levels.URGENT)
{
sound = 'alarm2.mp3';
}
else if (notify.level == levels.WARN)
{
sound = 'alarm.mp3';
}

// TODO: We should generalize this across push platforms, pushover does the same thing
// TODO: Send a more meaningful category
var payload = {
eventName: notify.eventName
, group: notify.group
, key: notify.key
, level: notify.level
, message: notify.message
, sound: sound
, title: notify.title
, category: 'event'
}

notificationHubService.send(tag, payload, function (error, response) {
// *****REMOVE*****: Beta logging
if (error) {
logTrelloIssue(env, '0x01-Failed to deliver notification to Azure');
}
// END: *****REMOVE*****

callback(error, response);
});
}
});
};

azurepush.unregister = function unregister(request, callback) {
if (request && request.installationId) {
ctx.store.collection(env.azurepush_collection).find({_id: request.installationId}).toArray(function findAzurepush(err, docs) {
if (docs.length > 0) {
ctx.store.collection(env.azurepush_collection).remove({_id: request.installationId}, function removeAzurepush(err, doc) {
// TODO: Handle error
console.log(err, doc);
});
// Remove registration from mongodb, this will ensure we don't call up to Azure if there are no devices registered
ctx.store.collection(env.azurepush_collection).remove({_id: request.installationId}, function removeAzurepush(err, doc) {
// *****REMOVE*****: Beta logging
if (err) {
logTrelloIssue(env, '0x02-Failed to remove registration from mongodb');
}
// END: *****REMOVE*****

// TODO: Handle error
console.log(err, doc);
});

// Remove installation from Azure, make sure that if we do call up to Azure, this device won't get a notification
notificationHubService.deleteInstallation(request.installationId, function (error, response) {
// *****REMOVE*****: Beta logging
if (error) {
logTrelloIssue(env, '0x03-Failed to delete installation from Azure');
}
// END: *****REMOVE*****

callback(error);
});
}
else {
// *****REMOVE*****: Beta logging
logTrelloIssue(env, '0x04-Unregister received without installationId');
// END: *****REMOVE*****

//No installationId, required parameter
return callback({message: 'InstallationId required'});
}
Expand Down Expand Up @@ -99,16 +132,60 @@ function init (env, ctx) {
// _id is unique, only ever one
if (docs.length > 0) {
ctx.store.collection(env.azurepush_collection).update({_id: entry._id}, {$set: {settings: entry.settings}}, function updateAzurepush(err, doc) {
// *****REMOVE*****: Beta logging
if (err) {
logTrelloIssue(env, '0x05-Failed to update existing registration in mongodb');
}
// END: *****REMOVE*****

// TODO: Handle error
console.log(err, doc);
});
} else {
ctx.store.collection(env.azurepush_collection).insert(entry, function storeAzurepush(err, doc) {
// *****REMOVE*****: Beta logging
if (err) {
logTrelloIssue(env, '0x06-Failed to add new registration in mongodb');
}
// END: *****REMOVE*****

// TODO: Handle error
console.log(err, doc);
});
}
});

// Verify/create index
ctx.store.collection(env.azurepush_collection).ensureIndex('settings.announcement', function indexAnnouncement(err, doc) {
// *****REMOVE*****: Beta logging
if (err) {
logTrelloIssue(env, '0x07-Failed to verify index on anncouncement in mongodb');
}
// END: *****REMOVE*****

// TODO: Handle error
console.log(err, doc);
});
ctx.store.collection(env.azurepush_collection).ensureIndex('settings.alert', function indexAnnouncement(err, doc) {
// *****REMOVE*****: Beta logging
if (err) {
logTrelloIssue(env, '0x08-Failed to verify index on alert in mongodb');
}
// END: *****REMOVE*****

// TODO: Handle error
console.log(err, doc);
});
ctx.store.collection(env.azurepush_collection).ensureIndex('settings.info', function indexAnnouncement(err, doc) {
// *****REMOVE*****: Beta logging
if (err) {
logTrelloIssue(env, '0x09-Failed to verify index on info in mongodb');
}
// END: *****REMOVE*****

// TODO: Handle error
console.log(err, doc);
});

// More info on request body: https://msdn.microsoft.com/en-us/library/mt621153.aspx
var installation = {
Expand All @@ -134,19 +211,59 @@ function init (env, ctx) {
case 'WinPhone': //Not yet supported
case 'Windows': //Not yet supported
default: //No platform specified, or invalid platform
// *****REMOVE*****: Beta logging
logTrelloIssue(env, '0x10-Register received without supported platform');
// END: *****REMOVE*****

return callback({message: 'Platform not supported'});
}

notificationHubService.createOrUpdateInstallation(installation, function (error, response) {
// *****REMOVE*****: Beta logging
if (error) {
logTrelloIssue(env, '0x11-Failed to create or update installation');
}
// END: *****REMOVE*****

callback(error, {installationId: installation.installationId});
});
}
else {
// *****REMOVE*****: Beta logging
logTrelloIssue(env, '0x12-Register received without platform');
// END: *****REMOVE*****

// No platform, required parameter
return callback({message: 'Platform required'});
}
};

azurepush.ack = function ack(request, callback) {
if (request && request.level) {
var response = {};
response.level = Number(request.level);
response.group = request.group || 'default';
response.time = request.time && Number(request.time);

//Convert minutes to milliseconds
if (response.time) {
response.time = response.time * 60 * 1000;
}

ctx.notifications.ack(response.level, response.group, response.time, true);

callback(null, response);
}
else {
// *****REMOVE*****: Beta logging
logTrelloIssue(env, '0x13-Ack received without level');
// END: *****REMOVE*****

//No level, required parameter
callback({message: 'Level required'});
}
};

if (notificationHubService) {
return azurepush;
}
Expand All @@ -157,21 +274,50 @@ function init (env, ctx) {

function setupAzurePush (env) {
if (env.settings.isEnabled('azurepush')) {
//TODO: Allow override
//env.extendedSettings.azurepush.hubName, env.extendedSettings.azurepush.connectionString
var notificationHubService = azure.createNotificationHubService('Push-Notification-Hub','Endpoint=sb://nspush.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=VdtFDvldTypVUgroq6JtrbI162upFh7MeWcpSdJu3yE=');

//Hash the baseURL for the tag
var tag = env.settings.baseURL;
var shasum = crypto.createHash('sha1');
shasum.update(tag);
env.settings.azureTag = shasum.digest('hex');

return notificationHubService;
}
else {
if (env.settings.baseURL) {
//TODO: Allow override
//env.extendedSettings.azurepush.hubName, env.extendedSettings.azurepush.connectionString
var notificationHubService = azure.createNotificationHubService('Push-Notification-Hub','Endpoint=sb://nspush.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=VdtFDvldTypVUgroq6JtrbI162upFh7MeWcpSdJu3yE=');

//Hash the baseURL for the tag
var tag = env.settings.baseURL;
var shasum = crypto.createHash('sha1');
shasum.update(tag);
env.settings.azureTag = shasum.digest('hex');

return notificationHubService;
} else {
// No baseURL, cannot create azureTag
// *****REMOVE*****: Beta logging
logTrelloIssue(env, '0x14-Azure enabled but no BASE_URL');
// END: *****REMOVE*****

console.error('Azure push notifications enabled but no BASE_URL set, notifications will not be sent');
return null;
}
} else {
return null;
}
}

function logTrelloIssue (env, message) {
var trace = {};
Error.captureStackTrace(trace);

var card = {
name: env.settings.baseURL + ': ' + message
, desc: trace.stack
, idList: '58539ab3416027457b1238c0'
};

trelloService.post('/1/cards', card, function trelloCallback(err, data) {
if (err) {
console.error('Trello card creation failed: ', err);
}
else {
console.info('Logged Trello issue: ', data);
}
});
}

module.exports = init;
Loading

0 comments on commit 550e218

Please sign in to comment.