mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-26 15:28:37 -05:00
Automatically generate API documentation.
Needs CSS love and some rewriting of the docstrings that are now exposed, but otherwise complete.
This commit is contained in:
@@ -204,6 +204,7 @@ def make_map(global_conf={}, app_conf={}):
|
||||
mc('/post/:action', controller='post',
|
||||
requirements=dict(action="options|over18|unlogged_options|optout|optin|login|reg"))
|
||||
|
||||
mc('/api', controller='apihelp', action='help')
|
||||
mc('/api/distinguish/:how', controller='api', action="distinguish")
|
||||
# wherever this is, google has to agree.
|
||||
mc('/api/gcheckout', controller='ipn', action='gcheckout')
|
||||
|
||||
@@ -59,7 +59,7 @@ try:
|
||||
except ImportError:
|
||||
from api import ApiController
|
||||
|
||||
from api import ApiminimalController
|
||||
from api import ApiminimalController, ApihelpController
|
||||
from apiv1 import APIv1Controller as Apiv1Controller
|
||||
from oauth2 import OAuth2FrontendController as Oauth2frontendController
|
||||
from oauth2 import OAuth2AccessController as Oauth2accessController
|
||||
|
||||
@@ -34,7 +34,7 @@ from r2.lib.utils import query_string, timefromnow, randstr
|
||||
from r2.lib.utils import timeago, tup, filter_links
|
||||
from r2.lib.pages import EnemyList, FriendList, ContributorList, ModList, \
|
||||
BannedList, BoringPage, FormPage, CssError, UploadedImage, ClickGadget, \
|
||||
UrlParser, WrappedUser
|
||||
UrlParser, WrappedUser, ApiHelp, BoringPage
|
||||
from r2.lib.pages import FlairList, FlairCsv, FlairTemplateEditor, \
|
||||
FlairSelector
|
||||
from r2.lib.utils.trial_utils import indict, end_trial, trial_info
|
||||
@@ -2494,3 +2494,52 @@ class ApiController(RedditController):
|
||||
|
||||
self.enable_admin_mode(c.user)
|
||||
form.redirect(dest)
|
||||
|
||||
class ApihelpController(RedditController):
|
||||
@staticmethod
|
||||
def docs_from_controller(controller, url_prefix='/api'):
|
||||
api_methods = defaultdict(dict)
|
||||
for name, func in controller.__dict__.iteritems():
|
||||
i = name.find('_')
|
||||
if i > 0:
|
||||
method = name[:i]
|
||||
action = name[i+1:]
|
||||
else:
|
||||
continue
|
||||
|
||||
if func.__doc__ and method in ('GET', 'POST'):
|
||||
docs = func.__doc__.strip()
|
||||
if hasattr(func, 'oauth2_perms'):
|
||||
scopes = func.oauth2_perms.get('allowed_scopes')
|
||||
if scopes:
|
||||
docs = '*OAuth2 scope(s): %s*\n\n%s' % (
|
||||
', '.join([
|
||||
('[%s](#oauth2_scope_%s)' % (scope, scope))
|
||||
for scope in scopes
|
||||
]),
|
||||
docs,
|
||||
)
|
||||
api_methods['/'.join((url_prefix, action))][method] = docs
|
||||
|
||||
return api_methods
|
||||
|
||||
def GET_help(self):
|
||||
from r2.controllers.apiv1 import APIv1Controller
|
||||
from r2.controllers.oauth2 import OAuth2FrontendController, OAuth2AccessController, scope_info
|
||||
|
||||
api_methods = defaultdict(dict)
|
||||
for controller, url_prefix in ((ApiController, '/api'),
|
||||
(ApiminimalController, '/api'),
|
||||
(OAuth2FrontendController, '/api/v1'),
|
||||
(OAuth2AccessController, '/api/v1'),
|
||||
(APIv1Controller, '/api/v1')):
|
||||
for url, methods in self.docs_from_controller(controller, url_prefix).iteritems():
|
||||
api_methods[url].update(methods)
|
||||
|
||||
return BoringPage(
|
||||
_('api documentation'),
|
||||
content=ApiHelp(
|
||||
api_methods=api_methods,
|
||||
oauth2_scopes=scope_info,
|
||||
)
|
||||
).render()
|
||||
|
||||
@@ -29,5 +29,6 @@ class APIv1Controller(OAuth2ResourceController):
|
||||
|
||||
@require_oauth2_scope("identity")
|
||||
def GET_me(self):
|
||||
"""Returns the identity of the user currently authenticated via OAuth."""
|
||||
resp = IdentityJsonTemplate().data(c.oauth_user)
|
||||
return self.api_wrapper(resp)
|
||||
|
||||
@@ -89,6 +89,8 @@ class OAuth2FrontendController(RedditController):
|
||||
scope = VOneOf("scope", scope_info.keys()),
|
||||
state = VRequired("state", errors.NO_TEXT))
|
||||
def GET_authorize(self, response_type, client, redirect_uri, scope, state):
|
||||
"""Endpoint for OAuth2 authorization."""
|
||||
|
||||
self._check_redirect_uri(client, redirect_uri)
|
||||
|
||||
resp = {}
|
||||
@@ -107,6 +109,8 @@ class OAuth2FrontendController(RedditController):
|
||||
state = VRequired("state", errors.NO_TEXT),
|
||||
authorize = VRequired("authorize", errors.OAUTH2_ACCESS_DENIED))
|
||||
def POST_authorize(self, authorize, client, redirect_uri, scope, state):
|
||||
"""Endpoint for OAuth2 authorization."""
|
||||
|
||||
self._check_redirect_uri(client, redirect_uri)
|
||||
|
||||
resp = {}
|
||||
|
||||
@@ -139,6 +139,9 @@ def validate(*simple_vals, **param_vals):
|
||||
return self.intermediate_redirect('/login')
|
||||
except VerifiedUserRequiredException:
|
||||
return self.intermediate_redirect('/verify')
|
||||
|
||||
newfn.__name__ = fn.__name__
|
||||
newfn.__doc__ = fn.__doc__
|
||||
return newfn
|
||||
return val
|
||||
|
||||
@@ -183,11 +186,14 @@ def api_validate(response_type=None):
|
||||
except VerifiedUserRequiredException:
|
||||
responder.send_failure(errors.VERIFIED_USER_REQUIRED)
|
||||
return self.api_wrapper(responder.make_response())
|
||||
|
||||
newfn.__name__ = fn.__name__
|
||||
newfn.__doc__ = fn.__doc__
|
||||
return newfn
|
||||
return val
|
||||
return _api_validate
|
||||
return wrap
|
||||
|
||||
|
||||
|
||||
@api_validate("html")
|
||||
def noresponse(self, self_method, responder, simple_vals, param_vals, *a, **kw):
|
||||
|
||||
@@ -3782,3 +3782,9 @@ class UserIPHistory(Templated):
|
||||
def __init__(self):
|
||||
self.ips = ips_by_account_id(c.user._id)
|
||||
super(UserIPHistory, self).__init__()
|
||||
|
||||
class ApiHelp(Templated):
|
||||
def __init__(self, api_methods, oauth2_scopes, *a, **kw):
|
||||
self.api_methods = api_methods
|
||||
self.oauth2_scopes = oauth2_scopes
|
||||
super(ApiHelp, self).__init__(*a, **kw)
|
||||
|
||||
78
r2/r2/templates/apihelp.html
Normal file
78
r2/r2/templates/apihelp.html
Normal file
@@ -0,0 +1,78 @@
|
||||
<%!
|
||||
from r2.lib.filters import safemarkdown
|
||||
%>
|
||||
|
||||
<div class="content apihelp">
|
||||
<div class="introduction">
|
||||
<p>This is the automatically-generated documentation for the Reddit API.</p>
|
||||
<p>It's gathered from the docstrings in the code.</p>
|
||||
</div>
|
||||
|
||||
<div class="contents">
|
||||
<h1>Contents</h1>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<strong>API methods</strong>
|
||||
<ul>
|
||||
%for uri in sorted(thing.api_methods.keys()):
|
||||
<li>
|
||||
<a href="#api_method_${uri.split('/')[-1]}">${uri}</a>
|
||||
<span class="gray">(${', '.join(sorted(thing.api_methods[uri].keys()))})</span>
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
<li>
|
||||
<strong>OAuth scopes</strong>
|
||||
<ul>
|
||||
%for scope in sorted(thing.oauth2_scopes.keys()):
|
||||
<li><a href="#oauth2_scope_${scope}">${scope}</a></li>
|
||||
%endfor
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="methods">
|
||||
<h1>API methods</h1>
|
||||
|
||||
%for uri in sorted(thing.api_methods.keys()):
|
||||
<div class="endpoint">
|
||||
<a name="api_method_${uri.split('/')[-1]}"></a>
|
||||
<h2>${uri}</h2>
|
||||
%for method in sorted(thing.api_methods[uri].keys()):
|
||||
<div class="method">
|
||||
<h3>${method}</h3>
|
||||
${unsafe(safemarkdown(thing.api_methods[uri][method]))}
|
||||
</div>
|
||||
%endfor
|
||||
</div>
|
||||
%endfor
|
||||
</div>
|
||||
|
||||
<div class="apihelp scopes">
|
||||
<h1>OAuth scopes</h1>
|
||||
|
||||
<table class="oauth2-scopes">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>id</th>
|
||||
<th>name</th>
|
||||
<th>description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
%for scope in sorted(thing.oauth2_scopes.keys()):
|
||||
<tr>
|
||||
<td class="oauth2-scope-id">
|
||||
<a name="oauth2_scope_${scope}"></a>
|
||||
${scope}
|
||||
</td>
|
||||
<td class="oauth2-scope-name">${thing.oauth2_scopes[scope]['name']}</td>
|
||||
<td class="oauth2-scope-description">${thing.oauth2_scopes[scope]['description']}</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user