-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add GlobalNotifier to the app (#100)
https://eaflood.atlassian.net/browse/WATER-3833 Whilst working on [Flag bill runs as errored on CHA failure](#99) we realised that by no longer throwing an error and sending them back via [Boom](https://hapi.dev/module/boom/) we'd lose any logs or notifications that the request to the Charge Module had failed. We want to log this and send a notification to our [Errbit](https://github.com/errbit/errbit) instance. Looking at what we have though, all our [notification](app/lib) logic is tied up with the [Hapi request](https://hapi.dev/api/?v=21.1.0#request). We don't need to know the specific request which failed in this case, as it avoids passing it around. But we also want to avoid instantiating both the [Airbrake notifier](https://github.com/airbrake/airbrake-js/tree/master/packages/node) and [Pino](https://github.com/pinojs/hapi-pino) every time we want to log something. So, as an example, our [RequestNotifierPlugin](app/plugins/request-notifier.plugin.js) takes advantage of the work the [AirbrakePlugin](app/plugins/airbrake.plugin.js) does to set up the Airbrake notifier. We'll add a plugin that does something similar but adds the 'notifier' (our name for something that can both log and send something to Errbit) to the [Node global namespace](https://nodejs.org/api/globals.html#globals_global) instead of the request. We'll then have something we can reuse anywhere whilst instantiating only once per process. We're aware of the 'never use globals' rule. But as with all rules, there is always an exception!
- Loading branch information
1 parent
081167a
commit 27115b0
Showing
8 changed files
with
231 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
'use strict' | ||
|
||
/** | ||
* @module GlobalNotifierLib | ||
*/ | ||
|
||
const BaseNotifierLib = require('./base-notifier.lib.js') | ||
|
||
/** | ||
* A combined logging and Airbrake (Errbit) notification manager for actions that take place outside of a | ||
* {@link https://hapi.dev/api/?v=20.1.2#request|Hapi request} | ||
* | ||
* Created for use with the `app/plugins/global-notifier.plugin.js`. | ||
*/ | ||
class GlobalNotifierLib extends BaseNotifierLib { | ||
constructor (logger, notifier) { | ||
// This is here more to make it clear that we expect these args to be provided. BaseNotifierLib has the built-in | ||
// ability to instantiate them if not provided. But for our use case in global-notifier.plugin.js we want to ensure | ||
// we are using the existing instances. | ||
if (!logger || !notifier) { | ||
throw new Error('new instance of GlobalNotifierLib is missing a required argument') | ||
} | ||
|
||
super(logger, notifier) | ||
} | ||
|
||
_formatLogPacket (message, data) { | ||
return { | ||
message, | ||
...data | ||
} | ||
} | ||
|
||
_formatNotifyPacket (message, data) { | ||
return { | ||
message, | ||
session: data | ||
} | ||
} | ||
} | ||
|
||
module.exports = GlobalNotifierLib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
'use strict' | ||
|
||
/** | ||
* Plugin to add a globally available notifier for logging and sending exceptions to Errbit | ||
* @module GlobalNotifierPlugin | ||
*/ | ||
|
||
const GlobalNotifierLib = require('../lib/global-notifier.lib.js') | ||
|
||
const GlobalNotifierPlugin = { | ||
name: 'global-notifier', | ||
register: (server, _options) => { | ||
global.GlobalNotifier = new GlobalNotifierLib(server.logger, server.app.airbrake) | ||
} | ||
} | ||
|
||
module.exports = GlobalNotifierPlugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
'use strict' | ||
|
||
// Test framework dependencies | ||
const Lab = require('@hapi/lab') | ||
const Code = require('@hapi/code') | ||
const Sinon = require('sinon') | ||
|
||
const { describe, it, beforeEach, afterEach } = exports.lab = Lab.script() | ||
const { expect } = Code | ||
|
||
// Things we need to stub | ||
const BaseNotifierLib = require('../../app/lib/base-notifier.lib.js') | ||
|
||
// Thing under test | ||
const GlobalNotifierLib = require('../../app/lib/global-notifier.lib.js') | ||
|
||
describe('GlobalNotifierLib class', () => { | ||
let airbrakeFake | ||
let pinoFake | ||
|
||
beforeEach(async () => { | ||
airbrakeFake = { notify: Sinon.fake.resolves({ id: 1 }), flush: Sinon.fake() } | ||
Sinon.stub(BaseNotifierLib.prototype, '_setNotifier').returns(airbrakeFake) | ||
|
||
pinoFake = { info: Sinon.fake(), error: Sinon.fake() } | ||
Sinon.stub(BaseNotifierLib.prototype, '_setLogger').returns(pinoFake) | ||
}) | ||
|
||
afterEach(() => { | ||
Sinon.restore() | ||
}) | ||
|
||
describe('#constructor', () => { | ||
describe("when the 'logger' argument is not provided", () => { | ||
it('throws an error', () => { | ||
expect(() => new GlobalNotifierLib(null, airbrakeFake)).to.throw() | ||
}) | ||
}) | ||
|
||
describe("when the 'notifier' argument is not provided", () => { | ||
it('throws an error', () => { | ||
expect(() => new GlobalNotifierLib(pinoFake)).to.throw() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('when a log entry is made', () => { | ||
const id = '1234567890' | ||
const message = 'say what test' | ||
|
||
it('formats it as expected', () => { | ||
const expectedArgs = { | ||
message, | ||
id | ||
} | ||
const testNotifier = new GlobalNotifierLib(pinoFake, airbrakeFake) | ||
testNotifier.omg(message, { id }) | ||
|
||
expect(pinoFake.info.calledOnceWith(expectedArgs)).to.be.true() | ||
}) | ||
}) | ||
|
||
describe('when an airbrake notification is sent', () => { | ||
const message = 'hell no test' | ||
const data = { offTheChart: true } | ||
|
||
it('formats it as expected', () => { | ||
const expectedArgs = { | ||
message, | ||
session: { | ||
...data | ||
} | ||
} | ||
const testNotifier = new GlobalNotifierLib(pinoFake, airbrakeFake) | ||
testNotifier.omfg(message, data) | ||
|
||
expect(airbrakeFake.notify.calledOnceWith(expectedArgs)).to.be.true() | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
'use strict' | ||
|
||
// Test framework dependencies | ||
const Lab = require('@hapi/lab') | ||
const Code = require('@hapi/code') | ||
|
||
const { describe, it, beforeEach } = exports.lab = Lab.script() | ||
const { expect } = Code | ||
|
||
// Test helpers | ||
const GlobalNotifierLib = require('../../app/lib/global-notifier.lib.js') | ||
|
||
// For running our service | ||
const { init } = require('../../app/server.js') | ||
|
||
describe('Global Notifier plugin', () => { | ||
beforeEach(async () => { | ||
// Create server before each test | ||
await init() | ||
}) | ||
|
||
describe('Global Notifier Plugin', () => { | ||
describe('when the server is initialised', () => { | ||
it('makes an instance of GlobalNotifierLib available globally', async () => { | ||
const result = global.GlobalNotifier | ||
|
||
expect(result).to.be.an.instanceOf(GlobalNotifierLib) | ||
}) | ||
}) | ||
}) | ||
}) |