Skip to content

Commit 72afddd

Browse files
committed
feat(rest): enable dependency injection for controller methods
1 parent 61f6534 commit 72afddd

File tree

4 files changed

+69
-6
lines changed

4 files changed

+69
-6
lines changed

packages/openapi-v2/src/controller-spec.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,19 @@ function resolveControllerSpec(constructor: Function): ControllerSpec {
160160
throw new Error('More than one body parameters found: ' + bodyParams);
161161
}
162162
params = DecoratorFactory.cloneDeep(params);
163-
operationSpec.parameters = params;
163+
/**
164+
* If a controller method uses dependency injection, the parameters
165+
* might be sparsed. For example,
166+
* ```ts
167+
* class MyController {
168+
* greet(
169+
* @inject('prefix') prefix: string,
170+
* @param.query.string('name) name: string) {
171+
* return `${prefix}`, ${name}`;
172+
* }
173+
* ```
174+
*/
175+
operationSpec.parameters = params.filter(p => p != null);
164176
}
165177
operationSpec['x-operation-name'] = op;
166178

packages/rest/src/router/routing-table.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import {
88
ParameterObject,
99
PathsObject,
1010
} from '@loopback/openapi-spec';
11-
import {Context, Constructor, instantiateClass} from '@loopback/context';
11+
import {
12+
Context,
13+
Constructor,
14+
instantiateClass,
15+
invokeMethod,
16+
} from '@loopback/context';
1217
import {ServerRequest} from 'http';
1318
import * as HttpErrors from 'http-errors';
1419

@@ -315,7 +320,13 @@ export class ControllerRoute extends BaseRoute {
315320
`Controller method not found: ${this.describe()}`,
316321
);
317322
}
318-
return await controller[this._methodName](...args);
323+
// Invoke the method with dependency injection
324+
return await invokeMethod(
325+
controller,
326+
this._methodName,
327+
requestContext,
328+
args,
329+
);
319330
}
320331

321332
private async _createControllerInstance(

packages/rest/test/acceptance/routing/routing.acceptance.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,47 @@ describe('Routing', () => {
119119
);
120120
});
121121

122+
it('allows controllers to use method DI with mixed params', async () => {
123+
class MyController {
124+
@get('/greet')
125+
greet(
126+
@param.query.string('name') name: string,
127+
@inject('hello.prefix') prefix: string,
128+
) {
129+
return `${prefix} ${name}`;
130+
}
131+
}
132+
const app = givenAnApplication();
133+
app.bind('hello.prefix').to('Hello');
134+
const server = await givenAServer(app);
135+
givenControllerInApp(app, MyController);
136+
return (
137+
whenIMakeRequestTo(server)
138+
.get('/greet?name=world')
139+
// Then I get the result `hello world` from the `Method`
140+
.expect('Hello world')
141+
);
142+
});
143+
144+
it('allows controllers to use method DI without rest params', async () => {
145+
class MyController {
146+
@get('/greet')
147+
greet(@inject('hello.prefix') prefix: string) {
148+
return `${prefix} world`;
149+
}
150+
}
151+
const app = givenAnApplication();
152+
app.bind('hello.prefix').to('Hello');
153+
const server = await givenAServer(app);
154+
givenControllerInApp(app, MyController);
155+
return (
156+
whenIMakeRequestTo(server)
157+
.get('/greet')
158+
// Then I get the result `hello world` from the `Method`
159+
.expect('Hello world')
160+
);
161+
});
162+
122163
it('injects controller constructor arguments', async () => {
123164
const app = givenAnApplication();
124165
const server = await givenAServer(app);

packages/rest/test/unit/router/routing-table.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,8 @@ describe('RoutingTable', () => {
4343
table.registerController(TestController, spec);
4444
const paths = table.describeApiPaths();
4545
const params = paths['/greet']['get'].parameters;
46-
expect(params).to.have.property('length', 2);
47-
expect(params[0]).to.be.undefined();
48-
expect(params[1]).to.have.properties({
46+
expect(params).to.have.property('length', 1);
47+
expect(params[0]).to.have.properties({
4948
name: 'message',
5049
in: 'query',
5150
type: 'string',

0 commit comments

Comments
 (0)