Skip to content

Commit

Permalink
use vision for rendering the api's html into a page
Browse files Browse the repository at this point in the history
  • Loading branch information
frankthelen committed Feb 6, 2018
1 parent d20eacf commit 4ee4b7e
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 57 deletions.
65 changes: 64 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,67 @@ The plugin provides the following options:
| `cache` | `{ privacy: 'public', expiresIn: 60 * 60 * 1000 } // one hour` | Hapi's `route.options.cache` to be assigned to the static documentation endpoint. Please refer to the [Hapi docs](https://hapijs.com/api#-routeoptionscache) for more information. |
| `auth` | - | Hapi's `route.options.auth` to be assigned to the static documentation endpoint. Please refer to the [Hapi docs](https://hapijs.com/api#-routeoptionsauth) for more information. By default, this option is not set, i.e., inheriting auth settings from Hapi's `server.options.routes.auth`. |
| `headers` | `{}` | The request's `authorization` header is automatically forwarded to the `/swagger.json` endpoint. If you need any additional headers, add them through the `headers` option. |
| `frame` | - | Filename or file descriptor of the html frame for the static documentation page. Supporting placeholders `{{title}}` and `{{content}}`. `{{title}}` will be replaced with the API's title. `{{content}}` will be replaced with the rendered html of the API. File encoding is UTF-8. |
| `template` | - | This plugin prefers `vision` to finally render the api into a page. The `template` option is the template filename and path, relative to the templates path configured via the server views manager. The api data will be provided into the view context, e.g., use `{{api.info.title}}` in the template to get hold of the api title. Use `{{{api.html}}}` (three curly brackets here!) in the template for rendering the api's html content. |
| `viewOptions` | `{}` | The options passed to the view via `h.view()`. If your default layout does not provide the Bootstrap 4 CSS resources (Bootstrap's JS is not needed), you should provide a special layout for this. See the example below. |

## View Example

This example works with the following plugin options:
```js
{ template: 'api', // referring to the view file `api.html` below
viewOptions: {
layout: 'api-layout' // referring to the layout file `api-layout.html` below
}
}
```

View example, e.g., `api.html` to be placed into your view location:
```html
{{{api.html}}}
```

Layout example, e.g., `api-layout.html` to be placed into your layout location:
```html
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
<link href='/public/api.css' rel='stylesheet' type='text/css' />
<title>{{api.info.title}}</title>
</head>
<body>
<div class="container">
{{{content}}}
<footer>Your company; for internal use only</footer>
</div>
</body>
</html>
```

Style example, e.g., `api.css` to be placed into `/public`, containing some minor adjustments to Bootstrap 4:
```css
.h2, h2 {
margin-top: 1rem;
}
.h4, h4 {
margin-top: .5rem;
}
.card {
margin-bottom: 1rem;
}
.o2h-description p {
color: grey;
}
.card .card-body .h4, .card .card-body h4 {
border-top: 1px solid #eee;
margin-top: 1rem;
padding-top: 1rem;
}
.card .card-body .h5, .card .card-body h5 {
margin-top: 1rem;
}
.card .card-body .o2h-description p {
margin-bottom: 0;
}
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
},
"peerDependencies": {
"hapi": ">=17.x.x",
"hapi-swagger": ">=9.x.x"
"vision": ">=5.x.x"
},
"devDependencies": {
"babel-eslint": "^8.2.1",
Expand Down
10 changes: 8 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const register = async (server, {
cache = { privacy: 'public', expiresIn: 60 * 60 * 1000 }, // one hour
auth, // if undefined, inheriting auth settings from server.options.routes.auth
headers: additionalHeaders = {},
frame: framePath = path.join(__dirname, 'frame.html'),
template,
viewOptions = {},
}) => {
server.route({
method: 'GET',
Expand All @@ -36,7 +37,12 @@ const register = async (server, {
},
});
const html = openapi2html(api);
const frame = await readFile(framePath, 'UTF-8');
// the preferred way is to render the page via `view`
if (h.view && template) {
return h.view(template, { api: { ...api, html } }, viewOptions);
}
// as a fallback, use own template
const frame = await readFile(path.join(__dirname, 'frame.html'), 'UTF-8');
const page = frame
.replace(/\{\{content\}\}/, html)
.replace(/\{\{title\}\}/, api.info.title);
Expand Down
53 changes: 0 additions & 53 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const HapiSwagger = require('hapi-swagger');
const Inert = require('inert');
const Vision = require('vision');
const fs = require('fs');
const path = require('path');
const sinon = require('sinon');
const HapiSwaggerStatic = require('..');
require('./setupTests');
Expand Down Expand Up @@ -177,58 +176,6 @@ describe('hapi-swagger-static with specific `swaggerEndpoint` option', async ()
}));
});

describe('hapi-swagger-static with specific `frame` option', async () => {
let server;

beforeEach(async () => {
server = await setup({
pluginOptions: { frame: path.join(__dirname, 'testframe.html') },
});
});

afterEach(async () => {
await server.stop();
});

it('should create /documentation.html with custom frame', () =>
server
.inject({
url: '/documentation.html',
})
.should.be.fulfilled.then((response) => {
const { statusCode, payload } = response;
expect(statusCode).to.be.equal(200);
expect(payload).to.contain('<html>');
expect(payload).to.contain('<title>API Documentation 4711</title>');
expect(payload).to.contain('<h1>API Documentation 4711</h1>');
expect(payload).to.contain('<p>this is a test frame</p>');
}));
});

describe('hapi-swagger-static with invalid `frame` option', async () => {
let server;

beforeEach(async () => {
server = await setup({
pluginOptions: { frame: 'bla.html' },
});
});

afterEach(async () => {
await server.stop();
});

it('should return http 500', () =>
server
.inject({
url: '/documentation.html',
})
.should.be.fulfilled.then((response) => {
const { statusCode } = response;
expect(statusCode).to.be.equal(500);
}));
});

describe('hapi-swagger-static with error while plugin registration', async () => {
let server;

Expand Down

0 comments on commit 4ee4b7e

Please sign in to comment.