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
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
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: |
|
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
|
||||||
|
|
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