rework notifications

notifications are now system level instead of user level.

To clarify the use events/notifications/email:
* eventlog - everything that is happenning on server
* notifications - specific important events (alerts)
* email - these are really urgent things that require immediate attention. this is for
  the case where an admin does not visit the dashboard often. can also be alerts like
  bad backup config or reboot required which are not events per-se.

Notes on notifications
* oom - notification only
* appUpdated - notification only
* cert renewal failure - only raise when < 10 days to go. also send email thereafter (todo).
* Backup failure - only if last 5 backups failed (todo).
* Box update - notification only. we anyway send newsletter.
* box update available - we raise a notification. no email.
* app update available - we already have update indicator on dashboard. so, no notification or email.

Alerts:
* backup config
* disk space
* mail status
* reboot
* box updated
* ubuntu update required
This commit is contained in:
Girish Ramakrishnan
2021-05-28 14:34:18 -07:00
parent 3ba62f2ba1
commit 73917e95c9
18 changed files with 219 additions and 959 deletions

View File

@@ -6,51 +6,24 @@
'use strict';
var async = require('async'),
const async = require('async'),
BoxError = require('../boxerror.js'),
database = require('../database.js'),
users = require('../users.js'),
userdb = require('../userdb.js'),
eventlogdb = require('../eventlogdb.js'),
expect = require('expect.js'),
notifications = require('../notifications.js'),
expect = require('expect.js');
safe = require('safetydance');
// owner
var USER_0 = {
username: 'username0',
password: 'Username0pass?1234',
email: 'user0@email.com',
fallbackEmail: 'user0fallback@email.com',
displayName: 'User 0',
role: 'owner'
};
var EVENT_0 = {
const EVENT_0 = {
id: 'event_0',
action: '',
action: 'action',
source: {},
data: {}
};
var AUDIT_SOURCE = {
ip: '1.2.3.4'
};
function setup(done) {
async.series([
database.initialize,
database._clear,
users.createOwner.bind(null, USER_0.username, USER_0.password, USER_0.email, USER_0.displayName, AUDIT_SOURCE),
function (callback) {
userdb.getByUsername(USER_0.username, function (error, result) {
if (error) return callback(error);
USER_0.id = result.id;
callback();
});
},
eventlogdb.add.bind(null, EVENT_0.id, EVENT_0.action, EVENT_0.source, EVENT_0.data),
], done);
}
@@ -65,114 +38,59 @@ describe('Notifications', function () {
before(setup);
after(cleanup);
var notificationId;
let notificationIds = [];
it('add succeeds', function (done) {
notifications._add(USER_0.id, EVENT_0.id, 'title', 'message text', function (error, result) {
expect(error).to.eql(null);
expect(result.id).to.be.ok();
notificationId = result.id;
done();
});
it('can add notifications', async function () {
for (let i = 0; i < 3; i++) {
const [error, id] = await safe(notifications._add(EVENT_0.id, `title ${i}`, `message ${i}`));
expect(error).to.equal(null);
expect(id).to.be.a('string');
notificationIds.push(id);
}
});
it('get succeeds', function (done) {
notifications.get(notificationId, function (error, result) {
expect(error).to.eql(null);
expect(result.id).to.equal(notificationId);
expect(result.title).to.equal('title');
expect(result.message).to.equal('message text');
expect(result.acknowledged).to.equal(false);
expect(result.creationTime).to.be.a(Date);
done();
});
it('can get by id', async function () {
const [error, result] = await safe(notifications.get(notificationIds[0]));
expect(error).to.be(null);
expect(result.title).to.be('title 0');
expect(result.message).to.be('message 0');
expect(result.acknowledged).to.be(false);
});
it('get of unknown id fails', function (done) {
notifications.get('notfoundid', function (error, result) {
expect(error).to.be.a(BoxError);
expect(error.reason).to.be(BoxError.NOT_FOUND);
expect(result).to.not.be.ok();
done();
});
it('cannot get non-existent id', async function () {
const result = await notifications.get('random');
expect(result).to.be(null);
});
it('ack succeeds', function (done) {
notifications.ack(notificationId, function (error) {
expect(error).to.eql(null);
notifications.get(notificationId, function (error, result) {
expect(error).to.eql(null);
expect(result.acknowledged).to.equal(true);
done();
});
});
it('can list notifications', async function () {
const result = await notifications.list({}, 1, 10);
expect(result.length).to.be(3);
expect(result[0].title).to.be('title 0');
expect(result[1].title).to.be('title 1');
expect(result[2].title).to.be('title 2');
});
it('ack succeeds twice', function (done) {
notifications.ack(notificationId, function (error) {
expect(error).to.eql(null);
it('can update notification', async function () {
await notifications.update({ id: notificationIds[0] }, { title: 'updated title 0', message: 'updated message 0', acknowledged: true });
notifications.get(notificationId, function (error, result) {
expect(error).to.eql(null);
expect(result.acknowledged).to.equal(true);
done();
});
});
const result = await notifications.get(notificationIds[0]);
expect(result.title).to.be('updated title 0');
expect(result.message).to.be('updated message 0');
expect(result.acknowledged).to.be(true);
});
it('ack fails for nonexisting id', function (done) {
notifications.ack('id does not exist', function (error) {
expect(error).to.be.a(BoxError);
expect(error.reason).to.be(BoxError.NOT_FOUND);
it('cannot update non-existent notification', async function () {
const [error] = await safe(notifications.update({ id: 'random' }, { title: 'updated title 0', message: 'updated message 0', acknowledged: true }));
expect(error.reason).to.be(BoxError.NOT_FOUND);
done();
});
});
it('getAllPaged succeeds', function (done) {
notifications.getAllPaged(USER_0.id, null, 1, 1, function (error, results) {
expect(error).to.eql(null);
expect(results).to.be.an(Array);
expect(results.length).to.be(1);
expect(results[0].id).to.be(notificationId);
expect(results[0].title).to.equal('title');
expect(results[0].message).to.equal('message text');
expect(results[0].acknowledged).to.equal(true);
expect(results[0].creationTime).to.be.a(Date);
done();
});
it('can delete', async function () {
await notifications.del(notificationIds[0]);
});
it('getAllPaged succeeds for second page (takes 5 seconds to add)', function (done) {
async.timesSeries(5, function (n, callback) {
// timeout is for database TIMESTAMP resolution
setTimeout(function () {
notifications._add(USER_0.id, EVENT_0.id, 'title' + n, 'some message', callback);
}, 1000);
}, function (error) {
expect(error).to.eql(null);
notifications.getAllPaged(USER_0.id, null /* ack */, 2, 3, function (error, results) {
expect(error).to.eql(null);
expect(results).to.be.an(Array);
expect(results.length).to.be(3);
expect(results[0].title).to.equal('title1');
expect(results[1].title).to.equal('title0');
// the previous tests already add one notification with 'title'
expect(results[2].title).to.equal('title');
done();
});
});
it('cannot delete non-existent notification', async function () {
const [error] = await safe(notifications.del('random'));
expect(error.reason).to.be(BoxError.NOT_FOUND);
});
});