-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2626 from opf/feature/add_api_documentation_layouts
Add aglio template layout files
- Loading branch information
Showing
3 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<!---- copyright | ||
OpenProject is a project management system. | ||
Copyright (C) 2012-2015 the OpenProject Foundation (OPF) | ||
This program is free software; you can redistribute it and/or | ||
modify it under the terms of the GNU General Public License version 3. | ||
OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: | ||
Copyright (C) 2006-2013 Jean-Philippe Lang | ||
Copyright (C) 2010-2013 the ChiliProject Team | ||
This program is free software; you can redistribute it and/or | ||
modify it under the terms of the GNU General Public License | ||
as published by the Free Software Foundation; either version 2 | ||
of the License, or (at your option) any later version. | ||
This program is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
You should have received a copy of the GNU General Public License | ||
along with this program; if not, write to the Free Software | ||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
See doc/COPYRIGHT.rdoc for more details. | ||
++--> | ||
|
||
API Documentation Layouts | ||
------------------------- | ||
|
||
## API Version 3 | ||
|
||
_Status: under development_ | ||
|
||
The documentation for APIv3 is written in the [API Blueprint Format](http://apiblueprint.org/). | ||
|
||
You can use [aglio](https://github.com/danielgtaylor/aglio) to generate HTML documentation. | ||
Aglio supports the use of templates to adjust the ouput to your own needs. OpenProject is | ||
using the openproject.jade template file. To generate the documentation using the | ||
openproject.jade layout file the following command: | ||
|
||
```bash | ||
aglio -t openproject-layout.jade -i ../apiv3-documentation.api -o api.html | ||
|
||
``` | ||
|
261 changes: 261 additions & 0 deletions
261
doc/api_documentation_layouts/openproject-includes.jade
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,261 @@ | ||
//- Common mixins that templates can share | ||
mixin Nav(multipage, collapsible) | ||
div.col-md-3 | ||
ul.menu(id="op-api-menu") | ||
- var menuItem = 'menu-item'; | ||
each resourceGroup in api.resourceGroups | ||
|
||
- var randomGroupIndex = Math.random().toString(16).slice(2) | ||
- var groupHasChildren = resourceGroup.resources.length ? menuItem + '-has-children' : '' | ||
- var resourceGroupClasses = [menuItem, menuItem + '-type-post_type', menuItem + '-object-page', 'dropdown', menuItem + '-' + randomGroupIndex, groupHasChildren] | ||
|
||
li(class=resourceGroupClasses, id=[menuItem + '-' + randomGroupIndex]) | ||
a.menu-item-link(href="##{slug(resourceGroup.name)}") | ||
= resourceGroup.name || 'Resource Group' | ||
if resourceGroup.resources.length | ||
a.dropdown-toggle.toggle-open-menu(href="##{slug(resourceGroup.name)}", data-toggle="dropdown") | ||
i.icon.icon-arrow-right5-2 | ||
i.icon.icon-arrow-right5-3 | ||
ul.dropdown-menu | ||
each resource in resourceGroup.resources | ||
- var randomResourceIndex = Math.random().toString(16).slice(2) | ||
- var resourceHasChildren = resource.length ? menuItem + '-has-children' : '' | ||
- var resourceClasses = [menuItem, menuItem + '-type-post_type', menuItem + '-object-page', 'dropdown', menuItem + '-' + randomResourceIndex, resourceHasChildren] | ||
li(class=resourceClasses, id=[menuItem + '-' + randomResourceIndex]) | ||
a.menu-item-link(href="##{slug(resourceGroup.name)}-#{slug(resource.name)}") | ||
= resource.name || 'Resource' | ||
if (resource.actions.length != 1) | ||
a.dropdown-toggle.toggle-open-menu(href="##{slug(resourceGroup.name)}-#{slug(resource.name)}", data-toggle="dropdown") | ||
i.icon.icon-arrow-right5-2 | ||
i.icon.icon-arrow-right5-3 | ||
ul.dropdown-menu | ||
each action in resource.actions | ||
- var randomActionIndex = Math.random().toString(16).slice(2) | ||
- var resourceActionClasses = [menuItem, menuItem + '-type-post_type', menuItem + '-object-page', 'dropdown', menuItem + '-' + randomActionIndex] | ||
li(class=resourceActionClasses, id=[menuItem + '-' + randomActionIndex]) | ||
a.menu-item-link(href="##{slug(resourceGroup.name)}-#{slug(resource.name)}-#{slug(action.method)}") | ||
= action.name || action.method + ' ' + resource.uriTemplate | ||
else | ||
each meta in api.metadata | ||
if meta.name == 'HOST' | ||
p(style="text-align: center; word-wrap: break-word;") | ||
a(href=meta.value)= meta.value | ||
|
||
mixin Parameters(params) | ||
ul.list-group | ||
li.list-group-item.bg-default: strong Parameters | ||
li.list-group-item | ||
dl.dl-horizontal | ||
each param in params | ||
dt= param.name | ||
dd | ||
code= param.type | ||
| | ||
if param.required | ||
span.required (required) | ||
else | ||
span (optional) | ||
| | ||
if param.default | ||
span.text-info.default | ||
strong Default: | ||
span= param.default | ||
| | ||
if param.example | ||
span.text-muted.example | ||
strong Example: | ||
span= param.example | ||
!= markdown(param.description) | ||
if param.values.length | ||
p | ||
strong Choices: | ||
each value in param.values | ||
code= value.value | ||
= ' ' | ||
|
||
mixin RequestResponse(title, request, resourceGroup, resource, action) | ||
- var id = hash(resourceGroup.name.toString() + resource.name.toString() + action.name.toString() + action.method.toString() + title.toString() + request.name.toString() + request.headers.toString() + request.body.toString() + request.schema.toString()) | ||
- var content = request.description || Object.keys(request.headers).length || request.body || request.schema | ||
li.list-group-item | ||
strong | ||
= title | ||
if request.name | ||
| | ||
code= request.name | ||
if content | ||
a.pull-right.collapsed(data-toggle="collapse", data-target="##{id}") | ||
span.closed Show | ||
span.open Hide | ||
if content | ||
li.list-group-item.panel-collapse.collapse(id=id) | ||
if request.description | ||
.description!= markdown(request.description) | ||
|
||
if Object.keys(request.headers).length | ||
h5 Headers | ||
pre | ||
code | ||
each item in request.headers | ||
!= highlight(item.name + ': ' + item.value, 'http') | ||
br | ||
if request.body | ||
h5 Body | ||
pre | ||
code!= highlight(request.body) | ||
if request.schema | ||
h5 Schema | ||
pre | ||
code!= highlight(request.schema) | ||
|
||
|
||
mixin Examples(resourceGroup, resource, action) | ||
ul.list-group | ||
each example in action.examples | ||
each request in example.requests | ||
+RequestResponse('Request', request, resourceGroup, resource, action) | ||
each response in example.responses | ||
+RequestResponse('Response', response, resourceGroup, resource, action) | ||
|
||
mixin ResourceGroup(resourceGroup, getButtonClass, multipage) | ||
.panel.panel-default | ||
.panel-heading | ||
h3(id="#{slug(resourceGroup.name)}") | ||
= resourceGroup.name || 'Resource Group' | ||
| | ||
a(href="##{multipage ? 'page:' : ''}#{slug(resourceGroup.name)}") | ||
i.fa.fa-link | ||
.panel-body | ||
if resourceGroup.description | ||
!= markdown(resourceGroup.description) | ||
each resource in resourceGroup.resources | ||
h4(id="#{slug(resourceGroup.name)}-#{slug(resource.name)}") | ||
= resource.name || 'Resources' | ||
| | ||
a(href="##{(multipage ? 'page:' + slug(resourceGroup.name) + ',header:' : '')}#{slug(resourceGroup.name)}-#{slug(resource.name)}") | ||
i.fa.fa-link | ||
if resource.description | ||
!= markdown(resource.description) | ||
each action in resource.actions | ||
case action.method | ||
when 'POST': - var panelClass = 'panel-success' | ||
when 'GET': - var panelClass = 'panel-info' | ||
when 'PUT': - var panelClass = 'panel-warning' | ||
when 'PATCH': - var panelClass = 'panel-warning' | ||
when 'DELETE': - var panelClass = 'panel-danger' | ||
default: - var panelClass = 'panel-default' | ||
section.panel(class=panelClass, id="#{slug(resourceGroup.name)}-#{slug(resource.name)}-#{slug(action.method)}") | ||
.panel-heading | ||
if action.name | ||
div(style="float:right") | ||
span(style="text-transform: lowercase")= action.name | ||
case action.method | ||
when 'POST': - var btnClass = 'btn-success' | ||
when 'GET': - var btnClass = 'btn-' + getButtonClass | ||
when 'PUT': - var btnClass = 'btn-warning' | ||
when 'PATCH': - var btnClass = 'btn-warning' | ||
when 'DELETE': - var btnClass = 'btn-danger' | ||
default: - var btnClass = 'btn-default' | ||
div(style="float:left") | ||
a.btn.btn-xs(class=btnClass, href="##{(multipage ? 'page:' + slug(resourceGroup.name) + ',header:' : '')}#{slug(resourceGroup.name)}-#{slug(resource.name)}-#{slug(action.method)}")= action.method | ||
div(style="overflow:hidden" class="panel-heading-code") | ||
code= resource.uriTemplate | ||
if action.description | ||
.panel-body!= markdown(action.description) | ||
|
||
- var params = action.parameters.length ? action.parameters : resource.parameters | ||
if params.length | ||
+Parameters(params) | ||
if action.examples | ||
+Examples(resourceGroup, resource, action) | ||
|
||
mixin Paginate(resourceGroups, index) | ||
if index < resourceGroups.length - 1 | ||
a.btn.btn-default.pull-right(href="#page:#{slug(resourceGroups[index + 1].name)}") | ||
| Next | ||
i.fa.fa-arrow-circle-right | ||
|
||
if index > 0 | ||
a.btn.btn-default(href="#page:#{slug(resourceGroups[index - 1].name)}") | ||
i.fa.fa-arrow-circle-left | ||
| Previous | ||
else | ||
a.btn.btn-default(href="#") | ||
i.fa.fa-arrow-circle-left | ||
| Previous | ||
.clearfix | ||
hr | ||
|
||
mixin Icon(method) | ||
case method | ||
when 'GET' | ||
span.badge.alert-info | ||
i.fa.fa-arrow-down | ||
when 'POST' | ||
span.badge.alert-success | ||
i.fa.fa-plus | ||
when 'PUT' | ||
span.badge.alert-warning | ||
i.fa.fa-pencil | ||
when 'PATCH' | ||
span.badge.alert-warning | ||
i.fa.fa-pencil | ||
when 'DELETE' | ||
span.badge.alert-danger | ||
i.fa.fa-times | ||
default | ||
span.badge | ||
i.fa.fa-dot-circle-o | ||
|
||
mixin Content(getButtonClass, multipage) | ||
div(data-bind=multipage ? "visible: page() == 'home'" : undefined) | ||
header | ||
.page-header | ||
h1#top= api.name || 'API Documentation' | ||
|
||
.description!= markdown(api.description) | ||
|
||
if multipage | ||
a.btn.btn-default.pull-right(href="#page:#{slug(api.resourceGroups[0].name)}") | ||
| Next | ||
i.fa.fa-arrow-circle-right | ||
.clearfix | ||
hr | ||
|
||
each resourceGroup, index in api.resourceGroups | ||
div(data-bind=multipage ? "visible: page() == '#{slug(resourceGroup.name)}', style: {marginTop: page() == '#{slug(resourceGroup.name)}' ? '38px' : ''}" : undefined) | ||
+ResourceGroup(resourceGroup, getButtonClass, multipage) | ||
|
||
if multipage | ||
+Paginate(api.resourceGroups, index) | ||
|
||
mixin Multipage() | ||
//- Multi-page support through Knockout.js | ||
script(src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js") | ||
script | ||
:coffee | ||
class App | ||
constructor: -> | ||
@page = ko.observable 'home' | ||
window.app = new App() | ||
window.onhashchange = -> | ||
vars = {} | ||
for v in location.hash.substr(1).split(',') | ||
parts = v.split ':' | ||
vars[parts[0]] = parts[1] | ||
# Set the page | ||
window.app.page vars.page or 'home' | ||
# Scroll to a header if Set | ||
if vars.header | ||
$("##{vars.header}")[0].scrollIntoView true | ||
else | ||
window.scrollTo 0, 0 | ||
ko.applyBindings window.app | ||
# Load the correct page | ||
window.onhashchange() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
doctype | ||
|
||
include openproject-includes.jade | ||
|
||
html | ||
head | ||
meta(charset="utf-8") | ||
title= api.name || 'API Documentation' | ||
block bootstrap-theme | ||
link(rel="stylesheet", href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css") | ||
link(rel="stylesheet", href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css") | ||
block styles | ||
link(rel="stylesheet", href="//fonts.googleapis.com/css?family=Roboto:400,700|Inconsolata|Raleway:200") | ||
style. | ||
/* Highlight.js Theme Tomorrow */ | ||
.hljs-comment,.hljs-title{color:#8e908c}.hljs-variable,.hljs-attribute,.hljs-tag,.hljs-regexp,.ruby .hljs-constant,.xml .hljs-tag .hljs-title,.xml .hljs-pi,.xml .hljs-doctype,.html .hljs-doctype,.css .hljs-id,.css .hljs-class,.css .hljs-pseudo{color:#c82829}.hljs-number,.hljs-preprocessor,.hljs-pragma,.hljs-built_in,.hljs-literal,.hljs-params,.hljs-constant{color:#f5871f}.ruby .hljs-class .hljs-title,.css .hljs-rules .hljs-attribute{color:#eab700}.hljs-string,.hljs-value,.hljs-inheritance,.hljs-header,.ruby .hljs-symbol,.xml .hljs-cdata{color:#718c00}.css .hljs-hexcolor{color:#3e999f}.hljs-function,.python .hljs-decorator,.python .hljs-title,.ruby .hljs-function .hljs-title,.ruby .hljs-title .hljs-keyword,.perl .hljs-sub,.javascript .hljs-title,.coffeescript .hljs-title{color:#4271ae}.hljs-keyword,.javascript .hljs-function{color:#8959a8}.hljs{display:block;background:white;color:#4d4d4c;padding:.5em}.coffeescript .javascript,.javascript .xml,.tex .hljs-formula,.xml .javascript,.xml .vbscript,.xml .css,.xml .hljs-cdata{opacity:.5} | ||
|
||
|
||
body | ||
div(class=fullWidth ? 'container-fluid' : 'container') | ||
.row | ||
block nav | ||
+Nav(false) | ||
|
||
.col-md-8 | ||
div(id="op-api-content") | ||
block content | ||
+Content('primary', false) |