Skip to content

Commit

Permalink
feat(rest): add local UI for api explorer
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed Sep 7, 2018
1 parent f72d817 commit d5e9db2
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 3 deletions.
5 changes: 4 additions & 1 deletion packages/explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@
"copyright.owner": "IBM Corp.",
"license": "MIT",
"dependencies": {
"@loopback/context": "^0.12.5",
"@loopback/core": "^0.11.5",
"@loopback/dist-util": "^0.3.6",
"@types/express": "^4.16.0",
"ejs": "^2.6.1",
"serve-static": "^1.13.2",
"swagger-ui-dist": "^3.18.2"
},
"devDependencies": {
"@loopback/build": "^0.7.1",
"@loopback/dist-util": "^0.3.6",
"@loopback/testlab": "^0.11.5",
"@loopback/rest": "^0.19.6",
"@types/ejs": "^2.6.0",
"@types/node": "^10.1.1",
"@types/serve-static": "^1.13.2",
Expand Down
30 changes: 30 additions & 0 deletions packages/explorer/src/explorer.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/explorer
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {inject, Setter, Provider} from '@loopback/context';
import {Component, ProviderMap} from '@loopback/core';
import {ApiExplorerUIOptions, apiExplorerUI} from './explorer';
import {Handler} from 'express';
import {ExplorerBindings} from './keys';

export class ExplorerProvider implements Provider<Handler> {
constructor(
@inject(ExplorerBindings.CONFIG, {optional: true})
private config: ApiExplorerUIOptions,
) {}

value() {
return apiExplorerUI(this.config);
}
}

export class ExplorerComponent implements Component {
providers?: ProviderMap;
constructor() {
this.providers = {
[ExplorerBindings.HANDLER.key]: ExplorerProvider,
};
}
}
2 changes: 2 additions & 0 deletions packages/explorer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
// License text available at https://opensource.org/licenses/MIT

export * from './explorer';
export * from './keys';
export * from './explorer.component';
19 changes: 19 additions & 0 deletions packages/explorer/src/keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/explorer
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {BindingKey} from '@loopback/context';
import {RequestHandler} from 'express';
import {ApiExplorerUIOptions} from './explorer';

/**
* Binding keys used by this component.
*/
export namespace ExplorerBindings {
export const HANDLER = BindingKey.create<RequestHandler>('explorer.handler');

export const CONFIG = BindingKey.create<ApiExplorerUIOptions | undefined>(
'explorer.config',
);
}
2 changes: 1 addition & 1 deletion packages/explorer/test/integration/explorer.integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// License text available at https://opensource.org/licenses/MIT

import {createClientForHandler} from '@loopback/testlab';
import {apiExplorerUI} from '../../src/explorer';
import {apiExplorerUI} from '../../';
import * as express from 'express';
import * as path from 'path';

Expand Down
45 changes: 45 additions & 0 deletions packages/explorer/test/integration/rest.integration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/explorer
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
import {createClientForHandler} from '@loopback/testlab';
import {RestServerConfig, RestComponent, RestServer} from '@loopback/rest';
import {Application} from '@loopback/core';
import {ExplorerComponent, ExplorerBindings} from '../..';

describe('API Explorer for REST Server', () => {
let server: RestServer;

before(async () => {
server = await givenAServer({
rest: {},
});

// FIXME: How do we configure the API Explorer UI?
server.bind(ExplorerBindings.CONFIG).to({});

// FIXME: Should the ExplorerComponent contribute a handler or route?
const handler = await server.get(ExplorerBindings.HANDLER);

// FIXME: How to register routes to the rest server by phase or order
// FIXME: Should REST server automatically mounts the route based on registration
// via bindings such as `rest.server.routes`?
server.staticRouter.use('/explorer', handler);
});

it('exposes "GET /explorer"', async () => {
await createClientForHandler(server.requestHandler)
.get('/explorer')
.expect(200, /\<title\>LoopBack API Explorer<\/title\>/)
.expect('content-type', /text\/html.*/);
});
});

async function givenAServer(options?: {rest: RestServerConfig}) {
const app = new Application(options);
app.component(RestComponent);
// FIXME: Can we mount the ExplorerComponent to a given server?
app.component(ExplorerComponent);
const server = await app.getServer(RestServer);
return server;
}
7 changes: 6 additions & 1 deletion packages/rest/src/rest.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export class RestServer extends Context implements Server, HttpServerLike {
maxAge: 86400,
credentials: true,
};
// Set up CORS
this._expressApp.use(cors(corsOptions));

// Place the assets router here before controllers
Expand Down Expand Up @@ -415,7 +416,7 @@ export class RestServer extends Context implements Server, HttpServerLike {
// 127.0.0.1
let host =
(request.get('x-forwarded-host') || '').split(',')[0] ||
request.headers.host!.replace(/:[0-9]+$/, '');
request.headers.host!.replace(/:([0-9]+)$/, '');
let port =
(request.get('x-forwarded-port') || '').split(',')[0] ||
this.config.port ||
Expand Down Expand Up @@ -580,6 +581,10 @@ export class RestServer extends Context implements Server, HttpServerLike {
this._routerForStaticAssets.use(path, express.static(rootDir, options));
}

get staticRouter() {
return this._routerForStaticAssets;
}

/**
* Set the OpenAPI specification that defines the REST API schema for this
* server. All routes, parameter definitions and return types will be defined
Expand Down

0 comments on commit d5e9db2

Please sign in to comment.