# Copyright (C) 2017-2020  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
from rest_framework.request import Request
from swh.deposit.api.common import APIDelete, APIPut, ParsedRequestHeaders
from swh.deposit.config import DEPOSIT_STATUS_LOAD_SUCCESS
from swh.deposit.errors import BAD_REQUEST, DepositError, ParserError
from swh.deposit.models import Deposit
from swh.deposit.parsers import SWHAtomEntryParser, SWHMultiPartParser
from swh.model.swhids import QualifiedSWHID
[docs]
class EditAPI(APIPut, APIDelete):
    """Deposit request class defining api endpoints for sword deposit.
    What's known as 'Edit-IRI' in the sword specification.
    HTTP verbs supported: PUT, DELETE
    """
    parser_classes = (SWHMultiPartParser, SWHAtomEntryParser)
[docs]
    def restrict_access(
        self, request: Request, headers: ParsedRequestHeaders, deposit: Deposit
    ) -> None:
        """Relax restriction access to allow metadata update on deposit with status "done" when
        a swhid is provided.
        """
        if (
            request.method == "PUT"
            and headers.swhid is not None
            and deposit.status == DEPOSIT_STATUS_LOAD_SUCCESS
        ):
            # Allow metadata update on deposit with status "done" when swhid provided
            return
        # otherwise, let the standard access restriction check occur
        super().restrict_access(request, headers, deposit) 
[docs]
    def process_put(
        self,
        request,
        headers: ParsedRequestHeaders,
        collection_name: str,
        deposit: Deposit,
    ) -> None:
        """This allows the following scenarios:
        - multipart: replace all the deposit (status partial) metadata and archive
          with the provided ones.
        - atom: replace all the deposit (status partial) metadata with the
          provided ones.
        - with swhid, atom: Add new metadata to deposit (status done) with provided ones
          and push such metadata to the metadata storage directly.
           source:
           - http://swordapp.github.io/SWORDv2-Profile/SWORDProfile.html#protocoloperations_editingcontent_metadata
           - http://swordapp.github.io/SWORDv2-Profile/SWORDProfile.html#protocoloperations_editingcontent_multipart
        Raises:
            400 if any of the following occur:
            - the swhid provided and the deposit swhid do not match
            - the provided metadata xml file is malformed
            - the provided xml atom entry is empty
            - the provided swhid does not exist in the archive
        """  # noqa
        swhid = headers.swhid
        if swhid is None:
            if request.content_type.startswith("multipart/"):
                self._multipart_upload(
                    request,
                    headers,
                    collection_name,
                    deposit=deposit,
                    replace_archives=True,
                    replace_metadata=True,
                )
            else:
                # standard metadata update (replace all metadata already provided to the
                # deposit by the new ones)
                self._atom_entry(
                    request,
                    headers,
                    collection_name,
                    deposit=deposit,
                    replace_metadata=True,
                )
            return
        # Update metadata on a deposit already ingested
        # Write to the metadata storage (and the deposit backend)
        # no ingestion triggered
        assert deposit.status == DEPOSIT_STATUS_LOAD_SUCCESS
        if swhid != deposit.swhid:
            raise DepositError(
                BAD_REQUEST,
                f"Mismatched provided SWHID {swhid} with deposit's {deposit.swhid}.",
                "The provided SWHID does not match the deposit to update. "
                "Please ensure you send the correct deposit SWHID.",
            )
        try:
            raw_metadata, metadata_tree = self._read_metadata(request.data)
        except ParserError:
            raise DepositError(
                BAD_REQUEST,
                "Malformed xml metadata",
                "The xml received is malformed. "
                "Please ensure your metadata file is correctly formatted.",
            )
        if len(metadata_tree) == 0:
            raise DepositError(
                BAD_REQUEST,
                "Empty body request is not supported",
                "Atom entry deposit is supposed to send for metadata. "
                "If the body is empty, there is no metadata.",
            )
        _, deposit, deposit_request = self._store_metadata_deposit(
            deposit,
            QualifiedSWHID.from_string(swhid),
            metadata_tree,
            raw_metadata,
            deposit.origin_url,
        ) 
[docs]
    def process_delete(self, req, collection_name: str, deposit: Deposit) -> None:
        """Delete the container (deposit).
        source: http://swordapp.github.io/SWORDv2-Profile/SWORDProfile.html#protocoloperations_deleteconteiner  # noqa
        """
        self._delete_deposit(collection_name, deposit)