Skip to content

Commit cebf971

Browse files
Make hook configurable (#8)
* not needed * updated unit tests and readme * contains breaking changes
1 parent 4e14656 commit cebf971

File tree

7 files changed

+168
-129
lines changed

7 files changed

+168
-129
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,12 @@ const fastify = require('fastify');
2727
fastify.register(fastifyHttpContextPlugin, { defaults: user: { id: 'system' } };
2828
```
2929
30-
This plugin takes in a single option named `defaults`. These are what the values should be if not set. This is optional and not necessary. There are cases where defaults are not wanted nor
31-
necessary.
30+
This plugin takes in one of two option named `defaults` and `hook`.
31+
32+
`defaults` are what the values should be if not set. This is optional and not necessary. There are cases where defaults are not wanted nor
33+
necessary.
34+
35+
`hook` is one of the fastify lifecycle hooks. By default it is set to `onRequest`.
3236
3337
From there you can set a context in another hook, route, or method that is within scope. For instance:
3438
@@ -38,6 +42,7 @@ const { fastifyHttpContextPlugin, httpContext } = require('fastify-http-context'
3842
const fastify = require('fastify')();
3943

4044
fastify.register(fastifyHttpContextPlugin, {
45+
hook: 'onRequest', // this is optional. If not set will default to onRequest
4146
defaults: {
4247
user: {
4348
id: 'system'

index.js

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,40 @@ const httpContext = {
77
set: fastAls.set
88
};
99

10-
/**
11-
* @deprecated
12-
* @param {*} key
13-
*/
14-
const getContext = (key) => {
15-
return httpContext.get(key);
16-
};
1710

18-
/**
19-
* @deprecated - Please use httpContext instead
20-
* @param {*} key
21-
* @param {*} value
22-
*/
23-
const setContext = (key, value) => {
24-
httpContext.set(key, value);
25-
};
11+
const validHooks = [
12+
'onRequest',
13+
'preParsing',
14+
'preValidation',
15+
'preHandler',
16+
'preSerialization',
17+
'onError',
18+
'onSend',
19+
'onResponse'
20+
];
21+
22+
function plugin(fastify, opts, next) {
23+
const defaults = opts.defaults || {};
24+
const hook = opts.hook || 'onRequest';
25+
26+
if (!validHooks.includes(hook)) {
27+
fastify.log.error(`${hook} is not a valid fastify hook. Please use one of the following ${validHooks}`);
28+
}
2629

27-
function plugin (fastify, opts, next) {
28-
const defaults = opts.defaults || {};
29-
fastify.addHook('onRequest', (req, res, done) => {
30-
fastAls.runWith(defaults, () => {
31-
done();
30+
31+
fastify.addHook('onRequest', (req, res, done) => {
32+
fastAls.runWith(defaults, () => {
33+
done();
34+
});
3235
});
33-
});
34-
next();
36+
next();
3537
}
3638

3739
module.exports = {
38-
httpContext: httpContext,
39-
setContext: setContext,
40-
getContext: getContext,
41-
fastifyHttpContextPlugin: fp(plugin, {
42-
fastify: '2.x',
43-
name: 'fastify-http-context'
44-
})
40+
validHooks: validHooks,
41+
httpContext: httpContext,
42+
fastifyHttpContextPlugin: fp(plugin, {
43+
fastify: '2.x',
44+
name: 'fastify-http-context'
45+
})
4546
};

package-lock.json

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fastify-http-context",
3-
"version": "1.1.0",
3+
"version": "2.0.0",
44
"description": "Simulates a thread of execution to allow for true session context to take place per api call within the fastify lifecycle of calls.",
55
"main": "index.js",
66
"scripts": {
@@ -34,6 +34,7 @@
3434
"devDependencies": {
3535
"autocannon": "^4.6.0",
3636
"chai": "^4.2.0",
37+
"chai-as-promised": "^7.1.1",
3738
"concurrently": "^5.1.0",
3839
"fastify": "^2.13.1",
3940
"mocha": "^7.1.1",

test/async-local-storage.test.js

Whitespace-only changes.

test/index.test.js

Lines changed: 109 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,131 @@
11
const chai = require('chai');
22
const sinonChai = require('sinon-chai');
33
const proxyquire = require('proxyquire');
4+
const chaiAsPromise = require('chai-as-promised');
5+
const sinon = require('sinon');
6+
const sandbox = sinon.createSandbox();
7+
48
chai.use(sinonChai);
9+
chai.use(chaiAsPromise);
510

6-
const { expect } = chai;
11+
const { expect, assert } = chai;
712

8-
const { getContext, setContext } = require('../');
13+
const { httpContext, validHooks } = require('../');
914

1015
const { buildFastify } = require('./mock-server.js');
1116

1217
describe('Testing fastify-http-context', () => {
13-
let fastify;
14-
15-
describe('when setContext is called', () => {
16-
let setContext;
17-
let getContext;
18-
let namespace;
19-
20-
describe('and the namespace is defined but not active', () => {
21-
let unactiveNamespaceFastify;
22-
23-
afterEach(() => {
24-
fastify.close();
25-
});
26-
27-
beforeEach(() => {
28-
fastify = buildFastify();
29-
});
30-
31-
it('then the call returns undefined because it is out of scope even with defaults set', async() => {
32-
await fastify.inject({ method: 'GET', url: '/' });
33-
getContext = require('../').getContext;
34-
expect(getContext('user')).to.be.undefined;
35-
})
36-
});
37-
describe('and the namespace is undeifned', () => {
38-
beforeEach(() => {
39-
const imports = require('../index.js');
40-
setContext = imports.setContext;
41-
getContext = imports.getContext;
42-
});
43-
44-
it('then the getContext returns undefined', () => {
45-
setContext('value', 'value');
46-
expect(getContext('value')).to.be.undefined;
47-
});
48-
});
49-
});
50-
describe('When intialized', () => {
51-
afterEach(() => {
52-
fastify.close();
18+
let fastify;
19+
20+
beforeEach(() => {
21+
sandbox.restore();
5322
});
5423

55-
describe('And there are no defaults', () => {
56-
beforeEach(() => {
57-
fastify = buildFastify();
58-
});
24+
describe('when httpContext.set is called', () => {
25+
let set;
26+
let get;
27+
let namespace;
28+
29+
describe('and the namespace is defined but not active', () => {
30+
let unactiveNamespaceFastify;
31+
32+
afterEach(() => {
33+
fastify.close();
34+
});
5935

60-
describe('And no context was set', () => {
61-
it('then undefined is returned', async() => {
62-
const result = await fastify.inject({ method: 'GET', url: '/' });
63-
expect(JSON.parse(result.body).user).to.be.undefined;
36+
beforeEach(async () => {
37+
fastify = await buildFastify();
38+
});
39+
40+
it('then the call returns undefined because it is out of scope even with defaults set', async () => {
41+
await fastify.inject({ method: 'GET', url: '/' });
42+
get = require('../').httpContext.get;
43+
expect(get('user')).to.be.undefined;
44+
})
45+
});
46+
describe('and the namespace is undeifned', () => {
47+
beforeEach(() => {
48+
const imports = require('../index.js');
49+
set = imports.httpContext.set;
50+
get = imports.httpContext.get;
51+
});
52+
53+
it('then the httpContext.get returns undefined', () => {
54+
set('value', 'value');
55+
expect(httpContext.get('value')).to.be.undefined;
56+
});
6457
});
65-
});
6658
});
67-
68-
describe('And there are defaults', () => {
69-
beforeEach(() => {
70-
fastify = buildFastify(true);
71-
});
72-
73-
describe('And the context is not set', () => {
74-
const defaultUser = { id: 'system' };
75-
76-
it('then the returned value is the default context value', async() => {
77-
const result = await fastify.inject({ method: 'GET', url: '/' });
78-
expect(JSON.parse(result.body).user).to.deep.equal(defaultUser);
59+
describe('When intialized', () => {
60+
afterEach(() => {
61+
fastify.close();
7962
});
80-
81-
});
82-
describe('And the context is set', () => {
83-
const user = { id: 'helloUser' };
84-
85-
beforeEach(() => {
86-
fastify.addHook('preHandler', async (req, reply) => {
87-
const id = req.headers['x-header-user'];
88-
if (id) {
89-
setContext('user', { id });
90-
}
91-
return;
92-
});
63+
64+
describe('And an invalid hook is supplied', () => {
65+
beforeEach(async () => {
66+
fastify = await buildFastify(true, true);
67+
});
68+
69+
it('then an error is thrown', async () => {
70+
const logErrorStub = sandbox.stub(fastify.log, 'error');
71+
await fastify.inject({ method: 'GET', url: '/' });
72+
expect(logErrorStub).to.have.been.calledWith(`invalidHook is not a valid fastify hook. Please use one of the following ${validHooks}`);
73+
});
9374
});
9475

95-
it('then the returned value is what is in the context', async() => {
96-
const defaultUser = { id: 'system' };
97-
const result = await fastify.inject({ method: 'GET', url: '/', headers: { 'x-header-user': 'helloUser' } });
98-
expect(JSON.parse(result.body).user).to.deep.equal(user);
99-
100-
const resultNoHeader = await fastify.inject({ method: 'GET', url: '/'});
101-
expect(JSON.parse(resultNoHeader.body).user).to.deep.equal(defaultUser);
76+
describe('And there are no defaults', () => {
77+
beforeEach(async () => {
78+
fastify = await buildFastify();
79+
});
80+
81+
describe('And no context was set', () => {
82+
it('then undefined is returned', async () => {
83+
const result = await fastify.inject({ method: 'GET', url: '/' });
84+
expect(JSON.parse(result.body).user).to.be.undefined;
85+
});
86+
});
87+
});
88+
89+
describe('And there are defaults', () => {
90+
beforeEach(async () => {
91+
fastify = await buildFastify(true);
92+
});
93+
94+
describe('And the context is not set', () => {
95+
const defaultUser = { id: 'system' };
96+
97+
it('then the returned value is the default context value', async () => {
98+
const result = await fastify.inject({ method: 'GET', url: '/' });
99+
expect(JSON.parse(result.body).user).to.deep.equal(defaultUser);
100+
});
101+
102+
});
103+
describe('And the context is set', () => {
104+
const user = { id: 'helloUser' };
105+
106+
beforeEach(() => {
107+
fastify.addHook('preHandler', async (req, reply) => {
108+
const id = req.headers['x-header-user'];
109+
if (id) {
110+
httpContext.set('user', { id });
111+
}
112+
return;
113+
});
114+
});
115+
116+
it('then the returned value is what is in the context', async () => {
117+
const defaultUser = { id: 'system' };
118+
const result = await fastify.inject({ method: 'GET', url: '/', headers: { 'x-header-user': 'helloUser' } });
119+
expect(JSON.parse(result.body).user).to.deep.equal(user);
120+
121+
const resultNoHeader = await fastify.inject({ method: 'GET', url: '/' });
122+
expect(JSON.parse(resultNoHeader.body).user).to.deep.equal(defaultUser);
123+
124+
const resultRedo = await fastify.inject({ method: 'GET', url: '/', headers: { 'x-header-user': 'iAmUser' } });
125+
expect(JSON.parse(resultRedo.body).user).to.deep.equal({ id: 'iAmUser' });
126+
});
102127

103-
const resultRedo = await fastify.inject({ method: 'GET', url: '/', headers: { 'x-header-user': 'iAmUser' } });
104-
expect(JSON.parse(resultRedo.body).user).to.deep.equal({ id: 'iAmUser' });
128+
});
105129
});
106-
107-
});
108130
});
109-
});
110131
});

test/mock-server.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
1-
const { fastifyHttpContextPlugin, setContext, getContext } = require('../');
1+
const { fastifyHttpContextPlugin, httpContext} = require('../');
22

33
const sharedRoute = (request, reply) => {
4-
reply.send({ user: getContext('user')})
4+
reply.send({ user: httpContext.get('user')})
55
}
66

7-
const buildFastify = ( withDefaults = false) => {
7+
const buildFastify = async ( withDefaults = false, withInvalidHook = false) => {
88
const fastify = require('fastify')({
99
disableRequestLogging: true
1010
});
1111

12-
let options;
12+
let options = {};
1313

1414
if (withDefaults) {
15-
options = {
16-
defaults: {
15+
options.defaults = {
1716
user: { id: 'system' }
18-
}
1917
};
2018
}
2119

22-
fastify.register(fastifyHttpContextPlugin, options);
20+
if (withInvalidHook) {
21+
options.hook = 'invalidHook';
22+
}
23+
24+
await fastify.register(fastifyHttpContextPlugin, options);
2325

2426
fastify.get('/', sharedRoute);
2527

0 commit comments

Comments
 (0)