Skip to content

Commit 42bae6f

Browse files
authored
Refactor EdgeDB currentUser global provider context tracking (#3327)
1 parent 66872b4 commit 42bae6f

File tree

1 file changed

+39
-27
lines changed

1 file changed

+39
-27
lines changed

src/components/authentication/current-user.provider.ts

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,69 @@ import {
55
NestInterceptor,
66
} from '@nestjs/common';
77
import { GqlExecutionContext } from '@nestjs/graphql';
8+
import { AsyncLocalStorage } from 'async_hooks';
89
import { isUUID } from 'class-validator';
910
import { BehaviorSubject, identity } from 'rxjs';
1011
import { Session } from '~/common';
1112
import { EdgeDB, OptionsFn } from '~/core/edgedb';
1213
import { GlobalHttpHook } from '~/core/http';
1314

15+
/**
16+
* This sets the currentUser EdgeDB global based on
17+
* - GQL: {@link GqlContextType.session$} (updates to this will also be carried forward here)
18+
* - HTTP: {@link IRequest.session}
19+
*/
1420
@Injectable()
1521
export class EdgeDBCurrentUserProvider implements NestInterceptor {
16-
// A map to transfer the options' holder
17-
// between the creation in middleware and the use in the interceptor.
18-
private readonly optionsHolderByRequest = new WeakMap<
19-
Parameters<GlobalHttpHook>[0]['raw'],
22+
constructor(private readonly edgedb: EdgeDB) {}
23+
24+
// Storage for the current options' holder layer
25+
private readonly currentHolder = new AsyncLocalStorage<
2026
BehaviorSubject<OptionsFn>
2127
>();
2228

23-
constructor(private readonly edgedb: EdgeDB) {}
24-
2529
@GlobalHttpHook()
26-
onRequest(...[req, _reply, next]: Parameters<GlobalHttpHook>) {
27-
// Create holder to use later to add current user to globals after it is fetched
30+
onRequest(...[_req, _reply, next]: Parameters<GlobalHttpHook>) {
31+
this.usingOptionsLayer(next);
32+
}
33+
34+
usingOptionsLayer<R>(next: () => R): R {
35+
// Create a holder to use later to add the current user to globals after it is fetched
2836
const optionsHolder = new BehaviorSubject<OptionsFn>(identity);
29-
this.optionsHolderByRequest.set(req.raw, optionsHolder);
3037

31-
// These options should apply to the entire HTTP/GQL operation.
32-
// Connect middleware is the only place we get a function which has all of
33-
// this in scope for the use of an ALS context.
34-
this.edgedb.usingOptions(optionsHolder, next);
38+
// Set this holder as the current holder for the current request.
39+
return this.currentHolder.run(optionsHolder, () =>
40+
// Add these options to the EdgeDB options context.
41+
// These options should apply to the entire HTTP/GQL operation.
42+
this.edgedb.usingOptions(optionsHolder, next),
43+
);
3544
}
3645

3746
/**
3847
* Connect the session to the options' holder
3948
*/
4049
intercept(context: ExecutionContext, next: CallHandler) {
41-
const type = context.getType();
50+
const optionsHolder = this.currentHolder.getStore();
51+
if (!optionsHolder) {
52+
throw new Error('Current user options holder is not in async context');
53+
}
54+
this.getSession(context)?.subscribe((session) => {
55+
this.applyToOptions(session, optionsHolder);
56+
});
57+
return next.handle();
58+
}
4259

60+
private getSession(context: ExecutionContext) {
61+
const type = context.getType();
4362
if (type === 'graphql') {
44-
const { request, session$ } =
45-
GqlExecutionContext.create(context).getContext();
46-
if (request) {
47-
const optionsHolder = this.optionsHolderByRequest.get(request.raw)!;
48-
session$.subscribe((session) => {
49-
this.applyToOptions(session, optionsHolder);
50-
});
51-
}
52-
} else if (type === 'http') {
63+
const { session$ } = GqlExecutionContext.create(context).getContext();
64+
return session$;
65+
}
66+
if (type === 'http') {
5367
const request = context.switchToHttp().getRequest();
54-
const optionsHolder = this.optionsHolderByRequest.get(request.raw)!;
55-
this.applyToOptions(request.session, optionsHolder);
68+
return new BehaviorSubject(request.session);
5669
}
57-
58-
return next.handle();
70+
return undefined;
5971
}
6072

6173
private applyToOptions(

0 commit comments

Comments
 (0)