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 @@ -7,6 +7,7 @@ from typing import Dict, List, Optional from django.http import HttpResponse +from django.utils.cache import add_never_cache_headers from rest_framework.decorators import api_view from swh.web.api import throttling @@ -65,6 +66,7 @@ throttle_scope: str = "swh_api", api_version: str = "1", checksum_args: Optional[List[str]] = None, + never_cache: bool = False, ): """ Decorator to ease the registration of an API endpoint @@ -78,6 +80,7 @@ throttle_scope: Named scope for rate limiting api_version: web API version checksum_args: list of view argument names holding checksum values + never_cache: define if api response must be cached """ @@ -97,9 +100,16 @@ response = response["data"] # check if HTTP response needs to be created if not isinstance(response, HttpResponse): - return make_api_response(request, data=response, doc_data=doc_data) + api_response = make_api_response( + request, data=response, doc_data=doc_data + ) else: - return response + api_response = response + + if never_cache: + add_never_cache_headers(api_response) + + return api_response # small hacks for correctly generating API endpoints index doc api_view_f.__name__ = f.__name__ diff --git a/swh/web/api/views/origin_save.py b/swh/web/api/views/origin_save.py --- a/swh/web/api/views/origin_save.py +++ b/swh/web/api/views/origin_save.py @@ -3,8 +3,6 @@ # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information -from django.views.decorators.cache import never_cache - from swh.web.api.apidoc import api_doc, format_docstring from swh.web.api.apiurls import api_route from swh.web.common.origin_save import ( @@ -13,12 +11,12 @@ ) -@never_cache @api_route( r"/origin/save/(?P.+)/url/(?P.+)/", "api-1-save-origin", methods=["GET", "POST"], throttle_scope="swh_save_origin", + never_cache=True, ) @api_doc("/origin/save/") @format_docstring() 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 @@ -7,7 +7,6 @@ from django.http import HttpResponse from django.shortcuts import redirect -from django.views.decorators.cache import never_cache from swh.model import hashutil from swh.web.api.apidoc import api_doc, format_docstring @@ -54,13 +53,13 @@ } -@never_cache @api_route( r"/vault/directory/(?P[0-9a-f]+)/", "api-1-vault-cook-directory", methods=["GET", "POST"], checksum_args=["dir_id"], throttle_scope="swh_vault_cooking", + never_cache=True, ) @api_doc("/vault/directory/") @format_docstring() @@ -160,13 +159,13 @@ return response -@never_cache @api_route( r"/vault/revision/(?P[0-9a-f]+)/gitfast/", "api-1-vault-cook-revision_gitfast", methods=["GET", "POST"], checksum_args=["rev_id"], throttle_scope="swh_vault_cooking", + never_cache=True, ) @api_doc("/vault/revision/gitfast/") @format_docstring() diff --git a/swh/web/tests/api/test_apiurls.py b/swh/web/tests/api/test_apiurls.py new file mode 100644 --- /dev/null +++ b/swh/web/tests/api/test_apiurls.py @@ -0,0 +1,38 @@ +# Copyright (C) 2020 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU Affero General Public License version 3, or any later version +# See top-level LICENSE file for more information + + +from swh.web.api.apiurls import api_route +from swh.web.common.utils import reverse +from swh.web.tests.utils import check_api_get_responses + + +@api_route(r"/some/route/(?P[0-9]+)/", "api-1-some-route") +def api_some_route(request, int_arg): + return {"result": int(int_arg)} + + +@api_route( + r"/never/cache/route/(?P[0-9]+)/", + "api-1-never-cache-route", + never_cache=True, +) +def api_never_cache_route(request, int_arg): + return {"result": int(int_arg)} + + +def test_api_route_with_cache(api_client): + url = reverse("api-1-some-route", url_args={"int_arg": 1}) + resp = check_api_get_responses(api_client, url, status_code=200) + assert resp.data == {"result": 1} + assert "Cache-Control" not in resp + + +def test_api_route_never_cache(api_client): + url = reverse("api-1-never-cache-route", url_args={"int_arg": 1}) + resp = check_api_get_responses(api_client, url, status_code=200) + assert resp.data == {"result": 1} + assert "Cache-Control" in resp + assert resp["Cache-Control"] == "max-age=0, no-cache, no-store, must-revalidate"