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 @@ -4,6 +4,7 @@ # See top-level LICENSE file for more information import base64 +import re import iso8601 @@ -135,6 +136,7 @@ result["metadata_url"] = reverse( "api-1-raw-extrinsic-metadata-get", url_args={"id": hashutil.hash_to_hex(metadata.id)}, + query_params={"filename": f"{target}_metadata"}, request=request, ) @@ -174,4 +176,16 @@ "Metadata not found. Use /raw-extrinsic-metadata/swhid/ to access metadata." ) - return HttpResponse(metadata[0].metadata, content_type="application/octet-stream") + response = HttpResponse( + metadata[0].metadata, content_type="application/octet-stream" + ) + + filename = request.query_params.get("filename") + if filename and re.match("[a-zA-Z0-9:._-]+", filename): + response["Content-disposition"] = f'attachment; filename="{filename}"' + else: + # It should always be not-None and match the regexp if the URL was created by + # /raw-extrinsic-metadata/swhid/, but we're better safe than sorry. + response["Content-disposition"] = "attachment" + + return response diff --git a/swh/web/tests/api/views/test_metadata.py b/swh/web/tests/api/views/test_metadata.py --- a/swh/web/tests/api/views/test_metadata.py +++ b/swh/web/tests/api/views/test_metadata.py @@ -39,6 +39,10 @@ rv = check_http_get_response(api_client, metadata_url, status_code=200) assert rv["Content-Type"] == "application/octet-stream" + assert ( + rv["Content-Disposition"] + == f'attachment; filename="{metadata.target}_metadata"' + ) assert rv.content == metadata.metadata