Skip to content

Commit caea93c

Browse files
Async local storage (#6)
* ready to go just waiting for a v12 backport before I can pull in * a little less verbose * Update index.js Just formatting * Update README.md Updating the language to more accurately match what is going on * Update npmpublish.yml Adding node versions for expected compatibality * Update pr.yml Adding to ensure compatibility across v12 and higher node versions * Update pr.yml Correct way to build with multiple version support * Update npmpublish.yml Correct way to do multiple versions * made to be agnostic of node version * updated tests to ensure that behavior is as expected * Update npmpublish.yml Adding v10 to ensure full test coverage given feature clauses in code * Update pr.yml Adding v10 to ensure full test coverage given feature clauses in code * Update pr.yml not needed * Update npmpublish.yml not needed * needed * needed
1 parent b6eb15e commit caea93c

File tree

9 files changed

+3159
-37
lines changed

9 files changed

+3159
-37
lines changed

.github/workflows/npmpublish.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ on:
1010
jobs:
1111
build:
1212
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
node: [ '14', '13', '12', '10']
1316
steps:
1417
- uses: actions/checkout@v2
1518
- uses: actions/setup-node@v1
1619
with:
17-
node-version: 12
20+
node-version: ${{ matrix.node }}
1821
- run: npm ci
1922
- run: npm test
2023
- run: npm run benchmark
21-
2224
publish-npm:
2325
needs: build
2426
runs-on: ubuntu-latest

.github/workflows/pr.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ubuntu-latest
1313
strategy:
1414
matrix:
15-
node: [ '14', '13', '12']
15+
node: [ '14', '13', '12', '10' ]
1616
steps:
1717
- uses: actions/checkout@v2
1818
- uses: actions/setup-node@v1

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
node_modules
22
.nyc_output
33
*.tgz
4-
package-lock.json

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
> This plugin was inspired by express-http-context, but works more seamlessly within the fastify ecosystem.
66
7-
The purpose of this fastify plugin is to easily add a true http context, where any variables set within the scope of a single http call won't be overwritten by simultaneous calls to the api
7+
The purpose of this fastify plugin is to easily add a true thread local http context, where any variables set within the scope of a single http call won't be overwritten by simultaneous calls to the api
88
nor will variables remain to be assumed by subsequent calls once a request is completed.
99

10-
This is ideal when you need, to for instance, set a user in a hook, and then retrieve that user later on to add them as the createdBy or modifiedBy user later in subsequent calls. This plugin
10+
This is ideal when you need to, for instance, set a user in a hook, and then retrieve that user later on to add them as the createdBy or modifiedBy user later in subsequent calls. This plugin
1111
will ensure that the user who made the call is the user that is retrieved later on.
1212

1313
## Getting started
@@ -27,7 +27,7 @@ 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 necessar. There are cases where defaults are not wanted nor
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
3131
necessary.
3232
3333
From there you can set a context in another hook, route, or method that is within scope. For instance:

async-local-storage.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
let AsyncLocalStorage = require('async_hooks').AsyncLocalStorage;
2+
let namespace; // = new AsyncLocalStorage();
3+
4+
if (AsyncLocalStorage == null) {
5+
const createNamespace = require('cls-hooked').createNamespace;
6+
const hyperid = require('hyperid');
7+
const generator = hyperid();
8+
const id = generator();
9+
10+
const namespaceIdentifier = hyperid.decode(id).uuid;
11+
12+
namespace = createNamespace(namespaceIdentifier);
13+
namespace.getContext = function(key) {
14+
if (this.active) {
15+
return this.get(key);
16+
}
17+
return undefined;
18+
};
19+
20+
namespace.setContext = function(key, value){
21+
if (this.active) {
22+
this.set(key, value);
23+
}
24+
}
25+
namespace.doRun = ( defaults = {}, callback) => {
26+
namespace.run(() => {
27+
for (let [key, value] of defaults) {
28+
namespace.set(key, value);
29+
}
30+
callback();
31+
});
32+
};
33+
} else {
34+
namespace = new class AsyncLocalStorageFacade extends AsyncLocalStorage {
35+
constructor() {
36+
super();
37+
}
38+
39+
doRun(defaults, callback) {
40+
this.run(new Map(), () => {
41+
const store = this.getStore();
42+
for (let [key, value] of defaults) {
43+
store.set(key, value);
44+
}
45+
callback();
46+
});
47+
}
48+
49+
setContext(key, value) {
50+
const store = this.getStore();
51+
52+
if (store != null) {
53+
store.set(key, value);
54+
}
55+
}
56+
57+
getContext(key) {
58+
const store = this.getStore();
59+
60+
return store != null ? store.get(key) : undefined;
61+
}
62+
}();
63+
}
64+
65+
66+
module.exports = {
67+
namespace: namespace
68+
}

index.js

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,21 @@
11
const fp = require('fastify-plugin');
2-
const createNamespace = require('cls-hooked').createNamespace;
3-
const hyperid = require('hyperid');
4-
const generator = hyperid();
5-
const id = generator();
62

7-
const namespaceIdentifier = hyperid.decode(id).uuid;
8-
9-
let namespace;
10-
11-
const getDefaults = (key) => {
12-
return namespace.get(namespaceIdentifier) || {};
13-
}
3+
const { namespace } = require('./async-local-storage');
144

155
const getContext = (key) => {
16-
let value;
17-
if (namespace && namespace.active) {
18-
value = namespace.get(key) || getDefaults()[key];
19-
}
20-
return value;
6+
return namespace.getContext(key);
217
};
228

239
const setContext = (key, value) => {
24-
if (namespace && namespace.active) {
25-
namespace.set(key, value);
26-
}
10+
namespace.setContext(key, value);
2711
};
2812

2913
function plugin (fastify, opts, next) {
30-
namespace = createNamespace(namespaceIdentifier);
31-
14+
const defaults = new Map(Object.entries(opts.defaults || {}));
3215
fastify.addHook('onRequest', (req, res, done) => {
33-
namespace.run(() => {
34-
if (opts && opts.defaults) {
35-
setContext(namespaceIdentifier, opts.defaults);
36-
}
16+
namespace.doRun(defaults, () => {
3717
done();
38-
});
18+
});
3919
});
4020
next();
4121
}

0 commit comments

Comments
 (0)