Skip to content

Commit

Permalink
datacite: hide DOI on delete record
Browse files Browse the repository at this point in the history
* datacite: restore DOI on restore record
  • Loading branch information
kpsherva committed Oct 12, 2023
1 parent dfad3ee commit 46b669d
Show file tree
Hide file tree
Showing 9 changed files with 450 additions and 26 deletions.
36 changes: 36 additions & 0 deletions invenio_rdm_records/services/components/pids.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,16 @@ def edit(self, identity, draft=None, record=None):
self.service.pids.pid_manager.validate(pids, record)
draft.pids = pids

def delete_record(self, identity, data=None, record=None, uow=None):
"""Process pids on delete record."""
record_pids = copy(record.get("pids", {}))
self.service.pids.pid_manager.discard_all(record_pids, soft_delete=True)

def restore_record(self, identity, record=None, uow=None):
"""Restore previously invalidated pids."""
record_pids = copy(record.get("pids", {}))
self.service.pids.pid_manager.restore_all(record_pids)


class ParentPIDsComponent(ServiceComponent):
"""Service component for record parent PIDs."""
Expand Down Expand Up @@ -174,3 +184,29 @@ def publish(self, identity, draft=None, record=None):
self.uow.register(
TaskOp(register_or_update_pid, record["id"], scheme, parent=True)
)

def delete_record(self, identity, data=None, record=None, uow=None):
"""Process pids on delete record."""
record_cls = self.service.record_cls
parent_pids = copy(record.parent.get("pids", {}))
if record_cls.next_latest_published_record_by_parent(record.parent) is None:
self.service.pids.parent_pid_manager.discard_all(
parent_pids, soft_delete=True
)

# Async register/update tasks after transaction commit.
for scheme in parent_pids.keys():
self.uow.register(
TaskOp(register_or_update_pid, record["id"], scheme, parent=True)
)

def restore_record(self, identity, record=None, uow=None):
"""Restore previously invalidated pids."""
parent_pids = copy(record.parent.get("pids", {}))
self.service.pids.parent_pid_manager.restore_all(parent_pids)

# Async register/update tasks after transaction commit.
for scheme in parent_pids.keys():
self.uow.register(
TaskOp(register_or_update_pid, record["id"], scheme, parent=True)
)
1 change: 0 additions & 1 deletion invenio_rdm_records/services/github/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from invenio_i18n import _
from marshmallow import Schema, ValidationError
from mistune import markdown
from nameparser import HumanName


class RDMReleaseMetadata(object):
Expand Down
38 changes: 23 additions & 15 deletions invenio_rdm_records/services/pids/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,33 +231,41 @@ def register(self, record, scheme, url):

provider.register(pid, record=record, url=url)

def discard(self, scheme, identifier, provider_name=None):
def discard(self, scheme, identifier, provider_name=None, soft_delete=False):
"""Discard a PID."""
provider = self._get_provider(scheme, provider_name)
pid = provider.get(identifier)
if not provider.can_modify(pid):
raise ValidationError(
message=[
{
"field": f"pids.{scheme}",
"message": _(
"Cannot discard a reserved or registered persistent "
"identifier."
),
}
]
)

provider.delete(pid)
# the provider should check the conditions of deletion
provider.delete(pid, soft_delete=soft_delete)

def restore(self, scheme, identifier, provider_name=None):
"""Restore previously invalidated DOI."""
provider = self._get_provider(scheme, provider_name)
pid = provider.get(identifier)
provider.restore(pid)

def restore_all(self, pids):
"""Restore all pids."""
for scheme, pid_attrs in pids.items():
try:
self.restore(
scheme,
pid_attrs["identifier"],
pid_attrs["provider"],
)
except PIDDoesNotExistError:
pass # might not have been saved to DB yet

def discard_all(self, pids):
def discard_all(self, pids, soft_delete=False):
"""Discard all PIDs."""
for scheme, pid_attrs in pids.items():
try:
self.discard(
scheme,
pid_attrs["identifier"],
pid_attrs["provider"],
soft_delete=soft_delete,
)
except PIDDoesNotExistError:
pass # might not have been saved to DB yet
9 changes: 7 additions & 2 deletions invenio_rdm_records/services/pids/providers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,17 @@ def update(self, pid, **kwargs):
"""Update information about the persistent identifier."""
pass

def delete(self, pid, **kwargs):
def restore(self, pid, **kwargs):
"""Update information about the persistent identifier."""
pass

def delete(self, pid, soft_delete=False, **kwargs):
"""Delete a persistent identifier.
See: :meth:`invenio_pidstore.models.PersistentIdentifier.delete`.
"""
return pid.delete()
if not soft_delete:
return pid.delete()

def validate(self, record, identifier=None, provider=None, **kwargs):
"""Validate the attributes of the identifier.
Expand Down
4 changes: 4 additions & 0 deletions invenio_rdm_records/services/pids/providers/datacite.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ def update(self, pid, record, url=None, **kwargs):

return True

def restore(self, pid, **kwargs):
"""Restore previously deactivated DOI."""
self.client.api.show_doi(pid.pid_value)

def delete(self, pid, **kwargs):
"""Delete/unregister a registered DOI.
Expand Down
2 changes: 1 addition & 1 deletion invenio_rdm_records/services/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def delete_record(
)

new_record_latest_version = None
if record.versions.is_latest == True:
if record.versions.is_latest is True:
# set latest to the previous non deleted record
new_record_latest_version = (
self.record_cls.next_latest_published_record_by_parent(record.parent)
Expand Down
11 changes: 11 additions & 0 deletions tests/fake_datacite_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ def hide_doi(self, doi):
"""
return Mock()

def show_doi(self, doi):
"""Show a previously hidden DOI ... not.
This DOI will no
longer be found in DataCite Search
:param doi: DOI to hide e.g. 10.12345/1.
:return:
"""
return Mock()

def check_doi(self, doi):
"""Check doi structure.
Expand Down
11 changes: 5 additions & 6 deletions tests/services/pids/test_pids_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,13 +580,13 @@ def test_pids_records_updates_managed_to_external_fail(
# create draft
draft = service.edit(identity_simple, record.id)
# fail to remove doi due to lack of permissions (validation error)
with pytest.raises(ValidationError):
service.pids.discard(identity_simple, draft.id, "doi")
# with pytest.raises(ValidationError):
service.pids.discard(identity_simple, draft.id, "doi")

doi = draft["pids"]["doi"]["identifier"]
assert doi
pid = provider.get(pid_value=doi)
assert pid.status in [PIDStatus.RESERVED, PIDStatus.REGISTERED]
assert pid.status in [PIDStatus.DELETED]


def test_pids_records_updates_managed_to_no_pid_fail(
Expand All @@ -601,13 +601,12 @@ def test_pids_records_updates_managed_to_no_pid_fail(
# create draft
draft = service.edit(identity_simple, record.id)
# fail to remove doi due to lack of permissions (validation error)
with pytest.raises(ValidationError):
service.pids.discard(identity_simple, draft.id, "doi")
service.pids.discard(identity_simple, draft.id, "doi")

doi = draft["pids"]["doi"]["identifier"]
assert doi
pid = provider.get(pid_value=doi)
assert pid.status in [PIDStatus.RESERVED, PIDStatus.REGISTERED]
assert pid.status in [PIDStatus.DELETED]


# Publishing
Expand Down
Loading

0 comments on commit 46b669d

Please sign in to comment.