Skip to content

Commit

Permalink
fix(rest): use context.get() to resolve current controller
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Feb 26, 2018
1 parent e3577ab commit f066509
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 17 deletions.
10 changes: 7 additions & 3 deletions packages/rest/src/http-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {Context} from '@loopback/context';
import {Context, Binding} from '@loopback/context';
import {PathsObject, DefinitionsObject} from '@loopback/openapi-spec';
import {ServerRequest, ServerResponse} from 'http';
import {ControllerSpec} from '@loopback/openapi-v2';
Expand Down Expand Up @@ -33,8 +33,12 @@ export class HttpHandler {
this.handleRequest = (req, res) => this._handleRequest(req, res);
}

registerController(name: ControllerClass, spec: ControllerSpec) {
this._routes.registerController(name, spec);
registerController(
name: ControllerClass,
spec: ControllerSpec,
binding?: Binding,
) {
this._routes.registerController(name, spec, binding);
}

registerRoute(route: RouteEntry) {
Expand Down
4 changes: 2 additions & 2 deletions packages/rest/src/rest-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export class RestServer extends Context implements Server {
if (apiSpec.definitions) {
this._httpHandler.registerApiDefinitions(apiSpec.definitions);
}
this._httpHandler.registerController(ctor, apiSpec);
this._httpHandler.registerController(ctor, apiSpec, b);
}

for (const b of this.find('routes.*')) {
Expand Down Expand Up @@ -264,7 +264,7 @@ export class RestServer extends Context implements Server {
);
}

const route = new ControllerRoute(verb, path, spec, ctor);
const route = new ControllerRoute(verb, path, spec, ctor, undefined, b);
this._httpHandler.registerRoute(route);
return;
}
Expand Down
37 changes: 28 additions & 9 deletions packages/rest/src/router/routing-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import {
PathsObject,
} from '@loopback/openapi-spec';
import {
BindingScope,
Context,
Constructor,
instantiateClass,
invokeMethod,
Binding,
} from '@loopback/context';
import {ServerRequest} from 'http';
import * as HttpErrors from 'http-errors';
Expand Down Expand Up @@ -66,7 +67,11 @@ export type ControllerClass = Constructor<any>;
export class RoutingTable {
private readonly _routes: RouteEntry[] = [];

registerController(controller: ControllerClass, spec: ControllerSpec) {
registerController(
controller: ControllerClass,
spec: ControllerSpec,
binding?: Binding,
) {
assert(
typeof spec === 'object' && !!spec,
'API specification must be a non-null object',
Expand All @@ -82,7 +87,14 @@ export class RoutingTable {
for (const verb in spec.paths[p]) {
const opSpec: OperationObject = spec.paths[p][verb];
const fullPath = RoutingTable.joinPath(basePath, p);
const route = new ControllerRoute(verb, fullPath, opSpec, controller);
const route = new ControllerRoute(
verb,
fullPath,
opSpec,
controller,
undefined,
binding,
);
this.registerRoute(route);
}
}
Expand Down Expand Up @@ -269,6 +281,7 @@ export class ControllerRoute extends BaseRoute {
spec: OperationObject,
protected readonly _controllerCtor: ControllerClass,
methodName?: string,
protected _binding?: Binding,
) {
super(
verb,
Expand Down Expand Up @@ -306,6 +319,13 @@ export class ControllerRoute extends BaseRoute {

updateBindings(requestContext: Context) {
const ctor = this._controllerCtor;
if (!this._binding) {
// A binding does not exist. Create one in the request context.
this._binding = requestContext
.bind(`controllers.${ctor.name}`)
.toClass(ctor)
.inScope(BindingScope.SINGLETON);
}
requestContext.bind('controller.current.ctor').to(ctor);
requestContext.bind('controller.current.operation').to(this._methodName);
}
Expand All @@ -329,14 +349,13 @@ export class ControllerRoute extends BaseRoute {
);
}

private async _createControllerInstance(
private _createControllerInstance(
requestContext: Context,
): Promise<ControllerInstance> {
const valueOrPromise = instantiateClass(
this._controllerCtor,
requestContext,
);
return (await Promise.resolve(valueOrPromise)) as ControllerInstance;
const controllerBindingKey = this._binding
? this._binding.key
: `controllers.${this._controllerCtor.name}`;
return requestContext.get(controllerBindingKey);
}
}

Expand Down
30 changes: 28 additions & 2 deletions packages/rest/test/acceptance/routing/routing.acceptance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {

import {expect, Client, createClientForHandler} from '@loopback/testlab';
import {anOpenApiSpec, anOperationSpec} from '@loopback/openapi-spec-builder';
import {inject, Context} from '@loopback/context';
import {inject, Context, BindingScope} from '@loopback/context';
import {ControllerClass} from '../../../src/router/routing-table';

/* # Feature: Routing
Expand Down Expand Up @@ -315,6 +315,32 @@ describe('Routing', () => {
});
});

it('binds the current controller', async () => {
const app = givenAnApplication();
const server = await givenAServer(app);
const spec = anOpenApiSpec()
.withOperationReturningString('get', '/name', 'checkController')
.build();

@api(spec)
class GetCurrentController {
async checkController(
@inject('controllers.GetCurrentController') inst: GetCurrentController,
): Promise<object> {
return {
result: this === inst,
};
}
}
givenControllerInApp(app, GetCurrentController);

return whenIMakeRequestTo(server)
.get('/name')
.expect({
result: true,
});
});

it('supports function-based routes', async () => {
const app = givenAnApplication();
const server = await givenAServer(app);
Expand Down Expand Up @@ -577,7 +603,7 @@ describe('Routing', () => {
}

function givenControllerInApp(app: Application, controller: ControllerClass) {
app.controller(controller);
app.controller(controller).inScope(BindingScope.CONTEXT);
}

function whenIMakeRequestTo(server: RestServer): Client {
Expand Down
3 changes: 2 additions & 1 deletion packages/rest/test/unit/router/routing-table.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import {getControllerSpec, param, get} from '@loopback/openapi-v2';
import {expect, ShotRequestOptions, ShotRequest} from '@loopback/testlab';
import {anOpenApiSpec} from '@loopback/openapi-spec-builder';
import {Binding} from '@loopback/context';

describe('RoutingTable', () => {
it('joins basePath and path', () => {
Expand Down Expand Up @@ -59,7 +60,7 @@ describe('RoutingTable', () => {
class TestController {}

const table = new RoutingTable();
table.registerController(TestController, spec);
table.registerController(TestController, spec, new Binding('foo'));

const request = givenRequest({
method: 'get',
Expand Down

0 comments on commit f066509

Please sign in to comment.