SciPost Code Repository

Skip to content
Snippets Groups Projects
Commit 2f787a94 authored by George Katsikas's avatar George Katsikas :goat:
Browse files

feat(api): :sparkles: add root API page with endpoint list

parent 0e577078
No related branches found
No related tags found
No related merge requests found
{% extends "scipost/base.html" %}
{% block content %}
<h1>SciPost API</h1>
<p>SciPost provides a public JSON API via the Django REST framework.</p>
<h2>Endpoints</h2>
<p>Here you can find a list of all the available public API endpoints (DRF ViewSets).</p>
{% for url in resolver.url_patterns %}{{ url.callback }}{% endfor %}
<ul>
{% for endpoint in endpoints %}
<li>
<a href="{{ endpoint.url }}">{{ endpoint.name }}</a>
</li>
{% endfor %}
</ul>
{% endblock %}
......@@ -3,6 +3,7 @@ __license__ = "AGPL v3"
from django.urls import include, path
from . import views
from rest_framework import routers
......@@ -89,10 +90,9 @@ router.register(r"news", NewsItemViewSet)
router.register(r"conflicts", ConflictOfInterestViewSet)
urlpatterns = router.urls
urlpatterns += [
urlpatterns = [
path("", views.APIView.as_view(), name="api"),
*router.urls,
path( # /api/omniauth/userinfo/, for SciPost as GitLab/OmniAuth authorization server
"omniauth/userinfo/", OmniAuthUserInfoView.as_view(), name="omniauth_userinfo"
),
......
from typing import Any
from django.core.handlers.asgi import HttpRequest
from django.http.response import HttpResponse as HttpResponse
from django.views.generic import TemplateView
from django.urls import get_resolver, URLPattern
class APIView(TemplateView):
template_name = "api/api.html"
def get_api_endpoints(self, request: HttpRequest) -> list[dict[str, str]] | None:
"""Fetch all routes under /api/ and their corresponding views"""
_, api_resolver = get_resolver().namespace_dict.get("api", (None, None))
if api_resolver is None:
return
def should_add_endpoint(pattern: URLPattern) -> bool:
url_name = pattern.name or ""
callback_name = pattern.callback.__name__
return (
"-list" in url_name
and "search" not in url_name
and "PublicAPIViewSet" in callback_name
)
def map_pattern_to_endpoint_dict(pattern: URLPattern) -> dict[str, str]:
url_name = pattern.name
callback_name = pattern.callback.__name__
return {
"name": callback_name.replace("PublicAPIViewSet", ""),
"url": api_resolver.reverse(url_name),
}
return sorted(
[
map_pattern_to_endpoint_dict(pattern)
for pattern in api_resolver.url_patterns
if should_add_endpoint(pattern)
],
key=lambda x: x["name"],
)
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
context["endpoints"] = self.get_api_endpoints(self.request)
_, api_resolver = get_resolver().namespace_dict.get("api", (None, None))
context["resolver"] = api_resolver
return context
......@@ -68,6 +68,7 @@
<div class="container">
{% block breadcrumbs %}
<ul class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'api:api' %}">API</a></li>
{% for breadcrumb_name, breadcrumb_url in breadcrumblist %}
{% if forloop.last %}
<li class="breadcrumb-item active"><a href="{{ breadcrumb_url }}">{{ breadcrumb_name }}</a></li>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment