diff --git a/osf/models/mixins.py b/osf/models/mixins.py index d39bd1a19d2..c9b8ea3eb04 100644 --- a/osf/models/mixins.py +++ b/osf/models/mixins.py @@ -30,6 +30,7 @@ ) from osf.models.notification_type import NotificationTypeEnum from osf.models.notification_subscription import NotificationSubscription +from .base import VersionedGuidMixin from .node_relation import NodeRelation from .nodelog import NodeLog from .subject import Subject @@ -1908,6 +1909,16 @@ def remove_contributor(self, contributor, auth, log=True, _force=False): save=False, ) + if isinstance(self, VersionedGuidMixin) and not self._id: + # Legacy/un-migrated versioned resources can't be saved -- Preprint.save() requires a + # valid _id. The contributor row is already removed above; there's + # nothing else to persist, so skip save + downstream signals. + logger.info( + f'Skipping save on {self.__class__.__name__} (pk={self.pk}) in remove_contributor: ' + 'resource has no valid _id.' + ) + return True + self.save() # send signal to remove this user from project subscriptions project_signals.contributor_removed.send(self, user=contributor) diff --git a/osf_tests/test_user.py b/osf_tests/test_user.py index 9d2e8b12628..d0284b9aa90 100644 --- a/osf_tests/test_user.py +++ b/osf_tests/test_user.py @@ -27,6 +27,7 @@ from osf.models import ( AbstractNode, OSFUser, + Preprint, Tag, Contributor, NotableDomain, @@ -2135,6 +2136,14 @@ def project_with_two_admins(self, user): project.save() return project + @pytest.fixture() + def preprint_with_two_admins(self, user): + second_admin_contrib = UserFactory() + preprint = PreprintFactory(creator=user) + preprint.add_contributor(second_admin_contrib, permissions=permissions.ADMIN) + preprint.save() + return preprint + @pytest.fixture() def project_with_two_admins_and_addon_credentials(self, user): second_admin_contrib = UserFactory() @@ -2338,6 +2347,16 @@ def test_can_gdpr_delete_shared_node_without_guid(self, user, project_with_two_a assert user.nodes.all().count() == 0 + def test_can_gdpr_delete_shared_preprint_without_guid(self, user, preprint_with_two_admins): + preprint_with_two_admins.versioned_guids.all().delete() + preprint = Preprint.objects.get(pk=preprint_with_two_admins.pk) + assert preprint._id is None + + with override_flag(ENABLE_GV, active=True): + user.gdpr_delete() + + assert user.preprints.all().count() == 0 + def test_cant_gdpr_delete_with_addon_credentials(self, user, project_with_two_admins_and_addon_credentials): with pytest.raises(UserStateError) as exc_info: