@@ -5,57 +5,69 @@ import {
5
5
NestInterceptor ,
6
6
} from '@nestjs/common' ;
7
7
import { GqlExecutionContext } from '@nestjs/graphql' ;
8
+ import { AsyncLocalStorage } from 'async_hooks' ;
8
9
import { isUUID } from 'class-validator' ;
9
10
import { BehaviorSubject , identity } from 'rxjs' ;
10
11
import { Session } from '~/common' ;
11
12
import { EdgeDB , OptionsFn } from '~/core/edgedb' ;
12
13
import { GlobalHttpHook } from '~/core/http' ;
13
14
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
+ */
14
20
@Injectable ( )
15
21
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 <
20
26
BehaviorSubject < OptionsFn >
21
27
> ( ) ;
22
28
23
- constructor ( private readonly edgedb : EdgeDB ) { }
24
-
25
29
@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
28
36
const optionsHolder = new BehaviorSubject < OptionsFn > ( identity ) ;
29
- this . optionsHolderByRequest . set ( req . raw , optionsHolder ) ;
30
37
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
+ ) ;
35
44
}
36
45
37
46
/**
38
47
* Connect the session to the options' holder
39
48
*/
40
49
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
+ }
42
59
60
+ private getSession ( context : ExecutionContext ) {
61
+ const type = context . getType ( ) ;
43
62
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' ) {
53
67
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 ) ;
56
69
}
57
-
58
- return next . handle ( ) ;
70
+ return undefined ;
59
71
}
60
72
61
73
private applyToOptions (
0 commit comments