-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(context): Add support for method dependency injection
Add `invokeMethod` to invoke a prototype method of a given class with dependency injection Add support for static methods Allow non-injected args
- Loading branch information
1 parent
c506b26
commit df1c879
Showing
9 changed files
with
375 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
packages/context/test/acceptance/method-level-bindings.feature.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Feature: Context bindings - injecting dependencies of methods | ||
|
||
- In order to receive information from the context for a method | ||
- As a developer | ||
- I want to setup bindings for my method | ||
- So that method dependencies are injected by the IoC framework | ||
|
||
## Scenario: Inject method arguments | ||
|
||
- Given a context | ||
- Given class `InfoController` | ||
- Given a class binding named `controllers.info` bound to class `InfoController` | ||
- When I resolve the binding for `controllers.info` | ||
- Then I get a new instance of `InfoController` | ||
- When I invoke the `hello` method, the parameter `user` is resolved to the | ||
- value bound to `user` key in the context | ||
|
||
```ts | ||
class InfoController { | ||
|
||
static say(@inject('user') user: string):string { | ||
const msg = `Hello ${user}`; | ||
console.log(msg); | ||
return msg; | ||
} | ||
|
||
hello(@inject('user') user: string):string { | ||
const msg = `Hello ${user}`; | ||
console.log(msg); | ||
return msg; | ||
} | ||
|
||
greet(prefix: string, @inject('user') user: string):string { | ||
const msg = `[${prefix}] Hello ${user}`; | ||
console.log(msg); | ||
return msg; | ||
} | ||
} | ||
|
||
const ctx = new Context(); | ||
// Mock up user authentication | ||
ctx.bind('user').toDynamicValue(() => Promise.resolve('John')); | ||
ctx.bind('controllers.info').toClass(InfoController); | ||
|
||
const instance = await ctx.get('controllers.info'); | ||
// Invoke the `hello` method => Hello John | ||
const helloMsg = await invokeMethod(instance, 'hello', ctx); | ||
// Invoke the `greet` method with non-injected args => [INFO] Hello John | ||
const greetMsg = await invokeMethod(instance, 'greet', ctx, ['INFO']); | ||
|
||
// Invoke the static `sayHello` method => [INFO] Hello John | ||
const greetMsg = await invokeMethod(InfoController, 'sayHello', ctx); | ||
``` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright IBM Corp. 2013,2017. All Rights Reserved. | ||
// Node module: loopback | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {expect} from '@loopback/testlab'; | ||
import {Context, inject, invokeMethod} from '../..'; | ||
|
||
class InfoController { | ||
static sayHello(@inject('user') user: string): string { | ||
const msg = `Hello ${user}`; | ||
console.log(msg); | ||
return msg; | ||
} | ||
|
||
hello(@inject('user') user: string): string { | ||
const msg = `Hello ${user}`; | ||
console.log(msg); | ||
return msg; | ||
} | ||
|
||
greet(prefix: string, @inject('user') user: string): string { | ||
const msg = `[${prefix}] Hello ${user}`; | ||
console.log(msg); | ||
return msg; | ||
} | ||
} | ||
|
||
const INFO_CONTROLLER = 'controllers.info'; | ||
|
||
describe('Context bindings - Injecting dependencies of method', () => { | ||
let ctx: Context; | ||
beforeEach('given a context', createContext); | ||
|
||
it('injects prototype method args', async () => { | ||
const instance = await ctx.get(INFO_CONTROLLER); | ||
// Invoke the `hello` method => Hello John | ||
const msg = await invokeMethod(instance, 'hello', ctx); | ||
expect(msg).to.eql('Hello John'); | ||
}); | ||
|
||
it('injects prototype method args with non-injected ones', async () => { | ||
const instance = await ctx.get(INFO_CONTROLLER); | ||
// Invoke the `hello` method => Hello John | ||
const msg = await invokeMethod(instance, 'greet', ctx, ['INFO']); | ||
expect(msg).to.eql('[INFO] Hello John'); | ||
}); | ||
|
||
it('injects static method args', async () => { | ||
// Invoke the `sayHello` method => Hello John | ||
const msg = await invokeMethod(InfoController, 'sayHello', ctx); | ||
expect(msg).to.eql('Hello John'); | ||
}); | ||
|
||
it('throws error if not all args can be resolved', async () => { | ||
const instance = await ctx.get(INFO_CONTROLLER); | ||
expect(() => { | ||
invokeMethod(instance, 'greet', ctx); | ||
}).to.throw(/The arguments\[0\] is not decorated for dependency injection/); | ||
}); | ||
|
||
function createContext() { | ||
ctx = new Context(); | ||
ctx.bind('user').toDynamicValue(() => Promise.resolve('John')); | ||
ctx.bind(INFO_CONTROLLER).toClass(InfoController); | ||
} | ||
}); |
Oops, something went wrong.