deepkit-framework icon indicating copy to clipboard operation
deepkit-framework copied to clipboard

DI: Module provider visibility issue

Open alpharder opened this issue 1 year ago • 7 comments

I've created a PR with a failing test case: https://github.com/deepkit/deepkit-framework/pull/595/files#diff-8638469975c860d179cea016ca3ee1f3c2d65fbd6eba97a2096def06beeaa332R193

TL;DR is provider registered within a deeply nested module isn't available for ingestion within the same module, unless this module gets exported to the root.

alpharder avatar Jul 09 '24 10:07 alpharder

The bug in the code here comes from a misunderstanding. Encapsulated services like SessionForRequest can not be set via the root InjectorContext that you have in event.injectorContext. This context operates on the root module (the app module). This module doesn't know anything about encapsulated services like SessionForRequest, so in order to set or get this particular service manually from a Injector or InjectorContext, you need to have a reference to the module it was defined in. Since your listeners is also defined in the same module, you can inject the AppModule it was defined in via dependency injection and then use this reference for the set call:


     class AuthenticationListener {
         @eventDispatcher.listen(httpWorkflow.onAuth)
         onServerMainBootstrap(event: typeof httpWorkflow.onAuth.event, module: AppModule) {
             event.injectorContext.set(
                 SessionForRequest,
                 new SessionForRequest(
                     'sidValue',
                     'uidValue',
                 ),
                 module,
             );
         }
     }

this way the DI container can find the SessionForRequest and set it correctly.

marcj avatar Jul 10 '24 15:07 marcj

Thanks for the explanation, it makes sense now. I didn't expect event injection context to be root context instead of the listener's module context.

alpharder avatar Jul 10 '24 19:07 alpharder

This probably needs to be documented in some way

alpharder avatar Jul 10 '24 19:07 alpharder

@marcj Unfortunately, the suggested fix didn't help: https://github.com/deepkit/deepkit-framework/pull/595/files#diff-5a1f8db1e5e23da96c85177fe8129b79e23bf0a80af0053c5407237aa2c1594bR8, result is the same: Controller for route /auth/whoami parameter resolving error: DependenciesUnmetError: Parameter sess is required but provider returned undefined.

alpharder avatar Jul 10 '24 23:07 alpharder

Here's the injector that gets built for AuthenticationModule:

'function(token, scope, destination) {\n' +
        '                switch (true) {\n' +
        '                    \n' +
        '            //AuthenticationListener, from AuthenticationModule\n' +
        '            case token === constVar_0: {\n' +
        '                if (injector.instances.i111 !== undefined) return injector.instances.i111;\n' +
        '                CircularDetector.push(constVar_0);\n' +
        '                \n' +
        '                injector.instantiated.i111 = injector.instantiated.i111 ? injector.instantiated.i111 + 1 : 1;\n' +
        '                injector.instances.i111 = new classType_0();\n' +
        '\n' +
        '                \n' +
        '                CircularDetector.pop();\n' +
        '                //no custom provider setup\n' +
        '                return injector.instances.i111;\n' +
        '            }\n' +
        '        \n' +
        '\n' +
        '                    case token === token_0 && scope && scope.name === "http": {\n' +
        '                        return resolveFrom_0.injector.resolver(token_0, scope, destination);\n' +
        '                    }\n' +
        '                    \n' +
        '\n' +
        '            //OAuth2Controller, from AuthenticationModule\n' +
        '            case token === constVar_1 && scope && scope.name === "http": {\n' +
        '                if (scope.instances.i113 !== undefined) return scope.instances.i113;\n' +
        '                CircularDetector.push(constVar_1);\n' +
        '                \n' +
        '                injector.instantiated.i113 = injector.instantiated.i113 ? injector.instantiated.i113 + 1 : 1;\n' +
        '                scope.instances.i113 = new classType_1();\n' +
        '\n' +
        '                \n' +
        '                CircularDetector.pop();\n' +
        '                //no custom provider setup\n' +
        '                return scope.instances.i113;\n' +
        '            }\n' +
        '        \n' +
        '                }\n' +
        '\n' +
        "                tokenNotfoundError(token, 'AuthenticationModule');\n" +
        '            }'

alpharder avatar Jul 11 '24 00:07 alpharder

the fix did help, I tested it locally. I guess you have a new use-case/different code now. the error message indicates that the setter isn't working (maybe injected "module" is wrong), but I haven't looked closely yet.

marcj avatar Jul 11 '24 00:07 marcj

Please refer to the PR with a failing test case: https://github.com/deepkit/deepkit-framework/pull/595/files

Use case is the same.

Injector wants to resolve value from some other module, because SessionForRequest is exported:

return resolveFrom_0.injector.resolver(token_0, scope, destination);

alpharder avatar Jul 11 '24 00:07 alpharder

@alpharder is this issue still present?

marcj avatar Nov 12 '24 10:11 marcj