Page MenuHomeSoftware Heritage

apiurls.py
No OneTemporary

apiurls.py

# Copyright (C) 2017-2018 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
import re
from rest_framework.decorators import api_view
from swh.web.common.urlsindex import UrlsIndex
from swh.web.common.throttling import throttle_scope
class APIUrls(UrlsIndex):
"""
Class to manage API documentation URLs.
- Indexes all routes documented using apidoc's decorators.
- Tracks endpoint/request processing method relationships for use in
generating related urls in API documentation
"""
apidoc_routes = {}
method_endpoints = {}
scope = 'api'
@classmethod
def get_app_endpoints(cls):
return cls.apidoc_routes
@classmethod
def get_method_endpoints(cls, f):
if f.__name__ not in cls.method_endpoints:
cls.method_endpoints[f.__name__] = cls.group_routes_by_method(f)
return cls.method_endpoints[f.__name__]
@classmethod
def group_routes_by_method(cls, f):
"""
Group URL endpoints according to their processing method.
Returns:
A dict where keys are the processing method names, and values are
the routes that are bound to the key method.
"""
rules = []
for urlp in cls.get_url_patterns():
endpoint = urlp.callback.__name__
if endpoint != f.__name__:
continue
method_names = urlp.callback.http_method_names
url_rule = urlp.regex.pattern.replace('^', '/').replace('$', '')
url_rule_params = re.findall('\([^)]+\)', url_rule)
for param in url_rule_params:
param_name = re.findall('<(.*)>', param)
param_name = param_name[0] if len(param_name) > 0 else None
if param_name and hasattr(f, 'doc_data') and f.doc_data['args']: # noqa
param_index = \
next(i for (i, d) in enumerate(f.doc_data['args'])
if d['name'] == param_name)
if param_index is not None:
url_rule = url_rule.replace(
param, '<' +
f.doc_data['args'][param_index]['name'] +
': ' + f.doc_data['args'][param_index]['type'] +
'>').replace('.*', '')
rule_dict = {'rule': '/api' + url_rule,
'name': urlp.name,
'methods': {method.upper() for method in method_names}
}
rules.append(rule_dict)
return rules
@classmethod
def add_route(cls, route, docstring, **kwargs):
"""
Add a route to the self-documenting API reference
"""
route_view_name = route[1:-1].replace('/', '-')
if route not in cls.apidoc_routes:
d = {'docstring': docstring,
'route_view_name': route_view_name}
for k, v in kwargs.items():
d[k] = v
cls.apidoc_routes[route] = d
class api_route(object): # noqa: N801
"""
Decorator to ease the registration of an API endpoint
using the Django REST Framework.
Args:
url_pattern: the url pattern used by DRF to identify the API route
view_name: the name of the API view associated to the route used to
reverse the url
methods: array of HTTP methods supported by the API route
"""
def __init__(self, url_pattern=None, view_name=None,
methods=['GET', 'HEAD', 'OPTIONS'], api_version='1'):
super().__init__()
self.url_pattern = '^' + api_version + url_pattern + '$'
self.view_name = view_name
self.methods = methods
def __call__(self, f):
# create a DRF view from the wrapped function
@api_view(self.methods)
@throttle_scope('swh_api')
def api_view_f(*args, **kwargs):
return f(*args, **kwargs)
# small hacks for correctly generating API endpoints index doc
api_view_f.__name__ = f.__name__
api_view_f.http_method_names = self.methods
# register the route and its view in the endpoints index
APIUrls.add_url_pattern(self.url_pattern, api_view_f,
self.view_name)
return f

File Metadata

Mime Type
text/x-python
Expires
Fri, Jul 4, 1:15 PM (1 w, 7 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3354412

Event Timeline