Changeset View
Changeset View
Standalone View
Standalone View
swh/web/api/views/content.py
# Copyright (C) 2015-2022 The Software Heritage developers | # Copyright (C) 2015-2022 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU Affero General Public License version 3, or any later version | # License: GNU Affero General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
import functools | import functools | ||||
import io | |||||
from typing import Optional | from typing import Optional | ||||
from django.http import HttpResponse | from django.http import FileResponse | ||||
from rest_framework.request import Request | from rest_framework.request import Request | ||||
from swh.web.api import utils | from swh.web.api import utils | ||||
from swh.web.api.apidoc import api_doc, format_docstring | from swh.web.api.apidoc import api_doc, format_docstring | ||||
from swh.web.api.apiurls import api_route | from swh.web.api.apiurls import api_route | ||||
from swh.web.api.views.utils import api_lookup | from swh.web.api.views.utils import api_lookup | ||||
from swh.web.common import archive | from swh.web.common import archive | ||||
from swh.web.common.exc import NotFoundExc | from swh.web.common.exc import NotFoundExc | ||||
▲ Show 20 Lines • Show All 160 Lines • ▼ Show 20 Lines | return api_lookup( | ||||
archive.lookup_content_ctags, | archive.lookup_content_ctags, | ||||
q, | q, | ||||
notfound_msg="No ctags symbol found for content {}.".format(q), | notfound_msg="No ctags symbol found for content {}.".format(q), | ||||
enrich_fn=utils.enrich_metadata_endpoint, | enrich_fn=utils.enrich_metadata_endpoint, | ||||
request=request, | request=request, | ||||
) | ) | ||||
class _ROBytesIO(io.BufferedIOBase): | |||||
"""Like BytesIO, but read-only so it does not need to copy the buffer it contains""" | |||||
def __init__(self, initial_bytes): | |||||
self._offset = 0 | |||||
self._buffer = initial_bytes | |||||
def read(self, size=-1): | |||||
old_offset = self._offset | |||||
if size < 0: | |||||
self._offset = len(self._index) | |||||
else: | |||||
self._offset += len(self._buffer) | |||||
return self._buffer[old_offset : self._offset] | |||||
def read1(self, size=-1): | |||||
return self.read(size) | |||||
def detach(self, b): | |||||
self._unsupported("detach") | |||||
def write(self, b): | |||||
self._unsupported("write") | |||||
@api_route( | @api_route( | ||||
r"/content/(?P<q>[0-9a-z_:]*[0-9a-f]+)/raw/", | r"/content/(?P<q>[0-9a-z_:]*[0-9a-f]+)/raw/", | ||||
"api-1-content-raw", | "api-1-content-raw", | ||||
checksum_args=["q"], | checksum_args=["q"], | ||||
) | ) | ||||
@api_doc("/content/raw/") | @api_doc("/content/raw/") | ||||
def api_content_raw(request: Request, q: str): | def api_content_raw(request: Request, q: str): | ||||
""" | """ | ||||
Show All 17 Lines | .. http:get:: /api/1/content/[(hash_type):](hash)/raw/ | ||||
:statuscode 404: requested content can not be found in the archive | :statuscode 404: requested content can not be found in the archive | ||||
**Example:** | **Example:** | ||||
.. parsed-literal:: | .. parsed-literal:: | ||||
:swh_web_api:`content/sha1:dc2830a9e72f23c1dfebef4413003221baa5fb62/raw/` | :swh_web_api:`content/sha1:dc2830a9e72f23c1dfebef4413003221baa5fb62/raw/` | ||||
""" | """ | ||||
def generate(content): | |||||
yield content["data"] | |||||
content_raw = archive.lookup_content_raw(q) | content_raw = archive.lookup_content_raw(q) | ||||
if not content_raw: | if not content_raw: | ||||
raise NotFoundExc("Content %s is not found." % q) | raise NotFoundExc("Content %s is not found." % q) | ||||
filename = request.query_params.get("filename") | filename = request.query_params.get("filename") | ||||
if not filename: | if not filename: | ||||
filename = "content_%s_raw" % q.replace(":", "_") | filename = "content_%s_raw" % q.replace(":", "_") | ||||
response = HttpResponse( | response = FileResponse( | ||||
generate(content_raw), content_type="application/octet-stream" | _ROBytesIO(content_raw["data"]), | ||||
filename=filename, | |||||
content_type="application/octet-stream", | |||||
as_attachment=True, | |||||
) | ) | ||||
response["Content-disposition"] = "attachment; filename=%s" % filename | response["Content-Length"] = len(content_raw["data"]) | ||||
return response | return response | ||||
@api_route(r"/content/symbol/(?P<q>.+)/", "api-1-content-symbol") | @api_route(r"/content/symbol/(?P<q>.+)/", "api-1-content-symbol") | ||||
@api_doc("/content/symbol/", tags=["hidden"]) | @api_doc("/content/symbol/", tags=["hidden"]) | ||||
def api_content_symbol(request: Request, q: str): | def api_content_symbol(request: Request, q: str): | ||||
"""Search content objects by `Ctags <http://ctags.sourceforge.net/>`_-style | """Search content objects by `Ctags <http://ctags.sourceforge.net/>`_-style | ||||
symbol (e.g., function name, data type, method, ...). | symbol (e.g., function name, data type, method, ...). | ||||
▲ Show 20 Lines • Show All 166 Lines • Show Last 20 Lines |