API docs tweaks

This commit is contained in:
Tom Christie 2017-03-01 14:50:01 +00:00
parent c412de920e
commit 5c99e7e01e
18 changed files with 2111 additions and 53 deletions

View File

@ -0,0 +1,63 @@
<style>
.promo li a {
float: left;
width: 130px;
height: 20px;
text-align: center;
margin: 10px 30px;
padding: 150px 0 0 0;
background-position: 0 50%;
background-size: 130px auto;
background-repeat: no-repeat;
font-size: 120%;
color: black;
}
.promo li {
list-style: none;
}
</style>
# Django REST framework 3.6
---
## Funding
The 3.6 release would not have been possible without our [collaborative funding model][funding].
If you use REST framework commercially and would like to see this work continue,
we strongly encourage you to invest in its continued development by
**[signing up for a paid&nbsp;plan][funding]**.
<ul class="premium-promo promo">
<li><a href="http://jobs.rover.com/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rover_130x130.png)">Rover.com</a></li>
<li><a href="https://getsentry.com/welcome/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/sentry130.png)">Sentry</a></li>
<li><a href="https://getstream.io/try-the-api/?utm_source=drf&utm_medium=banner&utm_campaign=drf" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/stream-130.png)">Stream</a></li>
<li><a href="https://hello.machinalis.co.uk/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/Machinalis130.png)">Machinalis</a></li>
<li><a href="https://rollbar.com" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/rollbar.png)">Rollbar</a></li>
<li><a href="https://micropyramid.com/django-rest-framework-development-services/" style="background-image: url(https://fund-rest-framework.s3.amazonaws.com/mp-text-logo.png)">MicroPyramid</a></li>
</ul>
<div style="clear: both; padding-bottom: 20px;"></div>
*Many thanks to all our [sponsors][sponsors], and in particular to our premium backers, [Rover](http://jobs.rover.com/), [Sentry](https://getsentry.com/welcome/), [Stream](https://getstream.io/?utm_source=drf&utm_medium=banner&utm_campaign=drf), [Machinalis](https://hello.machinalis.co.uk/), [Rollbar](https://rollbar.com), and [MicroPyramid](https://micropyramid.com/django-rest-framework-development-services/).*
---
## API documentation
...
## JavaScript Client
...
---
## Deprecations
...
---
[sponsors]: https://fund.django-rest-framework.org/topics/funding/#our-sponsors
[funding]: funding.md

View File

@ -6,9 +6,13 @@
There are a variety of approaches to API documentation. This document introduces a few of the various tools and options you might choose from. The approaches should not be considered exclusive - you may want to provide more than one documentation style for you API, such as a self describing API that also includes static documentation of the various API endpoints.
## Endpoint documentation
##
The most common way to document Web APIs today is to produce documentation that lists the API endpoints verbatim, and describes the allowable operations on each. There are various tools that allow you to do this in an automated or semi-automated way.
... TODO ...
## Third party packages
There are a number of mature third-party packages for providing API documentation.
---

View File

@ -3,7 +3,7 @@
Looking for a new Django REST Framework related role? On this site we provide a list of job resources that may be helpful. It's also worth checking out if any of [our sponsors are hiring][drf-funding].
## Places to Look for Django REST Framework Jobs
## Places to look for Django REST Framework Jobs
* [https://www.djangoproject.com/community/jobs/][djangoproject-website]
* [https://www.python.org/jobs/][python-org-jobs]

View File

@ -62,14 +62,15 @@ pages:
- 'Tutorials and Resources': 'topics/tutorials-and-resources.md'
- 'Contributing to REST framework': 'topics/contributing.md'
- 'Project management': 'topics/project-management.md'
- 'Jobs': 'topics/jobs.md'
- '3.0 Announcement': 'topics/3.0-announcement.md'
- '3.1 Announcement': 'topics/3.1-announcement.md'
- '3.2 Announcement': 'topics/3.2-announcement.md'
- '3.3 Announcement': 'topics/3.3-announcement.md'
- '3.4 Announcement': 'topics/3.4-announcement.md'
- '3.5 Announcement': 'topics/3.5-announcement.md'
- '3.6 Announcement': 'topics/3.6-announcement.md'
- 'Kickstarter Announcement': 'topics/kickstarter-announcement.md'
- 'Mozilla Grant': 'topics/mozilla-grant.md'
- 'Funding': 'topics/funding.md'
- 'Release Notes': 'topics/release-notes.md'
- 'Jobs': 'topics/jobs.md'

View File

@ -1,13 +1,36 @@
from rest_framework.renderers import CoreJSONRenderer, DocumentationRenderer
from rest_framework.renderers import CoreJSONRenderer, DocumentationRenderer, SchemaJSRenderer
from rest_framework.schemas import get_schema_view
from django.conf.urls import url, include
def get_docs_view(title=None, url=None, renderer_classes=None):
def get_docs_view(title=None, url=None, renderer_classes=None, public=True):
if renderer_classes is None:
renderer_classes = [DocumentationRenderer, CoreJSONRenderer]
return get_schema_view(
title=title,
url=url,
renderer_classes=renderer_classes
renderer_classes=renderer_classes,
public=public
)
def get_schemajs_view(title=None, url=None, public=True):
renderer_classes = [SchemaJSRenderer]
return get_schema_view(
title=title,
url=url,
renderer_classes=renderer_classes,
public=public
)
def include_docs_urls(title=None, schema_url=None, public=True):
docs_view = get_docs_view(title, schema_url, public=public)
schema_js_view = get_schemajs_view(title, schema_url, public=public)
urls = [
url(r'^$', docs_view, name='docs-index'),
url(r'^schema.js$', schema_js_view, name='schema-js')
]
return include(urls, namespace='api-docs')

View File

@ -8,6 +8,7 @@ REST framework also provides an HTML renderer that renders the browsable API.
"""
from __future__ import unicode_literals
import base64
import json
from collections import OrderedDict
@ -19,6 +20,7 @@ from django.http.multipartparser import parse_header
from django.template import Template, loader
from django.test.client import encode_multipart
from django.utils import six
from django.utils.html import mark_safe
from rest_framework import VERSION, exceptions, serializers, status
from rest_framework.compat import (
@ -803,17 +805,14 @@ class DocumentationRenderer(BaseRenderer):
def get_context(self, data, request):
from pygments.formatters import HtmlFormatter
from django.utils.html import mark_safe
formatter = HtmlFormatter(style=self.code_style)
code_style = formatter.get_style_defs('.highlight')
langs = ['shell', 'javascript', 'python']
codec = coreapi.codecs.CoreJSONCodec()
schema = mark_safe(json.dumps(codec.encode(data)))
return {
'document': data,
'langs': langs,
'code_style': code_style,
'schema': schema,
'request': request
}
@ -823,6 +822,22 @@ class DocumentationRenderer(BaseRenderer):
return template_render(template, context, request=renderer_context['request'])
class SchemaJSRenderer(BaseRenderer):
media_type = 'script/javascript'
format = 'javascript'
charset = 'utf-8'
template = 'rest_framework/schema.js'
def render(self, data, accepted_media_type=None, renderer_context=None):
codec = coreapi.codecs.CoreJSONCodec()
schema = base64.b64encode(codec.encode(data))
template = loader.get_template(self.template)
context = {'schema': mark_safe(schema)}
request = renderer_context['request']
return template_render(template, context, request=request)
class MultiPartRenderer(BaseRenderer):
media_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg'
format = 'multipart'

View File

@ -291,7 +291,7 @@ class SchemaGenerator(object):
self.url = url
self.endpoints = None
def get_schema(self, request=None):
def get_schema(self, request=None, public=False):
"""
Generate a `coreapi.Document` representing the API schema.
"""
@ -299,7 +299,7 @@ class SchemaGenerator(object):
inspector = self.endpoint_inspector_cls(self.patterns, self.urlconf)
self.endpoints = inspector.get_api_endpoints()
links = self.get_links(request)
links = self.get_links(None if public else request)
if not links:
return None
@ -661,7 +661,7 @@ class SchemaGenerator(object):
return named_path_components + [action]
def get_schema_view(title=None, url=None, urlconf=None, renderer_classes=None):
def get_schema_view(title=None, url=None, urlconf=None, renderer_classes=None, public=False):
"""
Return a schema view.
"""
@ -680,7 +680,7 @@ def get_schema_view(title=None, url=None, urlconf=None, renderer_classes=None):
renderer_classes = rclasses
def get(self, request, *args, **kwargs):
schema = generator.get_schema(request)
schema = generator.get_schema(request, public)
if schema is None:
raise exceptions.PermissionDenied()
return Response(schema)

View File

@ -1,3 +1,23 @@
h1 {
font-size: 45px;
}
.intro-code {
margin-top: 20px;
}
pre.highlight code * {
white-space: nowrap; // this sets all children inside to nowrap
}
pre.highlight {
overflow-x: auto; // this sets the scrolling in x
}
pre.highlight code {
white-space: pre; // forces <code> to respect <pre> formatting
}
.main-container {
padding-left: 30px;
padding-right: 30px;
@ -307,3 +327,17 @@ body {
margin: 0;
max-height: 550px;
}
.highlight {
background-color: #f7f7f9
}
.checkbox label.control-label {
font-weight: bold
}
@media (min-width: 768px) {
.navbar-nav.navbar-right:last-child {
margin-right: 0 !important;
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,16 @@
{% load rest_framework %}
<div class="row intro">
<div class="col-md-6 intro-title">
<h1>{{ document.title }}</h1>
</div>
<div class="col-md-6 intro-code">
{% if 'shell' in langs %}{% include "rest_framework/docs/langs/shell-intro.html" %}{% endif %}
{% if 'python' in langs %}{% include "rest_framework/docs/langs/python-intro.html" %}{% endif %}
{% if 'javascript' in langs %}{% include "rest_framework/docs/langs/javascript-intro.html" %}{% endif %}
</div>
</div>
{% for section_key, section in document.data.items %}
<h2 id="{{ section_key }}" class="coredocs-section-title">{{ section_key }} <a href="#{{ section_key }}"><i class="fa fa-link" aria-hidden="true"></i>
</a></h2>

View File

@ -17,16 +17,8 @@
<link href="{% static 'rest_framework/docs/img/favicon.ico' %}" rel="shortcut icon">
<style>{{ code_style }}</style>
<style>
.highlight {background-color: #f7f7f9}
.checkbox label.control-label {font-weight: bold}
@media (min-width: 768px) {
.navbar-nav.navbar-right:last-child {
margin-right: 0 !important;
}
}
</style>
<script src="https://unpkg.com/coreapi@0.0.20/dist/coreapi.js"></script>
<script src="{% static 'rest_framework/js/coreapi-0.0.20.js' %}"></script>
<script src="{% url 'api-docs:schema-js' %}"></script>
</head>
@ -76,9 +68,7 @@
let responseDisplay = 'data';
const coreapi = window.coreapi
const codec = new coreapi.codecs.CoreJSONCodec()
const schema = {{ schema }}
const doc = codec.decode(schema)
const schema = window.schema
{% if user.is_authenticated %}
window.auth = {
@ -147,71 +137,70 @@
})
function requestCallback(request) {
// Fill in the "GET /foo/" display.
let parser = document.createElement('a');
parser.href = request.url;
const method = request.options.method
const path = parser.pathname + parser.hash + parser.search
// Filling the main response meta
form.closest(".modal-content").find(".toggle-view").removeClass("hide")
form.find(".request-method").text(method)
form.find(".request-url").text(path)
// Filling the raw panel
// form.find(".response-raw-request").text(method + ' ' + path + '\n\n')
}
function responseCallback(response, responseText) {
// Display the 'Data'/'Raw' control.
form.closest(".modal-content").find(".toggle-view").removeClass("hide")
// Fill in the "200 OK" display.
form.find(".response-status-code").removeClass("label-success").removeClass("label-danger")
if (response.ok) {
form.find(".response-status-code").addClass("label-success")
} else {
form.find(".response-status-code").addClass("label-danger")
}
form.find(".response-status-code").text(response.status)
form.find(".meta").removeClass("hide")
// Filling the raw panel
// Fill in the Raw HTTP response display.
var panelText = 'HTTP/1.1 ' + response.status + ' ' + response.statusText + '\n';
response.headers.forEach((header, key) => {
panelText += normalizeHTTPHeader(key) + ': ' + header + '\n'
})
if (responseText) {
panelText += '\n' + responseText
}
form.find(".response-raw-response").text(panelText)
}
// Instantiate a client to make the outgoing request.
let options = {
requestCallback: requestCallback,
responseCallback: responseCallback,
}
// Setup authentication options.
if (window.auth && window.auth.type === 'token') {
// Header authentication
options.headers = {
'Authorization': window.auth.value
}
} else if (window.auth && window.auth.type === 'basic') {
// Basic authentication
const token = window.auth.username + ':' + window.auth.password
const hash = window.btoa(token)
options.headers = {
'Authorization': 'Basic ' + hash
}
} else if (window.auth && window.auth.type === 'session') {
// Session authentication
options.csrf = {
'X-CSRFToken': getCookie('csrftoken')
}
}
const client = new coreapi.Client(options)
client.action(doc, key, params).then(function (data) {
client.action(schema, key, params).then(function (data) {
var response = JSON.stringify(data, null, 2);
form.find(".request-awaiting").addClass("hide")
form.find(".response-raw").addClass("hide")
@ -240,6 +229,7 @@
})
});
// 'Data'/'Raw' control
$('.toggle-view button').click(function() {
responseDisplay = $(this).data("display-toggle");
$(this).removeClass("btn-default").addClass('btn-info').siblings().removeClass('btn-info');

View File

@ -0,0 +1,5 @@
{% load rest_framework %}
{% load staticfiles %}
<pre class="highlight javascript hide" data-language="javascript"><code>{% code html %}<!-- Load the JavaScript client library -->
<script src="{% static 'rest_framework/js/coreapi-0.0.20.js' %}"></script>
<script src="{% url 'api-docs:schema-js' %}"></script>{% endcode %}</code></pre>

View File

@ -1,18 +1,15 @@
{% load rest_framework %}
<pre class="highlight javascript hide" data-language="javascript"><code>{% code javascript %}var coreapi = window.coreapi
<pre class="highlight javascript hide" data-language="javascript"><code>{% code javascript %}var coreapi = window.coreapi // Loaded by `coreapi.js`
var schema = window.schema // Loaded by `schema.js`
// Initialize a client & load the schema document
// Initialize a client
var client = new coreapi.Client()
var document = null
client.get("{{ document.url }}").then(function(result) {
document = result
})
// Interact with the API endpoint
var action = [{% if section_key %}"{{ section_key }}", {% endif %}"{{ link_key }}"]
{% if link.fields %}var params = {
{% for field in link.fields %} {{ field.name }}: ...{% if not loop.last %},{% endif %}
{% endfor %}}
{% endif %}client.action(document, action, params=params).then(function(result) {
{% endif %}client.action(schema, action{% if link.fields %}, params{% endif %}).then(function(result) {
// Return value is in 'result'
}){% endcode %}</code></pre>

View File

@ -0,0 +1,3 @@
{% load rest_framework %}
<pre class="highlight python hide" data-language="python"><code>{% code bash %}# Install the Python client library
$ pip install coreapi{% endcode %}</code></pre>

View File

@ -3,11 +3,11 @@
# Initialize a client & load the schema document
client = coreapi.Client()
document = client.get("{{ document.url }}"{% if schema_format %}, format="{{ schema_format }}"{% endif %})
schema = client.get("{{ document.url }}"{% if schema_format %}, format="{{ schema_format }}"{% endif %})
# Interact with the API endpoint
action = [{% if section_key %}"{{ section_key }}", {% endif %}"{{ link_key }}"]
{% if link.fields %}params = {
{% for field in link.fields %} "{{ field.name }}": ...{% if not loop.last %},{% endif %}
{% endfor %}}
{% endif %}result = client.action(document, action{% if link.fields %}, params=params{% endif %}){% endcode %}</code></pre>
{% endif %}result = client.action(schema, action{% if link.fields %}, params=params{% endif %}){% endcode %}</code></pre>

View File

@ -0,0 +1,3 @@
{% load rest_framework %}
<pre class="highlight shell" data-language="shell"><code>{% code bash %}# Install the command line client
$ pip install coreapi-cli{% endcode %}</code></pre>

View File

@ -1,5 +1,5 @@
<div class="sidebar">
<h3 class="brand"><a href=".">{{ document.title }}</a></h3>
<h3 class="brand"><a href="#">{{ document.title }}</a></h3>
<i class="fa fa-bars fa-2x toggle-btn" data-toggle="collapse" data-target="#menu-content"></i>
<div class="menu-list">

View File

@ -0,0 +1,3 @@
let codec = new window.coreapi.codecs.CoreJSONCodec()
let coreJSON = window.atob('{{ schema }}')
window.schema = codec.decode(coreJSON)