revamp HTML template a bit for api docs (#757)

* my_tokens endpoint moved to normal index

* remove secured_by from metacodes/users

* ch ch ch changes

* mess with template

* fix securedBy

* convenience open

* gross authentication notes at the top of every endpoint

* better ordering

* move login tutorials into security tab

* oauth tutorial

* getting closer

* remove unneeded Endpoints header

* ok looks OK
This commit is contained in:
Devin Howard 2016-10-13 01:54:43 +08:00 committed by Connor Turland
parent 62c489cba7
commit 7eae8deacb
18 changed files with 718 additions and 116 deletions

BIN
.env.swp Normal file

Binary file not shown.

View file

@ -18,12 +18,6 @@ module Api
create_action create_action
respond_with_resource respond_with_resource
end end
def my_tokens
authorize resource_class
instantiate_collection
respond_with_collection
end
end end
end end
end end

View file

@ -2,8 +2,16 @@
# Note: you need to run `npm install` before using this script or raml2html won't be installed # Note: you need to run `npm install` before using this script or raml2html won't be installed
OLD_DIR=$(pwd)
cd $(dirname $0)/..
if [[ ! -x ./node_modules/.bin/raml2html ]]; then if [[ ! -x ./node_modules/.bin/raml2html ]]; then
npm install npm install
fi fi
./node_modules/.bin/raml2html -i ./doc/api/api.raml -o ./public/api/index.html ./node_modules/.bin/raml2html -i ./doc/api/api.raml -o ./public/api/index.html -t doc/api/templates/template.nunjucks
if [[ -x $(which open) ]]; then
open public/api/index.html
fi
cd $OLD_DIR

View file

@ -70,9 +70,7 @@ Metamaps::Application.routes.draw do
delete :stars, to: 'stars#destroy', on: :member delete :stars, to: 'stars#destroy', on: :member
end end
resources :synapses, only: [:index, :create, :show, :update, :destroy] resources :synapses, only: [:index, :create, :show, :update, :destroy]
resources :tokens, only: [:create, :destroy] do resources :tokens, only: [:index, :create, :destroy]
get :my_tokens, on: :collection
end
resources :topics, only: [:index, :create, :show, :update, :destroy] resources :topics, only: [:index, :create, :show, :update, :destroy]
resources :users, only: [:index, :show] do resources :users, only: [:index, :show] do
get :current, on: :collection get :current, on: :collection

View file

@ -8,12 +8,12 @@ protocols: [ HTTPS ]
documentation: documentation:
- title: Getting Started - title: Getting Started
content: !include pages/getting-started.md content: !include pages/getting-started.md
- title: Endpoints
content: ""
securitySchemes: securitySchemes:
cookie: !include securitySchemes/cookie.raml
token: !include securitySchemes/token.raml
oauth_2_0: !include securitySchemes/oauth_2_0.raml oauth_2_0: !include securitySchemes/oauth_2_0.raml
securedBy: [ oauth_2_0 ] securedBy: [ cookie, token, oauth_2_0 ]
traits: traits:
pageable: !include traits/pageable.raml pageable: !include traits/pageable.raml

View file

@ -1,4 +1,5 @@
#type: collection #type: collection
securedBy: [ null, cookie, token, oauth_2_0 ]
get: get:
is: [ searchable: { searchFields: "name" }, orderable, pageable ] is: [ searchable: { searchFields: "name" }, orderable, pageable ]
responses: responses:
@ -7,6 +8,7 @@ get:
application/json: application/json:
example: !include ../examples/metacodes.json example: !include ../examples/metacodes.json
/{id}: /{id}:
securedBy: [ null, cookie, token, oauth_2_0 ]
#type: item #type: item
get: get:
responses: responses:

View file

@ -1,4 +1,13 @@
#type: collection #type: collection
get:
description: |
A list of the current user's tokens.
is: [ searchable: { searchFields: description }, pageable, orderable ]
responses:
200:
body:
application/json:
example: !include ../examples/tokens.json
post: post:
body: body:
application/json: application/json:
@ -11,14 +20,6 @@ post:
body: body:
application/json: application/json:
example: !include ../examples/token.json example: !include ../examples/token.json
/my_tokens:
get:
is: [ searchable: { searchFields: description }, pageable, orderable ]
responses:
200:
body:
application/json:
example: !include ../examples/tokens.json
/{id}: /{id}:
#type: item #type: item
delete: delete:

View file

@ -1,4 +1,5 @@
#type: collection #type: collection
securedBy: [ null, cookie, token, oauth_2_0 ]
get: get:
is: [ searchable: { searchFields: "name" }, orderable, pageable ] is: [ searchable: { searchFields: "name" }, orderable, pageable ]
responses: responses:
@ -6,6 +7,15 @@ get:
body: body:
application/json: application/json:
example: !include ../examples/users.json example: !include ../examples/users.json
/{id}:
#type: item
securedBy: [ null, cookie, token, oauth_2_0 ]
get:
responses:
200:
body:
application/json:
example: !include ../examples/user.json
/current: /current:
#type: item #type: item
get: get:
@ -14,11 +24,3 @@ get:
body: body:
application/json: application/json:
example: !include ../examples/current_user.json example: !include ../examples/current_user.json
/{id}:
#type: item
get:
responses:
200:
body:
application/json:
example: !include ../examples/user.json

View file

@ -0,0 +1,3 @@
One way to access the API is through your browser. Log into metamaps.cc normally, then browse manually to https://metamaps.cc/api/v2/user/current. You should see a JSON description of your own user object in the database. You can browse any GET endpoint by simply going to that URL and appending query parameters in the URI.
To run a POST or DELETE request, you can use the Fetch API. See the example in the next section.

View file

@ -1,86 +1,2 @@
[Skip ahead to the endpoints.](#endpoints) There are three ways to log in: cookie-based authentication, token-based authentication, or OAuth 2. If you're testing the API or making simple scripts, cookie-based or token-based is the best. If you're developing and app and want users to be able to log into Metamaps inside your app, you'll be able to use the OAuth 2 mechanism. Check the security tab of any of the endpoints above for instructions on logging in.
There are three ways to log in: cookie-based authentication, token-based authentication, or OAuth 2. If you're testing the API or making simple scripts, cookie-based or token-based is the best. If you're developing and app and want users to be able to log into Metamaps inside your app, you'll be able to use the OAuth 2 mechanism.
### 1. Cookie-based authentication
One way to access the API is through your browser. Log into metamaps.cc normally, then browse manually to https://metamaps.cc/api/v2/user/current. You should see a JSON description of your own user object in the database. You can browse any GET endpoint by simply going to that URL and appending query parameters in the URI.
To run a POST or DELETE request, you can use the Fetch API. See the example in the next section.
### 2. Token-based authentication
If you are logged into the API via another means, you can create a token. Once you have this token, you can append it to a request. For example, opening a private window in your browser and browsing to `https://metamaps.cc/api/v2/user/current?token=...token here...` would show you your current user, even without logging in by another means.
To get a list of your current tokens, you can log in using cookie-based authentication and run the following fetch request in your browser console (assuming the current tab is on some page within the `metamaps.cc` website.
```javascript
fetch('/api/v2/tokens', {
method: 'GET',
credentials: 'same-origin' // needed to use the cookie-based auth
}).then(response => {
return response.json()
}).then(console.log).catch(console.error)
```
If this is your first time accessing the API, this list wil be empty. You can create a token using a similar method:
```javascript
fetch('/api/v2/tokens', {
method: 'POST',
credentials: 'same-origin'
}).then(response => {
return response.json()
}).then(payload => {
console.log(payload)
}).catch(console.error)
```
`payload.data.token` will contain a string which you can use to append to requests to access the API from anywhere.
### 3. OAuth 2 Authentication
We use a flow for Oauth 2 authentication called Authorization Code. It basically consists of an exchange of an `authorization` token for an `access token`. For more detailed info, check out the [RFC spec here](http://tools.ietf.org/html/rfc6749#section-4.1)
The first step is to register your client app.
#### Registering the client
Set up a new client in `/oauth/applications/new`. For testing purposes, you should fill in the redirect URI field with `urn:ietf:wg:oauth:2.0:oob`. This will tell it to display the authorization code instead of redirecting to a client application (that you don't have now).
#### Requesting authorization
To request the authorization token, you should visit the `/oauth/authorize` endpoint. You can do that either by clicking in the link to the authorization page in the app details or by visiting manually the URL:
```
http://metamaps.cc/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code
```
Once you are there, you should sign in and click on `Authorize`.
You will then see a response that contains your "authorization code", which you need to exchange for an access token.
#### Requesting the access token
To request the access token, you should use the returned code and exchange it for an access token. To do that you can use any HTTP client. Here's an example with `fetch`
```javascript
fetch('https://metamaps.cc/oauth/token?client_id=THE_ID&client_secret=THE_SECRET&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=urn:ietf:wg:oauth:2.0:oob', {
method: 'POST',
credentials: 'same-origin'
}).then(response => {
return response.json()
}).then(console.log).catch(console.error)
```
The response will look like
```json
{
"access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54",
"token_type": "bearer",
"expires_in": 7200,
"refresh_token": "8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1"
}
```
You can now make requests to the API with the access token returned.

View file

@ -0,0 +1,41 @@
We use a flow for Oauth 2 authentication called Authorization Code. It basically consists of an exchange of an `authorization` token for an `access token`. For more detailed info, check out the [RFC spec here](http://tools.ietf.org/html/rfc6749#section-4.1)
The first step is to register your client app.
#### Registering the client
Set up a new client in `/oauth/applications/new`. For testing purposes, you should fill in the redirect URI field with `urn:ietf:wg:oauth:2.0:oob`. This will tell it to display the authorization code instead of redirecting to a client application (that you don't have now).
#### Requesting authorization
To request the authorization token, you should visit the `/oauth/authorize` endpoint. You can do that either by clicking in the link to the authorization page in the app details or by visiting manually the URL:
```
http://metamaps.cc/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code
```
Once you are there, you should sign in and click on `Authorize`.
You will then see a response that contains your "authorization code", which you need to exchange for an access token.
#### Requesting the access token
To request the access token, you should use the returned code and exchange it for an access token. To do that you can use any HTTP client. Here's an example with `fetch`
```javascript
fetch('https://metamaps.cc/oauth/token?client_id=THE_ID&client_secret=THE_SECRET&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=urn:ietf:wg:oauth:2.0:oob', {
method: 'POST',
credentials: 'same-origin'
}).then(response => {
return response.json()
}).then(console.log).catch(console.error)
# The response will be like
{
"access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54",
"token_type": "bearer",
"expires_in": 7200,
"refresh_token": "8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1"
}
```
You can now make requests to the API with the access token returned.

View file

@ -0,0 +1,25 @@
If you are logged into the API via another means, you can create a token. Once you have this token, you can append it to a request. For example, opening a private window in your browser and browsing to `https://metamaps.cc/api/v2/user/current?token=...token here...` would show you your current user, even without logging in by another means.
To get a list of your current tokens, you can log in using cookie-based authentication and run the following fetch request in your browser console (assuming the current tab is on some page within the `metamaps.cc` website.
```
fetch('/api/v2/tokens', {
method: 'GET',
credentials: 'same-origin' // needed to use the cookie-based auth
}).then(response => {
return response.json()
}).then(console.log).catch(console.error)
```
If this is your first time accessing the API, this list wil be empty. You can create a token using a similar method:
```
fetch('/api/v2/tokens', {
method: 'POST',
credentials: 'same-origin'
}).then(response => {
return response.json()
}).then(console.log).catch(console.error)
```
`payload.data.token` will contain a string which you can use to append to requests to access the API from anywhere.

View file

@ -0,0 +1,3 @@
description: !include ../pages/cookie_tutorial.md
type: x-cookie
displayName: Secured by cookie-based authentication

View file

@ -1,5 +1,4 @@
description: | description: !include ../pages/oauth_2_0_tutorial.md
OAuth 2.0 implementation
type: OAuth 2.0 type: OAuth 2.0
settings: settings:
authorizationUri: https://metamaps.cc/api/v2/oauth/authorize authorizationUri: https://metamaps.cc/api/v2/oauth/authorize

View file

@ -0,0 +1,3 @@
description: !include ../pages/token_tutorial.md
type: x-token
displayName: Secured by token-based authentication

View file

@ -0,0 +1,61 @@
<li>
{% if item.displayName %}
<strong>{{ item.displayName }}</strong>:
{% else %}
<strong>{{ item.key }}</strong>:
{% endif %}
{% if not item.structuredValue %}
<em>
{%- if item.required -%}required {% endif -%}
(
{%- if item.enum -%}
{%- if item.enum.length === 1 -%}
{{ item.enum.join(', ') }}
{%- else -%}
one of {{ item.enum.join(', ') }}
{%- endif -%}
{%- else -%}
{{ item.type }}
{%- endif -%}
{%- if item.default or item.default == 0 or item.default == false %} - default: {{ item.default }}{%- endif -%}
{%- if item.repeat %} - repeat: {{ item.repeat }}{%- endif -%}
{%- if item.type == 'string' -%}
{%- if item.minLength or item.minLength == 0 %} - minLength: {{ item.minLength }}{%- endif -%}
{%- if item.maxLength or item.maxLength == 0 %} - maxLength: {{ item.maxLength }}{%- endif -%}
{%- else -%}
{%- if item.minimum or item.minimum == 0 %} - minimum: {{ item.minimum }}{%- endif -%}
{%- if item.maximum or item.maximum == 0 %} - maximum: {{ item.maximum }}{%- endif -%}
{%- endif -%}
{%- if item.pattern %} - pattern: {{ item.pattern }}{%- endif -%}
)
</em>
{% endif %}
{% markdown %}
{{ item.description }}
{% endmarkdown %}
{#
{% if item.type %}
<p><strong>Type</strong>:</p>
<pre><code>{{ item.type | escape }}</code></pre>
{% endif %}
#}
{% if item.examples.length %}
<p><strong>Examples</strong>:</p>
{% for example in item.examples %}
{% if item.type == 'string' %}
<pre>{{ example | escape }}</pre>
{% else %}
<pre><code>{{ example | escape }}</code></pre>
{% endif %}
{% endfor %}
{% endif %}
{% if item.structuredValue %}
<pre><code>{{ item.structuredValue | dump }}</code></pre>
{% endif %}
</li>

View file

@ -0,0 +1,314 @@
{% if (resource.methods or (resource.description and resource.parentUrl)) %}
<div class="panel panel-white">
<div class="panel-heading">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" href="#panel_{{ resource.uniqueId }}">
<span class="parent">{{ resource.parentUrl }}</span>{{ resource.relativeUri }}
</a>
<span class="methods">
{% for method in resource.methods %}
<a href="#{{ resource.uniqueId }}_{{ method.method }}"><!-- modal shown by hashchange event -->
<span class="badge badge_{{ method.method }}">{{ method.method }}
{% if method.securedBy.length %}
{% if method.securedBy | first == null %}
<span class="glyphicon glyphicon-transfer" title="Authentication not required"></span>
{% endif %}
<span class="glyphicon glyphicon-lock" title="Authentication required"></span>
{% endif %}
</span>
</a>
{% endfor %}
</span>
</h4>
</div>
<div id="panel_{{ resource.uniqueId }}" class="panel-collapse collapse">
<div class="panel-body">
{% if resource.parentUrl %}
{% if resource.description %}
<div class="resource-description">
{% markdown %}
{{ resource.description }}
{% endmarkdown %}
</div>
{% endif %}
{% endif %}
<div class="list-group">
{% for method in resource.methods %}
<div onclick="window.location.href = '#{{ resource.uniqueId }}_{{ method.method }}'" class="list-group-item">
<span class="badge badge_{{ method.method }}">
{{ method.method }}
{% if method.securedBy.length %}
{% if method.securedBy | first == null %}
<span class="glyphicon glyphicon-transfer" title="Authentication not required"></span>
{% endif %}
<span class="glyphicon glyphicon-lock" title="Authentication required"></span>
{% endif %}
</span>
<div class="method_description">
{% markdown %}
{{ method.description}}
{% endmarkdown %}
</div>
<div class="clearfix"></div>
</div>
{% endfor %}
</div>
</div>
</div>
{% for method in resource.methods %}
<div class="modal fade" tabindex="0" id="{{ resource.uniqueId }}_{{ method.method }}">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">
<span class="badge badge_{{ method.method }}">
{{ method.method }}
{% if method.securedBy.length %}
{% if method.securedBy | first == null %}
<span class="glyphicon glyphicon-transfer" title="Authentication not required"></span>
{% endif %}
<span class="glyphicon glyphicon-lock" title="Authentication required"></span>
{% endif %}
</span>
<span class="parent">{{ resource.parentUrl }}</span>{{ resource.relativeUri }}
</h4>
</div>
<div class="modal-body">
{% if method.description %}
<div class="alert alert-info">
{% markdown %}
{{ method.description}}
{% endmarkdown %}
</div>
{% endif %}
<!-- Nav tabs -->
<ul class="nav nav-tabs">
{% if method.allUriParameters.length or method.queryString or method.queryParameters or method.headers or method.body %}
<li class="active">
<a href="#{{ resource.uniqueId }}_{{ method.method }}_request" data-toggle="tab">Request</a>
</li>
{% endif %}
{% if method.responses %}
<li{%
if not method.allUriParameters.length and not method.queryParameters
and not method.queryString
and not method.headers and not method.body
%} class="active"{%
endif
%}>
<a href="#{{ resource.uniqueId }}_{{ method.method }}_response" data-toggle="tab">Response</a>
</li>
{% endif %}
{% if method.securedBy.length %}
<li>
<a href="#{{ resource.uniqueId }}_{{ method.method }}_securedby" data-toggle="tab">Security</a>
</li>
{% endif %}
</ul>
<!-- Tab panes -->
<div class="tab-content">
{% if method.allUriParameters.length or method.queryString or method.queryParameters or method.headers or method.body %}
<div class="tab-pane active" id="{{ resource.uniqueId }}_{{ method.method }}_request">
{% if resource.allUriParameters.length %}
<h3>URI Parameters</h3>
<ul>
{% for item in resource.allUriParameters %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% if method.annotations.length %}
<h3>Annotations</h3>
<ul>
{% for item in method.annotations %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% if method.headers.length %}
<h3>Headers</h3>
<ul>
{% for item in method.headers %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% if method.queryString and method.queryString.properties.length %}
<h3>Query String</h3>
<ul>
{% for item in method.queryString.properties %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% if method.queryParameters.length %}
<h3>Query Parameters</h3>
<ul>
{% for item in method.queryParameters %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% if method.body %}
<h3>Body</h3>
{% for b in method.body %}
<p><strong>Type: {{ b.key }}</strong></p>
{#
{% if b.type %}
<p><strong>Type</strong>:</p>
<pre><code>{{ b.type | escape }}</code></pre>
{% endif %}
#}
{% if b.properties.length %}
<strong>Properties</strong>
<ul>
{% for item in b.properties %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% if b.examples.length %}
<p><strong>Examples</strong>:</p>
{% for example in b.examples %}
<pre><code>{{ example | escape }}</code></pre>
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
</div>
{% endif %}
{% if method.responses %}
<div class="tab-pane{%
if not method.allUriParameters.length and not method.queryParameters.length
and not method.queryString
and not method.headers.length and not method.body.length
%} active{%
endif
%}" id="{{ resource.uniqueId }}_{{ method.method }}_response">
{% for response in method.responses %}
<h2>HTTP status code <a href="http://httpstatus.es/{{ response.code }}" target="_blank">{{ response.code }}</a></h2>
{% markdown %}
{{ response.description}}
{% endmarkdown %}
{% if response.headers.length %}
<h3>Headers</h3>
<ul>
{% for item in response.headers %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% if response.body.length %}
<h3>Body</h3>
{% for b in response.body %}
<p><strong>Type: {{ b.key }}</strong></p>
{#
{% if b.type %}
<p><strong>Type</strong>:</p>
<pre><code>{{ b.type | escape }}</code></pre>
{% endif %}
#}
{% if b.properties.length %}
<strong>Properties</strong>
<ul>
{% for item in b.properties %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% if b.examples.length %}
<p><strong>Examples</strong>:</p>
{% for example in b.examples %}
<pre><code>{{ example | escape }}</code></pre>
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</div>
{% endif %}
{% if method.securedBy.length %}
<div class="tab-pane" id="{{ resource.uniqueId }}_{{ method.method }}_securedby">
{% for securedBy in method.securedBy %}
{% if securedBy == null %}
<div class="alert alert-info">
<span class="glyphicon glyphicon-transfer" title="Authentication not required"></span>
This route can be accessed anonymously.</h1>
</div>
{% else %}
{% set securityScheme = securitySchemes[securedBy] %}
<div class="alert alert-warning">
{% set securedByScopes = renderSecuredBy(securedBy) %}
<span class="glyphicon glyphicon-lock" title="Authentication required"></span> Secured by {{ securedByScopes }}
{% set securityScheme = securitySchemes[securedBy] %}
{% if securityScheme.description %}
{% markdown %}
{{ securityScheme.description }}
{% endmarkdown %}
{% endif %}
{% if securityScheme.describedBy.headers.length %}
<h3>Headers</h3>
<ul>
{% for item in securityScheme.describedBy.headers %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% for response in securityScheme.describedBy.responses.length %}
<h2>HTTP status code <a href="http://httpstatus.es/{{ response.code }}" target="_blank">{{ response.code }}</a></h2>
{% markdown %}
{{ response.description}}
{% endmarkdown %}
{% if response.headers.length %}
<h3>Headers</h3>
<ul>
{% for item in response.headers %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% for resource in resource.resources %}
{% include "./resource.nunjucks" %}
{% endfor %}

View file

@ -0,0 +1,232 @@
<!DOCTYPE HTML>
<html>
<head>
<title>{{ title }} API documentation</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="generator" content="https://github.com/raml2html/raml2html {{ config.raml2HtmlVersion }}">
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/styles/default.min.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.0.min.js"></script>
<script type="text/javascript" src="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.3.0/highlight.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('.page-header pre code, .top-resource-description pre code, .modal-body pre code').each(function(i, block) {
hljs.highlightBlock(block);
});
$('[data-toggle]').click(function() {
var selector = $(this).data('target') + ' pre code';
$(selector).each(function(i, block) {
hljs.highlightBlock(block);
});
});
// open modal on hashes like #_action_get
$(window).bind('hashchange', function(e) {
var anchor_id = document.location.hash.substr(1); //strip #
var element = $('#' + anchor_id);
// do we have such element + is it a modal? --> show it
if (element.length && element.hasClass('modal')) {
element.modal('show');
}
});
// execute hashchange on first page load
$(window).trigger('hashchange');
// remove url fragment on modal hide
$('.modal').on('hidden.bs.modal', function() {
try {
if (history && history.replaceState) {
history.replaceState({}, '', '#');
}
} catch(e) {}
});
});
</script>
<style>
.hljs {
background: transparent;
}
.parent {
color: #999;
}
.list-group-item > .badge {
float: none;
margin-right: 6px;
}
.panel-title > .methods {
float: right;
}
.badge {
border-radius: 0;
text-transform: uppercase;
width: 70px;
font-weight: normal;
color: #f3f3f6;
line-height: normal;
}
.badge_get {
background-color: #63a8e2;
}
.badge_post {
background-color: #6cbd7d;
}
.badge_put {
background-color: #22bac4;
}
.badge_delete {
background-color: #d26460;
}
.badge_patch {
background-color: #ccc444;
}
.list-group, .panel-group {
margin-bottom: 0;
}
.panel-group .panel+.panel-white {
margin-top: 0;
}
.panel-group .panel-white {
border-bottom: 1px solid #F5F5F5;
border-radius: 0;
}
.panel-white:last-child {
border-bottom-color: white;
-webkit-box-shadow: none;
box-shadow: none;
}
.panel-white .panel-heading {
background: white;
}
.tab-pane ul {
padding-left: 2em;
}
.tab-pane h1 {
font-size: 1.3em;
}
.tab-pane h2 {
font-size: 1.2em;
padding-bottom: 4px;
border-bottom: 1px solid #ddd;
}
.tab-pane h3 {
font-size: 1.1em;
}
.tab-content {
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
border-bottom: 1px solid #ddd;
padding: 10px;
}
#sidebar {
margin-top: 30px;
padding-right: 5px;
overflow: auto;
height: 90%;
}
.top-resource-description {
border-bottom: 1px solid #ddd;
background: #fcfcfc;
padding: 15px 15px 0 15px;
margin: -15px -15px 10px -15px;
}
.resource-description {
border-bottom: 1px solid #fcfcfc;
background: #fcfcfc;
padding: 15px 15px 0 15px;
margin: -15px -15px 10px -15px;
}
.resource-description p:last-child {
margin: 0;
}
.list-group .badge {
float: left;
}
.method_description {
margin-left: 85px;
}
.method_description p:last-child {
margin: 0;
}
.list-group-item {
cursor: pointer;
}
.list-group-item:hover {
background-color: #f5f5f5;
}
pre code {
overflow: auto;
word-wrap: normal;
white-space: pre;
}
</style>
</head>
<body data-spy="scroll" data-target="#sidebar">
<div class="container">
<div class="row">
<div class="col-md-9" role="main">
<div class="page-header">
<h1>{{ title }} API documentation{% if version %} <small>version {{ version }}</small>{% endif %}</h1>
<p>{{ baseUri }}</p>
{% if baseUriParameters %}
<ul>
{% for item in baseUriParameters %}
{% include "./item.nunjucks" %}
{% endfor %}
</ul>
{% endif %}
</div>
{% for resource in resources %}
<div class="panel panel-default">
<div class="panel-heading">
<h3 id="{{ resource.uniqueId }}" class="panel-title">{% if resource.displayName %}{{ resource.displayName}}{% else %}{{ resource.relativeUri }}{% endif %}</h3>
</div>
<div class="panel-body">
{% if resource.description %}
<div class="top-resource-description">
{% markdown %}
{{ resource.description }}
{% endmarkdown %}
</div>
{% endif %}
<div class="panel-group">
{% include "./resource.nunjucks" %}
</div>
</div>
</div>
{% endfor %}
{% for chapter in documentation %}
<h3 id="{{ chapter.uniqueId }}"><a href="#{{ chapter.uniqueId }}">{{ chapter.title }}</a></h3>
{% markdown %}
{{ chapter.content }}
{% endmarkdown %}
{% endfor %}
</div>
<div class="col-md-3">
<div id="sidebar" class="hidden-print affix" role="complementary">
<ul class="nav nav-pills nav-stacked">
{% for resource in resources %}
<li><a href="#{{ resource.uniqueId}}">{% if resource.displayName %}{{ resource.displayName}}{% else %}{{ resource.relativeUri }}{% endif %}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
</body>
</html>