Skip to content

Commit

Permalink
fix(rest-explorer): exclude basePath from /openapi URL
Browse files Browse the repository at this point in the history
Endpoints serving OpenAPI spec ignore basePath setting, i.e. even if
basePath is set to `/api`, the spec is served at `/openapi.json`.

Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
  • Loading branch information
bajtos committed May 9, 2019
1 parent b8d005c commit 94102bd
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ describe('API Explorer (acceptance)', () => {
let app: RestApplication;
let request: Client;

afterEach(async () => {
if (app) await app.stop();
(app as unknown) = undefined;
});

context('with default config', () => {
beforeEach(async () => {
app = givenRestApplication();
Expand Down Expand Up @@ -109,6 +114,24 @@ describe('API Explorer (acceptance)', () => {
}
});

context('with custom basePath', () => {
beforeEach(async () => {
app = givenRestApplication();
app.basePath('/api');
app.component(RestExplorerComponent);
await app.start();
request = createRestAppClient(app);
});

it('uses correct URLs', async () => {
// static assets (including swagger-ui) honor basePath
const response = await request.get('/api/explorer/').expect(200);
const body = response.body;
// OpenAPI endpoints DO NOT honor basePAth
expect(body).to.match(/^\s*url: '\/openapi.json',\s*$/m);
});
});

function givenRestApplication(config?: RestServerConfig) {
const rest = Object.assign({}, givenHttpServerConfig(), config);
return new RestApplication({rest});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,11 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {
HttpRequestListener,
RestApplication,
RestServer,
RestServerConfig,
} from '@loopback/rest';
import {RestApplication, RestServer, RestServerConfig} from '@loopback/rest';
import {
Client,
createClientForHandler,
expect,
givenHttpServerConfig,
} from '@loopback/testlab';
import * as express from 'express';
Expand All @@ -21,7 +17,6 @@ describe('REST Explorer mounted as an express router', () => {
let client: Client;
let expressApp: express.Application;
let server: RestServer;
let handler: HttpRequestListener;
beforeEach(givenLoopBackApp);
beforeEach(givenExpressApp);
beforeEach(givenClient);
Expand All @@ -41,22 +36,32 @@ describe('REST Explorer mounted as an express router', () => {
.expect('location', '/api/explorer/');
});

it('honors basePath config', async () => {
server.basePath('/v1');
// static assets (including swagger-ui) honor basePath
const response = await client.get('/api/v1/explorer/').expect(200);
// OpenAPI endpoints DO NOT honor basePath
expect(response.body).to.match(/^\s*url: '\/api\/openapi.json',\s*$/m);
});

async function givenLoopBackApp(
options: {rest: RestServerConfig} = {rest: {port: 0}},
) {
options.rest = givenHttpServerConfig(options.rest);
const app = new RestApplication(options);
app.component(RestExplorerComponent);
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);
expressApp.use('/api', (req, res, _next) => {
// defer calling of `server.requestHandler` until a request arrives
server.requestHandler(req, res);
});
}

function givenClient() {
Expand Down
19 changes: 16 additions & 3 deletions packages/rest-explorer/src/rest-explorer.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import {inject} from '@loopback/context';
import {
RestBindings,
RestServerConfig,
OpenApiSpecForm,
Request,
Response,
RestBindings,
RestServerConfig,
} from '@loopback/rest';
import * as ejs from 'ejs';
import * as fs from 'fs';
Expand All @@ -26,6 +26,7 @@ export class ExplorerController {
constructor(
@inject(RestBindings.CONFIG, {optional: true})
restConfig: RestServerConfig = {},
@inject(RestBindings.BASE_PATH) private serverBasePath: string,
@inject(RestBindings.Http.REQUEST) private request: Request,
@inject(RestBindings.Http.RESPONSE) private response: Response,
) {
Expand All @@ -39,7 +40,19 @@ export class ExplorerController {

index() {
let openApiSpecUrl = this.openApiSpecUrl;
if (this.request.baseUrl && this.request.baseUrl !== '/') {

// baseURL is composed from mountPath and basePath
// OpenAPI endpoints ignore basePath but do honor mountPath
let rootPath = this.request.baseUrl;
if (
this.serverBasePath &&
this.serverBasePath !== '/' &&
rootPath.endsWith(this.serverBasePath)
) {
rootPath = rootPath.slice(0, -this.serverBasePath.length);
}

if (rootPath && rootPath !== '/') {
openApiSpecUrl = this.request.baseUrl + openApiSpecUrl;
}
const data = {
Expand Down

0 comments on commit 94102bd

Please sign in to comment.