This repository has been archived by the owner on Feb 1, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixes bug 858244 - documentation for public api, r=rhelmer
- Loading branch information
Showing
7 changed files
with
460 additions
and
7 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,52 @@ | ||
import urllib | ||
import warnings | ||
import datetime | ||
from jingo import register | ||
import jinja2 | ||
|
||
|
||
@register.function | ||
def describe_friendly_type(type_): | ||
if type_ is basestring: | ||
return "String" | ||
if type_ is int: | ||
return "Integer" | ||
if type_ is list: | ||
return "List of strings" | ||
if type_ is datetime.date: | ||
return "Date" | ||
if type_ is datetime.datetime: | ||
return "Date and time" | ||
warnings.warn("Don't know how to describe type %r" % type_) | ||
return type_ | ||
|
||
|
||
@register.function | ||
def make_test_input(parameter, defaults): | ||
template = u'<input type="%(type)s" name="%(name)s"' | ||
data = { | ||
'name': parameter['name'], | ||
} | ||
classes = [] | ||
if parameter['required']: | ||
classes.append('required') | ||
|
||
if parameter['type'] is datetime.date: | ||
data['type'] = 'date' | ||
else: | ||
data['type'] = 'text' | ||
if parameter['type'] is not basestring: | ||
classes.append('validate-%s' % parameter['type'].__name__) | ||
if defaults.get(parameter['name']): | ||
data['value'] = urllib.quote(unicode(defaults.get(parameter['name']))) | ||
else: | ||
data['value'] = '' | ||
|
||
data['classes'] = ' '.join(classes) | ||
if data['classes']: | ||
template += ' class="%(classes)s"' | ||
if data['value']: | ||
template += ' value="%(value)s"' | ||
template += '>' | ||
html = template % data | ||
return jinja2.Markup(html) |
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,158 @@ | ||
(function ($, document) { | ||
'use strict'; | ||
|
||
$.fn.serializeExclusive = function() { | ||
var o = {}; | ||
var a = this.serializeArray(); | ||
$.each(a, function() { | ||
var value; | ||
if (o[this.name] !== undefined) { | ||
if (!o[this.name].push) { | ||
o[this.name] = [o[this.name]]; | ||
} | ||
if (this.value) | ||
o[this.name].push(this.value || ''); | ||
} else { | ||
if (this.value) | ||
o[this.name] = this.value || ''; | ||
} | ||
}); | ||
return o; | ||
}; | ||
|
||
function one_more(element) { | ||
var container = $(element).parents('td'); | ||
var previous = $('input', container).eq(-1); | ||
var clone = previous.clone(); | ||
clone.val(''); | ||
clone.insertAfter(previous); | ||
if ($('input', container).length > 1) { | ||
$('a.collapse-list', container).css('display', 'inline'); | ||
} | ||
} | ||
|
||
function one_less(element) { | ||
var container = $(element).parents('td'); | ||
var last = $('input', container).eq(-1); | ||
last.remove(); | ||
if ($('input', container).length < 2) { | ||
$('a.collapse-list', container).css('display', 'none'); | ||
} | ||
} | ||
|
||
function validate_form(form) { | ||
|
||
function is_int(x) { | ||
var y = parseInt(x, 10); | ||
if (isNaN(y)) return false; | ||
return x==y && x.toString() == y.toString(); | ||
} | ||
var all_valid = true; | ||
$('input', form).each(function() { | ||
var valid = true; | ||
var element = $(this); | ||
var value = element.val(); | ||
if (element.hasClass('required') && !value) { | ||
valid = false; | ||
} else if (element.hasClass('validate-int') && !is_int(value)) { | ||
valid = false; | ||
} else { | ||
// we can do more validation but let's not go too crazy yet | ||
} | ||
if (!valid) { | ||
all_valid = false; | ||
element.addClass('error'); | ||
} else { | ||
element.removeClass('error'); | ||
} | ||
}); | ||
return all_valid; | ||
} | ||
|
||
function submit_form(form) { | ||
var url = $('p.url code', form).text(); | ||
// unlike regular, form.serialize() by doing it this way we get a | ||
// query string that only contains actual values | ||
// The second parameter (`true`) is so that things like | ||
// `{products: ["Firefox", "Thunderbird"]}` | ||
// becomes: `products=Firefox&products=Thunderbird` | ||
var qs = $.param(form.serializeExclusive(), true); | ||
if (qs) { | ||
url += '?' + qs; | ||
} | ||
var ajax_url = url; | ||
if (ajax_url.search(/\?/) == -1) ajax_url += '?'; | ||
else ajax_url += '&'; | ||
ajax_url += 'pretty=print'; | ||
|
||
$.ajax({ | ||
url: ajax_url, | ||
method: 'GET', | ||
dataType: 'text', | ||
success: function(response, textStatus, jqXHR) { | ||
var container = $('.test-drive', form); | ||
$('.used-url code', container).text(url); | ||
$('.used-url a', container).attr('href', url); | ||
$('pre', container).text(response); | ||
$('.status code', container).hide(); | ||
$('.status-error', container).hide(); | ||
container.show(); | ||
$('button.close', form).show(); | ||
}, | ||
error: function(jqXHR, textStatus, errorThrown) { | ||
var container = $('.test-drive', form); | ||
$('pre', container).text(jqXHR.responseText); | ||
$('.status code', container).text(jqXHR.status).show(); | ||
$('.status-error', container).show(); | ||
|
||
container.show(); | ||
$('button.close', form).show(); | ||
} | ||
}); | ||
} | ||
|
||
$(document).ready(function () { | ||
$('input.validate-list').each(function() { | ||
$('<a href="#">-</a>') | ||
.hide() | ||
.addClass('collapse-list') | ||
.attr('title', 'Click to remove the last added input field') | ||
.click(function(e) { | ||
e.preventDefault(); | ||
one_less(this); | ||
}) | ||
.insertAfter($(this)); | ||
$('<a href="#">+</a>') | ||
.addClass('expand-list') | ||
.attr('title', 'Click to create another input field') | ||
.click(function(e) { | ||
e.preventDefault(); | ||
one_more(this); | ||
}) | ||
.insertAfter($(this)); | ||
}); | ||
|
||
$('form.testdrive').submit(function(event) { | ||
var $form = $(this); | ||
event.preventDefault(); | ||
if (validate_form($form)) { | ||
submit_form($form); | ||
} else { | ||
} | ||
}); | ||
|
||
// Can we PLEASE upgrade to modern jquery soon? | ||
$('input.error').live('change', function() { | ||
$(this).removeClass('error'); | ||
}); | ||
|
||
$('button.close').click(function(event) { | ||
event.preventDefault(); | ||
$('.test-drive', $(this).parents('form')).hide(); | ||
$(this).hide(); | ||
}); | ||
|
||
}); | ||
|
||
|
||
}($, document)); |
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,131 @@ | ||
{% extends "crashstats_base.html" %} | ||
|
||
{% block page_title %}API Documentation{% endblock %} | ||
|
||
{% block product_nav_filter %} {% endblock %} | ||
|
||
{% block site_css %} | ||
{{ super() }} | ||
<style type="text/css"> | ||
p.url { margin: 15px; font-size: 1.6em; } | ||
p.url code { padding-left: 25px; } | ||
th.fixed, td.fixed { width: 15%; } | ||
div.run-test { text-align: right; margin: 10px; } | ||
a.collapse-list, a.expand-list { text-decoration: none; font-weight: bold; padding: 3px; } | ||
input.error { border: 2px solid red; } | ||
div.test-drive { display: none; } | ||
button.close { display: none; } | ||
.status-error { display: none; color: red; font-weight: bold; } | ||
pre.docstring { background-color: rgb(225,225,225); padding: 6px; } | ||
</style> | ||
{% endblock %} | ||
|
||
|
||
{% block site_js %} | ||
{{ super() }} | ||
{% compress js %} | ||
<script type="text/javascript" src="{{ static('api/js/testdrive.js') }}"></script> | ||
{% endcompress %} | ||
{% endblock %} | ||
|
||
|
||
{% block content %} | ||
<div id="mainbody"> | ||
<div class="page-heading"> | ||
<h2>API Documentation</h2> | ||
</div> | ||
|
||
<div class="panel"> | ||
<div class="body notitle"> | ||
<p> | ||
These API endpoints are publically available. The parameters to some endpoints | ||
are non-trivial as some might require deeper understanding of the Soccoro backend. | ||
</p> | ||
<p> | ||
<b>Note:</b> All Personal Identifiable Information | ||
will be <b>removed or scrubbed</b> from all responses. | ||
</p> | ||
</div> | ||
</div> | ||
|
||
{% for endpoint in endpoints %} | ||
<div class="panel"> | ||
<div class="title"> | ||
<h2>{{ endpoint.name }}</h2> | ||
</div> | ||
<div class="body"> | ||
<form class="testdrive"> | ||
<p class="url"> | ||
<strong>{{ ' | '.join(endpoint.methods) }}</strong> | ||
<code>{{ base_url }}{{ endpoint.url }}</code> | ||
</p> | ||
|
||
{% if endpoint.docstring %} | ||
<p class="docstring">Documentation notes</p> | ||
<pre class="docstring">{{ endpoint.docstring }}</pre> | ||
{% endif %} | ||
|
||
{% if endpoint.parameters %} | ||
<table class="data-table vertical"> | ||
<thead> | ||
<tr> | ||
<th class="fixed">Parameter key</th> | ||
<th class="fixed">Required?</th> | ||
<th class="fixed">Type</th> | ||
<th class="fixed">Default</th> | ||
<th>Test drive</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for parameter in endpoint.parameters %} | ||
<tr> | ||
<td class="fixed"><strong>{{ parameter.name }}</strong></td> | ||
<td class="fixed"> | ||
{% if parameter.required %}Required | ||
{% else %}Optional | ||
{% endif %} | ||
</td> | ||
<td class="fixed"> | ||
{{ describe_friendly_type(parameter['type']) }} | ||
</td> | ||
<td class="fixed"> | ||
<code>{{ endpoint['defaults'].get(parameter['name'], '') }}</code> | ||
</td> | ||
<td> | ||
{{ make_test_input(parameter, endpoint['defaults']) }} | ||
</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
{% else %} | ||
<p><em>Takes no parameters</em></p> | ||
{% endif %} | ||
|
||
<!-- for starting a test drive --> | ||
<div class="run-test"> | ||
<button type="submit">Run Test Drive!</button> | ||
<button type="button" class="close">× Close</button> | ||
</div> | ||
|
||
<div class="test-drive"> | ||
<p class="status-error"> | ||
An error happened on the server when trying this URL. | ||
</p> | ||
<p class="used-url"> | ||
<strong>Using</strong><br> | ||
<a href=""><code></code></a> | ||
</p> | ||
<p class="status"> | ||
Status <code></code> | ||
</p> | ||
<p><strong>Output:</strong></p> | ||
<pre></pre> | ||
|
||
</div> | ||
</form> | ||
</div> | ||
</div> | ||
{% endfor %} | ||
</div> | ||
{% endblock %} |
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
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
Oops, something went wrong.