diff --git a/swh/web/add_forge_now/api_views.py b/swh/web/add_forge_now/api_views.py --- a/swh/web/add_forge_now/api_views.py +++ b/swh/web/add_forge_now/api_views.py @@ -107,7 +107,7 @@ "api-1-add-forge-request-create", methods=["POST"], ) -@api_doc("/add-forge/request/create") +@api_doc("/add-forge/request/create", category="Request archival") @format_docstring() @transaction.atomic def api_add_forge_request_create(request: Union[HttpRequest, Request]) -> HttpResponse: @@ -190,7 +190,7 @@ "api-1-add-forge-request-update", methods=["POST"], ) -@api_doc("/add-forge/request/update", tags=["hidden"]) +@api_doc("/add-forge/request/update", category="Request archival", tags=["hidden"]) @format_docstring() @transaction.atomic def api_add_forge_request_update( @@ -281,7 +281,7 @@ "api-1-add-forge-request-list", methods=["GET"], ) -@api_doc("/add-forge/request/list") +@api_doc("/add-forge/request/list", category="Request archival") @format_docstring() def api_add_forge_request_list(request: Request): """ @@ -352,7 +352,7 @@ "api-1-add-forge-request-get", methods=["GET"], ) -@api_doc("/add-forge/request/get") +@api_doc("/add-forge/request/get", category="Request archival") @format_docstring() def api_add_forge_request_get(request: Request, id: int): """ diff --git a/swh/web/api/apidoc.py b/swh/web/api/apidoc.py --- a/swh/web/api/apidoc.py +++ b/swh/web/api/apidoc.py @@ -18,7 +18,7 @@ from rest_framework.decorators import api_view from swh.web.api.apiresponse import make_api_response -from swh.web.api.apiurls import APIUrls +from swh.web.api.apiurls import APIUrls, CategoryId from swh.web.utils import parse_rst @@ -316,6 +316,8 @@ def api_doc( route: str, + *, + category: CategoryId, noargs: bool = False, tags: List[str] = [], api_version: str = "1", @@ -351,6 +353,7 @@ doc_desc = doc_data["description"] APIUrls.add_doc_route( route, + category, re.split(r"\.\s", doc_desc)[0], noargs=noargs, api_version=api_version, diff --git a/swh/web/api/apiurls.py b/swh/web/api/apiurls.py --- a/swh/web/api/apiurls.py +++ b/swh/web/api/apiurls.py @@ -6,6 +6,8 @@ import functools from typing import Dict, List, Optional +from typing_extensions import Literal + from django.http.response import HttpResponseBase from rest_framework.decorators import api_view @@ -13,6 +15,10 @@ from swh.web.api.apiresponse import make_api_response from swh.web.utils.urlsindex import UrlsIndex +CategoryId = Literal[ + "Archive", "Batch download", "Metadata", "Request archival", "Miscellaneous", "test" +] + class APIUrls(UrlsIndex): """ @@ -35,6 +41,7 @@ def add_doc_route( cls, route: str, + category: CategoryId, docstring: str, noargs: bool = False, api_version: str = "1", @@ -49,6 +56,7 @@ route_view_name = "api-%s-%s" % (api_version, route_name) if route not in cls._apidoc_routes: d = { + "category": category, "docstring": docstring, "route": "/api/%s%s" % (api_version, route), "route_view_name": route_view_name, @@ -60,7 +68,7 @@ def api_route( url_pattern: str, - view_name: Optional[str] = None, + view_name: str, methods: List[str] = ["GET", "HEAD", "OPTIONS"], throttle_scope: str = "swh_api", api_version: str = "1", diff --git a/swh/web/api/templates/api-endpoints.html b/swh/web/api/templates/api-endpoints.html --- a/swh/web/api/templates/api-endpoints.html +++ b/swh/web/api/templates/api-endpoints.html @@ -36,7 +36,9 @@
-Description | - {% for route, doc in doc_routes %} + {% for doc in routes %}|||
---|---|---|---|
+ | {% url doc.route_view_name %} | {% else %} -+ | {{ doc.route }} @@ -67,17 +69,10 @@ {% endfor %} |
(?!search).+)/", "api-1-content-known") -@api_doc("/content/known/", tags=["hidden"]) +@api_doc("/content/known/", category="Archive", tags=["hidden"]) @format_docstring() def api_check_content_known(request: Request, q: Optional[str] = None): """ @@ -288,7 +288,7 @@ @api_route( r"/content/(?P[0-9a-z_:]*[0-9a-f]+)/", "api-1-content", checksum_args=["q"] ) -@api_doc("/content/") +@api_doc("/content/", category="Archive") @format_docstring() def api_content_metadata(request: Request, q: str): """ diff --git a/swh/web/api/views/directory.py b/swh/web/api/views/directory.py --- a/swh/web/api/views/directory.py +++ b/swh/web/api/views/directory.py @@ -24,7 +24,7 @@ "api-1-directory", checksum_args=["sha1_git"], ) -@api_doc("/directory/") +@api_doc("/directory/", category="Archive") @format_docstring() def api_directory(request: Request, sha1_git: str, path: Optional[str] = None): """ diff --git a/swh/web/api/views/graph.py b/swh/web/api/views/graph.py --- a/swh/web/api/views/graph.py +++ b/swh/web/api/views/graph.py @@ -77,7 +77,7 @@ @api_route(r"/graph/", "api-1-graph-doc") -@api_doc("/graph/") +@api_doc("/graph/", category="Miscellaneous") def api_graph(request: Request) -> None: """ .. http:get:: /api/1/graph/(graph_query)/ diff --git a/swh/web/api/views/identifiers.py b/swh/web/api/views/identifiers.py --- a/swh/web/api/views/identifiers.py +++ b/swh/web/api/views/identifiers.py @@ -17,7 +17,7 @@ @api_route(r"/resolve/(?P.+)/", "api-1-resolve-swhid") -@api_doc("/resolve/") +@api_doc("/resolve/", category="Archive") @format_docstring() def api_resolve_swhid(request: Request, swhid: str): """ @@ -74,7 +74,7 @@ @api_route(r"/known/", "api-1-known", methods=["POST"]) -@api_doc("/known/") +@api_doc("/known/", category="Archive") @format_docstring() def api_swhid_known(request: Request): """ diff --git a/swh/web/api/views/metadata.py b/swh/web/api/views/metadata.py --- a/swh/web/api/views/metadata.py +++ b/swh/web/api/views/metadata.py @@ -25,7 +25,7 @@ f"/raw-extrinsic-metadata/swhid/(?P {SWHID_RE})/", "api-1-raw-extrinsic-metadata-swhid", ) -@api_doc("/raw-extrinsic-metadata/swhid/") +@api_doc("/raw-extrinsic-metadata/swhid/", category="Metadata") @format_docstring() def api_raw_extrinsic_metadata_swhid(request: Request, target: str): """ @@ -222,7 +222,7 @@ f"/raw-extrinsic-metadata/swhid/(?P {SWHID_RE})/authorities/", "api-1-raw-extrinsic-metadata-swhid-authorities", ) -@api_doc("/raw-extrinsic-metadata/swhid/authorities/") +@api_doc("/raw-extrinsic-metadata/swhid/authorities/", category="Metadata") @format_docstring() def api_raw_extrinsic_metadata_swhid_authorities(request: Request, target: str): """ @@ -289,7 +289,7 @@ "/raw-extrinsic-metadata/origin/(?P .*)/authorities/", "api-1-raw-extrinsic-metadata-origin-authorities", ) -@api_doc("/raw-extrinsic-metadata/origin/authorities/") +@api_doc("/raw-extrinsic-metadata/origin/authorities/", category="Metadata") @format_docstring() def api_raw_extrinsic_metadata_origin_authorities(request: Request, origin_url: str): """ diff --git a/swh/web/api/views/origin.py b/swh/web/api/views/origin.py --- a/swh/web/api/views/origin.py +++ b/swh/web/api/views/origin.py @@ -61,7 +61,7 @@ @api_route(r"/origins/", "api-1-origins") -@api_doc("/origins/", noargs=True) +@api_doc("/origins/", category="Archive", noargs=True) @format_docstring(return_origin_array=DOC_RETURN_ORIGIN_ARRAY) def api_origins(request: Request): """ @@ -116,7 +116,7 @@ @api_route(r"/origin/(?P .+)/get/", "api-1-origin") -@api_doc("/origin/") +@api_doc("/origin/", category="Archive") @format_docstring(return_origin=DOC_RETURN_ORIGIN) def api_origin(request: Request, origin_url: str): """ @@ -173,7 +173,7 @@ "api-1-origin-search", throttle_scope="swh_api_origin_search", ) -@api_doc("/origin/search/") +@api_doc("/origin/search/", category="Archive") @format_docstring( return_origin_array=DOC_RETURN_ORIGIN_ARRAY, visit_types=_visit_types() ) @@ -255,7 +255,7 @@ @api_route(r"/origin/metadata-search/", "api-1-origin-metadata-search") -@api_doc("/origin/metadata-search/", noargs=True) +@api_doc("/origin/metadata-search/", category="Metadata", noargs=True) @format_docstring(return_origin_array=DOC_RETURN_ORIGIN_ARRAY) def api_origin_metadata_search(request: Request): """ @@ -299,7 +299,7 @@ @api_route(r"/origin/(?P .+)/visits/", "api-1-origin-visits") -@api_doc("/origin/visits/") +@api_doc("/origin/visits/", category="Archive") @format_docstring(return_origin_visit_array=DOC_RETURN_ORIGIN_VISIT_ARRAY) def api_origin_visits(request: Request, origin_url: str): """ @@ -391,7 +391,7 @@ "api-1-origin-visit-latest", throttle_scope="swh_api_origin_visit_latest", ) -@api_doc("/origin/visit/latest/") +@api_doc("/origin/visit/latest/", category="Archive") @format_docstring(return_origin_visit=DOC_RETURN_ORIGIN_VISIT) def api_origin_visit_latest(request: Request, origin_url: str): """ @@ -435,7 +435,7 @@ @api_route( r"/origin/(?P .+)/visit/(?P [0-9]+)/", "api-1-origin-visit" ) -@api_doc("/origin/visit/") +@api_doc("/origin/visit/", category="Archive") @format_docstring(return_origin_visit=DOC_RETURN_ORIGIN_VISIT) def api_origin_visit(request: Request, visit_id: str, origin_url: str): """ @@ -477,7 +477,7 @@ @api_route( r"/origin/(?P .+)/intrinsic-metadata/", "api-origin-intrinsic-metadata" ) -@api_doc("/origin/intrinsic-metadata/") +@api_doc("/origin/intrinsic-metadata/", category="Metadata") @format_docstring() def api_origin_intrinsic_metadata(request: Request, origin_url: str): """ diff --git a/swh/web/api/views/ping.py b/swh/web/api/views/ping.py --- a/swh/web/api/views/ping.py +++ b/swh/web/api/views/ping.py @@ -10,7 +10,7 @@ @api_route(r"/ping/", "api-1-ping") -@api_doc("/ping/", noargs=True) +@api_doc("/ping/", category="Miscellaneous", noargs=True) def ping(request: Request): """ .. http:get:: /api/1/ping/ diff --git a/swh/web/api/views/raw.py b/swh/web/api/views/raw.py --- a/swh/web/api/views/raw.py +++ b/swh/web/api/views/raw.py @@ -31,7 +31,7 @@ "api-1-raw-object", throttle_scope="swh_raw_object", ) -@api_doc("/raw/") +@api_doc("/raw/", category="Archive") @format_docstring() def api_raw_object(request: Request, swhid: str): """ diff --git a/swh/web/api/views/release.py b/swh/web/api/views/release.py --- a/swh/web/api/views/release.py +++ b/swh/web/api/views/release.py @@ -15,7 +15,7 @@ @api_route( r"/release/(?P [0-9a-f]+)/", "api-1-release", checksum_args=["sha1_git"] ) -@api_doc("/release/") +@api_doc("/release/", category="Archive") @format_docstring() def api_release(request: Request, sha1_git: str): """ diff --git a/swh/web/api/views/revision.py b/swh/web/api/views/revision.py --- a/swh/web/api/views/revision.py +++ b/swh/web/api/views/revision.py @@ -42,7 +42,7 @@ @api_route( r"/revision/(?P [0-9a-f]+)/", "api-1-revision", checksum_args=["sha1_git"] ) -@api_doc("/revision/") +@api_doc("/revision/", category="Archive") @format_docstring(return_revision=DOC_RETURN_REVISION) def api_revision(request: Request, sha1_git: str): """ @@ -85,7 +85,7 @@ "api-1-revision-raw-message", checksum_args=["sha1_git"], ) -@api_doc("/revision/raw/", tags=["hidden"]) +@api_doc("/revision/raw/", category="Archive", tags=["hidden"]) def api_revision_raw_message(request: Request, sha1_git: str): """Return the raw data of the message of revision identified by sha1_git""" raw = archive.lookup_revision_message(sha1_git) @@ -104,7 +104,7 @@ "api-1-revision-directory", checksum_args=["sha1_git"], ) -@api_doc("/revision/directory/") +@api_doc("/revision/directory/", category="Archive") @format_docstring() def api_revision_directory( request: Request, sha1_git: str, dir_path: Optional[str] = None @@ -162,7 +162,7 @@ "api-1-revision-log", checksum_args=["sha1_git"], ) -@api_doc("/revision/log/") +@api_doc("/revision/log/", category="Archive") @format_docstring(return_revision_array=DOC_RETURN_REVISION_ARRAY) def api_revision_log(request: Request, sha1_git: str): """ diff --git a/swh/web/api/views/snapshot.py b/swh/web/api/views/snapshot.py --- a/swh/web/api/views/snapshot.py +++ b/swh/web/api/views/snapshot.py @@ -18,7 +18,7 @@ "api-1-snapshot", checksum_args=["snapshot_id"], ) -@api_doc("/snapshot/") +@api_doc("/snapshot/", category="Archive") @format_docstring() def api_snapshot(request: Request, snapshot_id: str): """ diff --git a/swh/web/api/views/stat.py b/swh/web/api/views/stat.py --- a/swh/web/api/views/stat.py +++ b/swh/web/api/views/stat.py @@ -11,7 +11,7 @@ @api_route(r"/stat/counters/", "api-1-stat-counters") -@api_doc("/stat/counters/", noargs=True) +@api_doc("/stat/counters/", category="Miscellaneous", noargs=True) @format_docstring() def api_stats(request: Request): """ diff --git a/swh/web/api/views/utils.py b/swh/web/api/views/utils.py --- a/swh/web/api/views/utils.py +++ b/swh/web/api/views/utils.py @@ -83,9 +83,18 @@ @api_route(r"/", "api-1-endpoints") def api_endpoints(request): """Display the list of opened api endpoints.""" - routes = APIUrls.get_app_endpoints().copy() - for route, doc in routes.items(): + routes_by_category = {} + for route, doc in APIUrls.get_app_endpoints().items(): doc["doc_intro"] = doc["docstring"].split("\n\n")[0] - # Return a list of routes with consistent ordering - env = {"doc_routes": sorted(routes.items())} + routes_by_category.setdefault(doc["category"], []).append(doc) + + for routes in routes_by_category.values(): + routes.sort(key=lambda route: route["route"]) + + # sort routes by alphabetical category name, with 'miscellaneous' at the end + misc_routes = routes_by_category.pop("Miscellaneous") + sorted_routes = sorted(routes_by_category.items()) + sorted_routes.append(("Miscellaneous", misc_routes)) + + env = {"doc_routes": sorted_routes} return Response(env, template_name="api-endpoints.html") diff --git a/swh/web/api/views/vault.py b/swh/web/api/views/vault.py --- a/swh/web/api/views/vault.py +++ b/swh/web/api/views/vault.py @@ -72,7 +72,7 @@ throttle_scope="swh_vault_cooking", never_cache=True, ) -@api_doc("/vault/flat/") +@api_doc("/vault/flat/", category="Batch download") @format_docstring() def api_vault_cook_flat(request: Request, swhid: str): """ @@ -147,7 +147,7 @@ throttle_scope="swh_vault_cooking", never_cache=True, ) -@api_doc("/vault/directory/", tags=["deprecated"]) +@api_doc("/vault/directory/", category="Batch download", tags=["deprecated"]) @format_docstring() def api_vault_cook_directory(request: Request, dir_id: str): """ @@ -173,7 +173,7 @@ f"/vault/flat/(?P {SWHID_RE})/raw/", "api-1-vault-fetch-flat", ) -@api_doc("/vault/flat/raw/") +@api_doc("/vault/flat/raw/", category="Batch download") def api_vault_fetch_flat(request: Request, swhid: str): """ .. http:get:: /api/1/vault/flat/(swhid)/raw/ @@ -212,7 +212,9 @@ "api-1-vault-fetch-directory", checksum_args=["dir_id"], ) -@api_doc("/vault/directory/raw/", tags=["hidden", "deprecated"]) +@api_doc( + "/vault/directory/raw/", category="Batch download", tags=["hidden", "deprecated"] +) def api_vault_fetch_directory(request: Request, dir_id: str): """ .. http:get:: /api/1/vault/directory/(dir_id)/raw/ @@ -239,7 +241,7 @@ throttle_scope="swh_vault_cooking", never_cache=True, ) -@api_doc("/vault/gitfast/") +@api_doc("/vault/gitfast/", category="Batch download") @format_docstring() def api_vault_cook_gitfast(request: Request, swhid: str): """ @@ -314,7 +316,7 @@ throttle_scope="swh_vault_cooking", never_cache=True, ) -@api_doc("/vault/revision/gitfast/", tags=["deprecated"]) +@api_doc("/vault/revision/gitfast/", category="Batch download", tags=["deprecated"]) @format_docstring() def api_vault_cook_revision_gitfast(request: Request, rev_id: str): """ @@ -340,7 +342,7 @@ f"/vault/gitfast/(?P {SWHID_RE})/raw/", "api-1-vault-fetch-gitfast", ) -@api_doc("/vault/gitfast/raw/") +@api_doc("/vault/gitfast/raw/", category="Batch download") def api_vault_fetch_revision_gitfast(request: Request, swhid: str): """ .. http:get:: /api/1/vault/gitfast/(swhid)/raw/ @@ -379,7 +381,11 @@ "api-1-vault-fetch-revision_gitfast", checksum_args=["rev_id"], ) -@api_doc("/vault/revision_gitfast/raw/", tags=["hidden", "deprecated"]) +@api_doc( + "/vault/revision_gitfast/raw/", + category="Batch download", + tags=["hidden", "deprecated"], +) def _api_vault_revision_gitfast_raw(request: Request, rev_id: str): """ .. http:get:: /api/1/vault/revision/(rev_id)/gitfast/raw/ @@ -403,7 +409,7 @@ throttle_scope="swh_vault_cooking", never_cache=True, ) -@api_doc("/vault/git-bare/") +@api_doc("/vault/git-bare/", category="Batch download") @format_docstring() def api_vault_cook_git_bare(request: Request, swhid: str): """ @@ -478,7 +484,7 @@ f"/vault/git-bare/(?P {SWHID_RE})/raw/", "api-1-vault-fetch-git-bare", ) -@api_doc("/vault/git-bare/raw/") +@api_doc("/vault/git-bare/raw/", category="Batch download") def api_vault_fetch_revision_git_bare(request: Request, swhid: str): """ .. http:get:: /api/1/vault/git-bare/(swhid)/raw/ diff --git a/swh/web/save_code_now/api_views.py b/swh/web/save_code_now/api_views.py --- a/swh/web/save_code_now/api_views.py +++ b/swh/web/save_code_now/api_views.py @@ -40,7 +40,7 @@ throttle_scope="swh_save_origin", never_cache=True, ) -@api_doc("/origin/save/") +@api_doc("/origin/save/", category="Request archival") @format_docstring(visit_types=_savable_visit_types()) def api_save_origin(request: Request, visit_type: str, origin_url: str): """ diff --git a/swh/web/tests/api/test_apidoc.py b/swh/web/tests/api/test_apidoc.py --- a/swh/web/tests/api/test_apidoc.py +++ b/swh/web/tests/api/test_apidoc.py @@ -81,13 +81,13 @@ def test_apidoc_nodoc_failure(): with pytest.raises(Exception): - @api_doc("/my/nodoc/url/") + @api_doc("/my/nodoc/url/", "test") def apidoc_nodoc_tester(request, arga=0, argb=0): return Response(arga + argb) @api_route(r"/some/(?P [0-9]+)/(?P [0-9]+)/", "api-1-some-doc-route") -@api_doc("/some/doc/route/") +@api_doc("/some/doc/route/", category="test") def apidoc_route(request, myarg, myotherarg, akw=0): """ Sample doc @@ -106,7 +106,7 @@ @api_route(r"/test/error/(?P .+)/", "api-1-test-error") -@api_doc("/test/error/") +@api_doc("/test/error/", category="test") def apidoc_test_error_route(request, exc_name): """ Sample doc @@ -126,7 +126,7 @@ r"/some/full/(?P [0-9]+)/(?P [0-9]+)/", "api-1-some-complete-doc-route", ) -@api_doc("/some/complete/doc/route/") +@api_doc("/some/complete/doc/route/", category="test") def apidoc_full_stack(request, myarg, myotherarg, akw=0): """ Sample doc @@ -147,7 +147,7 @@ @api_route(r"/test/post/only/", "api-1-test-post-only", methods=["POST"]) -@api_doc("/test/post/only/") +@api_doc("/test/post/only/", category="test") def apidoc_test_post_only(request, exc_name): """ Sample doc @@ -337,7 +337,7 @@ @api_route(r"/post/endpoint/", "api-1-post-endpoint", methods=["POST"]) -@api_doc("/post/endpoint/") +@api_doc("/post/endpoint/", category="test") def apidoc_test_post_endpoint(request): """ .. http:post:: /api/1/post/endpoint/ @@ -427,7 +427,7 @@ @api_route(r"/endpoint/links/in/doc/", "api-1-endpoint-links-in-doc") -@api_doc("/endpoint/links/in/doc/") +@api_doc("/endpoint/links/in/doc/", category="test") def apidoc_test_endpoint_with_links_in_doc(request): """ .. http:get:: /api/1/post/endpoint/