Skip to content

Commit

Permalink
feat(rest): allow rest-server to be mounted on a path to express
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Feb 15, 2019
1 parent f8db27c commit de8f626
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 4 deletions.
89 changes: 89 additions & 0 deletions packages/rest/src/__tests__/integration/express.integration.ts
@@ -0,0 +1,89 @@
// Copyright IBM Corp. 2017,2018. All Rights Reserved.
// Node module: @loopback/rest
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {Application} from '@loopback/core';
import {get} from '@loopback/openapi-v3';
import {
Client,
createClientForHandler,
expect,
givenHttpServerConfig,
} from '@loopback/testlab';
import * as express from 'express';
import {RestComponent} from '../../rest.component';
import {
HttpRequestListener,
RestServer,
RestServerConfig,
} from '../../rest.server';

describe('HttpHandler mounted as an express router', () => {
let client: Client;
beforeEach(givenLoopBackApp);
beforeEach(givenExpressApp);
beforeEach(givenClient);

context('with a simple HelloWorld controller', () => {
beforeEach(function setupHelloController() {
class HelloController {
@get('/hello')
public async greet(): Promise<string> {
return 'Hello world!';
}
}

givenControllerClass(HelloController);
});

it('handles simple "GET /hello" requests', () => {
return client
.get('/api/hello')
.expect(200)
.expect('content-type', 'text/plain')
.expect('Hello world!');
});

it('handles openapi.json', async () => {
const res = await client
.get('/api/openapi.json')
.set('Accept', 'application/json')
.expect(200);
expect(res.body.servers[0]).to.eql({url: '/api'});
});
});

let expressApp: express.Application;
let server: RestServer;
let handler: HttpRequestListener;

function givenControllerClass(
// tslint:disable-next-line:no-any
ctor: new (...args: any[]) => Object,
) {
server.controller(ctor);
}

async function givenLoopBackApp(
options: {rest: RestServerConfig} = {rest: {port: 0}},
) {
options.rest = givenHttpServerConfig(options.rest);
const app = new Application(options);
app.component(RestComponent);
server = await app.getServer(RestServer);
handler = server.requestHandler;
}

/**
* Create an express app that mounts the LoopBack routes to `/api`
*/
function givenExpressApp() {
expressApp = express();
expressApp.use('/api', handler);
}

function givenClient() {
client = createClientForHandler(expressApp);
}
});
23 changes: 19 additions & 4 deletions packages/rest/src/rest.server.ts
Expand Up @@ -249,7 +249,7 @@ export class RestServer extends Context implements Server, HttpServerLike {
const mapping = this.config.openApiSpec!.endpointMapping!;
// Serving OpenAPI spec
for (const p in mapping) {
this._expressApp.use(p, (req, res) =>
this._expressApp.get(p, (req, res) =>
this._serveOpenApiSpec(req, res, mapping[p]),
);
}
Expand Down Expand Up @@ -376,11 +376,12 @@ export class RestServer extends Context implements Server, HttpServerLike {
specObj.servers = [{url: this._getUrlForClient(request)}];
}

if (specObj.servers && this._basePath) {
const basePath = this.getBasePathFor(request);
if (specObj.servers && basePath) {
for (const s of specObj.servers) {
// Update the default server url to honor `basePath`
if (s.url === '/') {
s.url = this._basePath;
s.url = basePath;
}
}
}
Expand Down Expand Up @@ -453,7 +454,21 @@ export class RestServer extends Context implements Server, HttpServerLike {
// add port number of present
host += port !== '' ? ':' + port : '';

return protocol + '://' + host + this._basePath;
return protocol + '://' + host + this.getBasePathFor(request);
}

/**
* Get the base for the request. It honors `baseUrl` sets by express if the
* application is mounted to an express app, such as:
* expressApp.use('/api', app.requestHandler);
* @param request Http request
*/
private getBasePathFor(request: Request) {
let basePath = this._basePath;
if (request.baseUrl && request.baseUrl !== '/') {
basePath = request.baseUrl + basePath;
}
return basePath;
}

private async _redirectToSwaggerUI(
Expand Down

0 comments on commit de8f626

Please sign in to comment.