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:
parent
62c489cba7
commit
7eae8deacb
18 changed files with 718 additions and 116 deletions
BIN
.env.swp
Normal file
BIN
.env.swp
Normal file
Binary file not shown.
|
@ -18,12 +18,6 @@ module Api
|
|||
create_action
|
||||
respond_with_resource
|
||||
end
|
||||
|
||||
def my_tokens
|
||||
authorize resource_class
|
||||
instantiate_collection
|
||||
respond_with_collection
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,8 +2,16 @@
|
|||
|
||||
# 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
|
||||
npm install
|
||||
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
|
||||
|
|
|
@ -70,9 +70,7 @@ Metamaps::Application.routes.draw do
|
|||
delete :stars, to: 'stars#destroy', on: :member
|
||||
end
|
||||
resources :synapses, only: [:index, :create, :show, :update, :destroy]
|
||||
resources :tokens, only: [:create, :destroy] do
|
||||
get :my_tokens, on: :collection
|
||||
end
|
||||
resources :tokens, only: [:index, :create, :destroy]
|
||||
resources :topics, only: [:index, :create, :show, :update, :destroy]
|
||||
resources :users, only: [:index, :show] do
|
||||
get :current, on: :collection
|
||||
|
|
|
@ -8,12 +8,12 @@ protocols: [ HTTPS ]
|
|||
documentation:
|
||||
- title: Getting Started
|
||||
content: !include pages/getting-started.md
|
||||
- title: Endpoints
|
||||
content: ""
|
||||
|
||||
securitySchemes:
|
||||
cookie: !include securitySchemes/cookie.raml
|
||||
token: !include securitySchemes/token.raml
|
||||
oauth_2_0: !include securitySchemes/oauth_2_0.raml
|
||||
securedBy: [ oauth_2_0 ]
|
||||
securedBy: [ cookie, token, oauth_2_0 ]
|
||||
|
||||
traits:
|
||||
pageable: !include traits/pageable.raml
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#type: collection
|
||||
securedBy: [ null, cookie, token, oauth_2_0 ]
|
||||
get:
|
||||
is: [ searchable: { searchFields: "name" }, orderable, pageable ]
|
||||
responses:
|
||||
|
@ -7,6 +8,7 @@ get:
|
|||
application/json:
|
||||
example: !include ../examples/metacodes.json
|
||||
/{id}:
|
||||
securedBy: [ null, cookie, token, oauth_2_0 ]
|
||||
#type: item
|
||||
get:
|
||||
responses:
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
#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:
|
||||
body:
|
||||
application/json:
|
||||
|
@ -11,14 +20,6 @@ post:
|
|||
body:
|
||||
application/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}:
|
||||
#type: item
|
||||
delete:
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#type: collection
|
||||
securedBy: [ null, cookie, token, oauth_2_0 ]
|
||||
get:
|
||||
is: [ searchable: { searchFields: "name" }, orderable, pageable ]
|
||||
responses:
|
||||
|
@ -6,6 +7,15 @@ get:
|
|||
body:
|
||||
application/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:
|
||||
#type: item
|
||||
get:
|
||||
|
@ -14,11 +24,3 @@ get:
|
|||
body:
|
||||
application/json:
|
||||
example: !include ../examples/current_user.json
|
||||
/{id}:
|
||||
#type: item
|
||||
get:
|
||||
responses:
|
||||
200:
|
||||
body:
|
||||
application/json:
|
||||
example: !include ../examples/user.json
|
||||
|
|
3
doc/api/pages/cookie_tutorial.md
Normal file
3
doc/api/pages/cookie_tutorial.md
Normal 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.
|
|
@ -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.
|
||||
|
|
41
doc/api/pages/oauth_2_0_tutorial.md
Normal file
41
doc/api/pages/oauth_2_0_tutorial.md
Normal 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.
|
25
doc/api/pages/token_tutorial.md
Normal file
25
doc/api/pages/token_tutorial.md
Normal 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.
|
3
doc/api/securitySchemes/cookie.raml
Normal file
3
doc/api/securitySchemes/cookie.raml
Normal file
|
@ -0,0 +1,3 @@
|
|||
description: !include ../pages/cookie_tutorial.md
|
||||
type: x-cookie
|
||||
displayName: Secured by cookie-based authentication
|
|
@ -1,5 +1,4 @@
|
|||
description: |
|
||||
OAuth 2.0 implementation
|
||||
description: !include ../pages/oauth_2_0_tutorial.md
|
||||
type: OAuth 2.0
|
||||
settings:
|
||||
authorizationUri: https://metamaps.cc/api/v2/oauth/authorize
|
||||
|
|
3
doc/api/securitySchemes/token.raml
Normal file
3
doc/api/securitySchemes/token.raml
Normal file
|
@ -0,0 +1,3 @@
|
|||
description: !include ../pages/token_tutorial.md
|
||||
type: x-token
|
||||
displayName: Secured by token-based authentication
|
61
doc/api/templates/item.nunjucks
Normal file
61
doc/api/templates/item.nunjucks
Normal 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>
|
314
doc/api/templates/resource.nunjucks
Normal file
314
doc/api/templates/resource.nunjucks
Normal 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">×</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 %}
|
232
doc/api/templates/template.nunjucks
Normal file
232
doc/api/templates/template.nunjucks
Normal 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>
|
Loading…
Reference in a new issue