Skip to content

Commit

Permalink
feat: an extension adding a self-hosted REST API Explorer
Browse files Browse the repository at this point in the history
Add a new package @loopback/rest-explorer that provides a self-hosted
swagger-ui instance and disables the built-in swagger-ui redirect.

Modify example projects to use @loopback/rest-explorer.

Modify the CLI template to include self-hosted REST API Explorer in new
applications.
  • Loading branch information
bajtos committed Nov 15, 2018
1 parent 34af6a0 commit 4c165c7
Show file tree
Hide file tree
Showing 33 changed files with 605 additions and 10 deletions.
13 changes: 13 additions & 0 deletions docs/site/Self-hosted-REST-API-Explorer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
lang: en
title: 'Self-hosted REST API Explorer'
keywords: LoopBack 4.0, LoopBack 4
layout: readme
source: loopback-next
file: packages/rest-explorer/README.md
sidebar: lb4_sidebar
permalink: /doc/en/lb4/Self-hosted-rest-api-explorer.html
summary:
A tutorial on using `@loopback/rest-explorer` to add a self-hosted REST API
Explorer
---
13 changes: 11 additions & 2 deletions docs/site/Server.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const app = new RestApplication({
To disable redirect to the externally hosted API Explorer, set the config option
`rest.apiExplorer.disabled` to `true`.

````ts
```ts
const app = new RestApplication({
rest: {
apiExplorer: {
Expand All @@ -139,6 +139,15 @@ const app = new RestApplication({
});
```

### Use a self-hosted API Explorer

Hosting the API Explorer at an external URL has a few downsides, for example a
working internet connection is required to explore the API. As a recommended
alternative, LoopBack comes with an extension that provides a self-hosted
Explorer UI. Please refer to
[Self-hosted REST API Explorer](./Self-hosted-rest-api-explorer.md) for more
details.

### Enable HTTPS

Enabling HTTPS for the LoopBack REST server is just a matter of specifying the
Expand Down Expand Up @@ -168,7 +177,7 @@ export async function main() {
const url = app.restServer.url;
console.log(`Server is running at ${url}`);
}
````
```

### Customize CORS

Expand Down
5 changes: 5 additions & 0 deletions docs/site/sidebars/lb4_sidebar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ children:
- title: 'Using Components'
url: Using-components.html
output: 'web, pdf'
children:

- title: Self-hosted REST API Explorer
url: Self-hosted-rest-api-explorer.html
output: 'web, pdf'

- title: 'Calling other APIs'
url: Calling-other-APIs-and-web-services.html
Expand Down
1 change: 1 addition & 0 deletions examples/soap-calculator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@loopback/boot": "^1.0.4",
"@loopback/context": "^1.1.0",
"@loopback/core": "^1.1.0",
"@loopback/rest-explorer": "^0.1.0",
"@loopback/openapi-v3": "^1.1.1",
"@loopback/repository": "^1.0.4",
"@loopback/rest": "^1.3.0",
Expand Down
5 changes: 4 additions & 1 deletion examples/soap-calculator/src/application.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {BootMixin} from '@loopback/boot';
import {ApplicationConfig} from '@loopback/core';
import {RestExplorerComponent} from '@loopback/rest-explorer';
import {RepositoryMixin} from '@loopback/repository';
import {RestApplication} from '@loopback/rest';
import {ServiceMixin} from '@loopback/service-proxy';
import {MySequence} from './sequence';
import * as path from 'path';
import {MySequence} from './sequence';

export class SoapCalculatorApplication extends BootMixin(
ServiceMixin(RepositoryMixin(RestApplication)),
Expand All @@ -18,6 +19,8 @@ export class SoapCalculatorApplication extends BootMixin(
// Set up default home page
this.static('/', path.join(__dirname, '../../public'));

this.component(RestExplorerComponent);

this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here
this.bootOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ describe('HomePage', () => {
.expect(200)
.expect('Content-Type', /text\/html/);
});

it('exposes self-hosted explorer', async () => {
await client
.get('/explorer/')
.expect(200)
.expect('Content-Type', /text\/html/)
.expect(/<title>LoopBack API Explorer/);
});
});
1 change: 1 addition & 0 deletions examples/todo-list/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@loopback/boot": "^1.0.4",
"@loopback/context": "^1.1.0",
"@loopback/core": "^1.1.0",
"@loopback/rest-explorer": "^0.1.0",
"@loopback/openapi-v3": "^1.1.1",
"@loopback/openapi-v3-types": "^1.0.1",
"@loopback/repository": "^1.0.4",
Expand Down
5 changes: 4 additions & 1 deletion examples/todo-list/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

import {BootMixin} from '@loopback/boot';
import {ApplicationConfig} from '@loopback/core';
import {RestExplorerComponent} from '@loopback/rest-explorer';
import {RepositoryMixin} from '@loopback/repository';
import {RestApplication} from '@loopback/rest';
import {MySequence} from './sequence';
import * as path from 'path';
import {MySequence} from './sequence';

export class TodoListApplication extends BootMixin(
RepositoryMixin(RestApplication),
Expand All @@ -22,6 +23,8 @@ export class TodoListApplication extends BootMixin(
// Set up default home page
this.static('/', path.join(__dirname, '../../public'));

this.component(RestExplorerComponent);

this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here
this.bootOptions = {
Expand Down
8 changes: 8 additions & 0 deletions examples/todo-list/test/acceptance/home-page.acceptance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ describe('HomePage', () => {
.expect(200)
.expect('Content-Type', /text\/html/);
});

it('exposes self-hosted explorer', async () => {
await client
.get('/explorer/')
.expect(200)
.expect('Content-Type', /text\/html/)
.expect(/<title>LoopBack API Explorer/);
});
});
1 change: 1 addition & 0 deletions examples/todo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"@loopback/boot": "^1.0.4",
"@loopback/context": "^1.1.0",
"@loopback/core": "^1.1.0",
"@loopback/rest-explorer": "^0.1.0",
"@loopback/openapi-v3": "^1.1.1",
"@loopback/openapi-v3-types": "^1.0.1",
"@loopback/repository": "^1.0.4",
Expand Down
5 changes: 4 additions & 1 deletion examples/todo/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

import {BootMixin} from '@loopback/boot';
import {ApplicationConfig} from '@loopback/core';
import {RestExplorerComponent} from '@loopback/rest-explorer';
import {RepositoryMixin} from '@loopback/repository';
import {RestApplication} from '@loopback/rest';
import {ServiceMixin} from '@loopback/service-proxy';
import {MySequence} from './sequence';
import * as path from 'path';
import {MySequence} from './sequence';

export class TodoListApplication extends BootMixin(
ServiceMixin(RepositoryMixin(RestApplication)),
Expand All @@ -23,6 +24,8 @@ export class TodoListApplication extends BootMixin(
// Set up default home page
this.static('/', path.join(__dirname, '../../public'));

this.component(RestExplorerComponent);

this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here
this.bootOptions = {
Expand Down
8 changes: 8 additions & 0 deletions examples/todo/test/acceptance/home-page.acceptance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ describe('HomePage', () => {
.expect(200)
.expect('Content-Type', /text\/html/);
});

it('exposes self-hosted explorer', async () => {
await client
.get('/explorer/')
.expect(200)
.expect('Content-Type', /text\/html/)
.expect(/<title>LoopBack API Explorer/);
});
});
12 changes: 11 additions & 1 deletion packages/cli/generators/app/templates/src/application.ts.ejs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import {BootMixin} from '@loopback/boot';
import {ApplicationConfig} from '@loopback/core';
import {
RestExplorerBindings,
RestExplorerComponent,
} from '@loopback/rest-explorer';
<% if (project.repositories) { -%>
import {RepositoryMixin} from '@loopback/repository';
<% } -%>
import {RestApplication} from '@loopback/rest';
<% if (project.services) { -%>
import {ServiceMixin} from '@loopback/service-proxy';
<% } -%>
import {MySequence} from './sequence';
import * as path from 'path';
import {MySequence} from './sequence';

<% if (project.appClassWithMixins) { -%>
export class <%= project.applicationName %> extends BootMixin(
Expand All @@ -28,6 +32,12 @@ export class <%= project.applicationName %> extends BootMixin(RestApplication) {
// Set up default home page
this.static('/', path.join(__dirname, '../../public'));
// Customize @loopback/rest-explorer configuration here
this.bind(RestExplorerBindings.CONFIG).to({
path: '/explorer',
});
this.component(RestExplorerComponent);
this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here
this.bootOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ describe('HomePage', () => {
.expect(200)
.expect('Content-Type', /text\/html/);
});

it('exposes self-hosted explorer', async () => {
await client
.get('/explorer/')
.expect(200)
.expect('Content-Type', /text\/html/)
.expect(/<title>LoopBack API Explorer/);
});
});
5 changes: 3 additions & 2 deletions packages/cli/generators/project/templates/package.json.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,12 @@
<% if (project.repositories) { -%>
"@loopback/repository": "<%= project.dependencies['@loopback/repository'] -%>",
<% } -%>
<% if (project.services) { -%>
"@loopback/rest": "<%= project.dependencies['@loopback/rest'] -%>",
<% if (project.services) { -%>
"@loopback/rest-explorer": "<%= project.dependencies['@loopback/rest-explorer'] -%>",
"@loopback/service-proxy": "<%= project.dependencies['@loopback/service-proxy'] -%>"
<% } else { -%>
"@loopback/rest": "<%= project.dependencies['@loopback/rest'] -%>"
"@loopback/rest-explorer": "<%= project.dependencies['@loopback/rest-explorer'] -%>"
<% } -%>
<% } else { /* NOT AN APPLICATION */-%>
"@loopback/core": "<%= project.dependencies['@loopback/core'] -%>"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
"@loopback/core": "<%= project.dependencies['@loopback/core'] -%>",
"@loopback/openapi-v3": "<%= project.dependencies['@loopback/openapi-v3'] -%>",
"@loopback/repository": "<%= project.dependencies['@loopback/repository'] -%>",
"@loopback/rest": "<%= project.dependencies['@loopback/rest'] -%>"
"@loopback/rest": "<%= project.dependencies['@loopback/rest'] -%>",
"@loopback/rest-explorer": "<%= project.dependencies['@loopback/rest-explorer'] -%>"
<% } else { -%>
"@loopback/core": "<%= project.dependencies['@loopback/core'] -%>"
<% } -%>
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@
"@loopback/http-caching-proxy": "^1.0.1",
"@loopback/http-server": "^1.0.2",
"@loopback/example-todo-list": "^1.1.1",
"@loopback/dist-util": "^0.4.0"
"@loopback/dist-util": "^0.4.0",
"@loopback/rest-explorer": "^0.1.0"
}
},
"copyright.owner": "IBM Corp.",
Expand Down
1 change: 1 addition & 0 deletions packages/rest-explorer/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
25 changes: 25 additions & 0 deletions packages/rest-explorer/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Copyright (c) IBM Corp. 2018. All Rights Reserved.
Node module: @loopback/rest-explorer
This project is licensed under the MIT License, full text below.

--------

MIT license

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
63 changes: 63 additions & 0 deletions packages/rest-explorer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# @loopback/rest-explorer

This module contains a component adding a self-hosted REST API Explorer to
LoopBack applications.

## Installation

```sh
npm install --save @loopback/rest-explorer
```

## Basic use

The component should be loaded in the constructor of your custom Application
class. Applications scaffolded by recent versions of our `lb4` CLI tool have the
self-hosted REST API Explorer pre-configured out of the box.

Start by importing the component class:

```ts
import {RestExplorerComponent} from '@loopback/rest-explorer';
```

In the constructor, add the component to your application:

```ts
this.component(RestExplorerComponent);
```

By default, API Explorer is mounted at `/explorer`. This path can be customized
via RestExplorer configuration as follows:

```ts
this.bind(RestExplorerBindings.CONFIG).to({
path: '/openapi/ui',
});
```

_NOTE: The Explorer UI's visual style is not customizable yet. Our recommended
solution is to create a fork of this module, make any style changes in the fork
and publish the modified module under a different name. The
[GitHub issue #2023](https://github.com/strongloop/loopback-next/issues/2023) is
requesting a configuration option for customizing the visual style, please
up-vote the issue and/or join the discussion if you are interested in this
feature._

## Contributions

- [Guidelines](https://github.com/strongloop/loopback-next/blob/master/docs/CONTRIBUTING.md)
- [Join the team](https://github.com/strongloop/loopback-next/issues/110)

## Tests

Run `npm test` from the root folder.

## Contributors

See
[all contributors](https://github.com/strongloop/loopback-next/graphs/contributors).

## License

MIT
9 changes: 9 additions & 0 deletions packages/rest-explorer/docs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"content": [
"index.ts",
"src/rest-explorer.component.ts",
"src/rest-explorer.keys.ts",
"src/rest-explorer.types.ts"
],
"codeSectionDepth": 4
}
6 changes: 6 additions & 0 deletions packages/rest-explorer/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/rest-explorer
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

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

module.exports = require('./dist');
8 changes: 8 additions & 0 deletions packages/rest-explorer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: @loopback/rest-explorer
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

// DO NOT EDIT THIS FILE
// Add any additional (re)exports to src/index.ts instead.
export * from './src';
Loading

0 comments on commit 4c165c7

Please sign in to comment.