Skip to content

Commit

Permalink
services: Override publish and remove APIs to check for new config
Browse files Browse the repository at this point in the history
  • Loading branch information
sakshamarora1 committed Sep 20, 2024
1 parent 385c788 commit cd57bfd
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 40 deletions.
7 changes: 7 additions & 0 deletions invenio_rdm_records/resources/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

from ..services.errors import (
AccessRequestExistsError,
CommunityNotSelectedError,
GrantExistsError,
InvalidAccessRestrictions,
RecordDeletedException,
Expand Down Expand Up @@ -256,6 +257,12 @@ class RDMRecordResourceConfig(RecordResourceConfig, ConfiguratorMixin):
description=e.description,
)
),
CommunityNotSelectedError: create_error_handler(
HTTPJSONException(
code=400,
description="Cannot publish without selecting a community.",
)
),
}


Expand Down
38 changes: 20 additions & 18 deletions invenio_rdm_records/services/communities/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
from flask import current_app
from invenio_access.permissions import system_identity
from invenio_communities.proxies import current_communities
from invenio_drafts_resources.services.records.uow import (
ParentRecordCommitOp,
)
from invenio_drafts_resources.services.records.uow import ParentRecordCommitOp
from invenio_i18n import lazy_gettext as _
from invenio_notifications.services.uow import NotificationOp
from invenio_pidstore.errors import PIDDoesNotExistError
Expand All @@ -22,10 +20,7 @@
Service,
ServiceSchemaWrapper,
)
from invenio_records_resources.services.errors import (
PermissionDeniedError,
CannotRemoveCommunityError,
)
from invenio_records_resources.services.errors import PermissionDeniedError
from invenio_records_resources.services.uow import (
IndexRefreshOp,
RecordIndexOp,
Expand All @@ -40,6 +35,7 @@
from ...proxies import current_rdm_records, current_rdm_records_service
from ...requests import CommunityInclusion
from ..errors import (
CannotRemoveCommunityError,
CommunityAlreadyExists,
InvalidAccessRestrictions,
OpenRequestAlreadyExists,
Expand Down Expand Up @@ -207,20 +203,22 @@ def _remove(self, identity, community_id, record):
if community_id not in record.parent.communities.ids:
raise RecordCommunityMissing(record.id, community_id)

# If config is True and there is only 1 communities left to remove
# If config is true and there is only 1 communities left to remove
is_community_required = current_app.config["RDM_RECORD_ALWAYS_IN_COMMUNITY"]
is_last_community = len(record.parent.communities.ids) == 1
# Then, check for permissions to remove last community
can_remove_last_community = self.check_permission(
identity, "remove_community", record=record, community_id=community_id
)
if (
current_app.config["RDM_RECORD_ALWAYS_IN_COMMUNITY"]
and len(record.parent.communities.ids) == 1
is_community_required
and is_last_community
and not can_remove_last_community
):
if not self.check_permission(
identity, "remove_community", record=record, community_id=community_id
):
raise CannotRemoveCommunityError()
raise CannotRemoveCommunityError()
# check permission here, per community: curator cannot remove another community
self.require_permission(
identity, "remove_community", record=record, community_id=community_id
)
elif not can_remove_last_community:
raise PermissionDeniedError("remove_community")

# Default community is deleted when the exact same community is removed from the record
record.parent.communities.remove(community_id)
Expand All @@ -245,7 +243,11 @@ def remove(self, identity, id_, data, uow):
try:
self._remove(identity, community_id, record)
processed.append({"community": community_id})
except (RecordCommunityMissing, PermissionDeniedError) as ex:
except (
RecordCommunityMissing,
PermissionDeniedError,
CannotRemoveCommunityError,
) as ex:
errors.append(
{
"community": community_id,
Expand Down
12 changes: 12 additions & 0 deletions invenio_rdm_records/services/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,15 @@ class RecordSubmissionClosedCommunityError(PermissionDenied):
"""Record submission policy forbids non-members from submitting records to community."""

description = "Submission to this community is only allowed to community members."


class CommunityNotSelectedError(Exception):
"""Error thrown when a record is being created/updated with less than 1 community."""

description = "Cannot publish without selecting a community."


class CannotRemoveCommunityError(PermissionDenied):
"""Error thrown when the last community is being removed from the record."""

description = "Cannot remove. A record should be part of atleast 1 community."
2 changes: 1 addition & 1 deletion invenio_rdm_records/services/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
AccessGrant,
CommunityInclusionReviewers,
GuestAccessRequestToken,
IfOneCommunity,
IfCreate,
IfDeleted,
IfExternalDOIRecord,
IfFileIsLocal,
IfNewRecord,
IfOneCommunity,
IfRecordDeleted,
IfRequestType,
IfRestricted,
Expand Down
24 changes: 24 additions & 0 deletions invenio_rdm_records/services/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from invenio_drafts_resources.services.records import RecordService
from invenio_drafts_resources.services.records.uow import ParentRecordCommitOp
from invenio_records_resources.services import LinksTemplate, ServiceSchemaWrapper
from invenio_records_resources.services.errors import PermissionDeniedError
from invenio_records_resources.services.uow import (
RecordCommitOp,
RecordIndexDeleteOp,
Expand All @@ -37,6 +38,7 @@

from ..records.systemfields.deletion_status import RecordDeletionStatusEnum
from .errors import (
CommunityNotSelectedError,
DeletionStatusException,
EmbargoNotLiftedError,
RecordDeletedException,
Expand Down Expand Up @@ -404,6 +406,28 @@ def purge_record(self, identity, id_, uow=None):

raise NotImplementedError()

def publish(self, identity, id_, uow=None, expand=False):
"""Publish a draft.
Check for permissions to publish a draft and then call invenio_drafts_resourcs.services.records.services.publish()
"""
# Get the draft
draft = self.draft_cls.pid.resolve(id_, registered_only=False)

# If config is true and there are no communities selected
is_community_required = current_app.config["RDM_RECORD_ALWAYS_IN_COMMUNITY"]
is_community_missing = len(draft.parent.communities.ids) == 0
# Then, check for permissions to upload without community
can_publish_draft = self.check_permission(identity, "publish", record=draft)

if is_community_required and is_community_missing and not can_publish_draft:
raise CommunityNotSelectedError()
elif not can_publish_draft:
# If community is not missing or the community is not required, raise error if user doesn't have permissions
raise PermissionDeniedError("publish")

return super().publish(identity, id_, uow=uow, expand=expand)

#
# Search functions
#
Expand Down
47 changes: 26 additions & 21 deletions tests/resources/test_resources_communities.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
)
from invenio_rdm_records.records.api import RDMDraft, RDMRecord
from invenio_rdm_records.requests.community_inclusion import CommunityInclusion
from invenio_rdm_records.services.errors import InvalidAccessRestrictions
from invenio_rdm_records.services.errors import (
CommunityNotSelectedError,
InvalidAccessRestrictions,
)


def _add_to_community(db, record, community):
Expand Down Expand Up @@ -891,28 +894,29 @@ def test_add_record_to_restricted_community_submission_open_member(
processed = response.json["processed"]
assert len(processed) == 1


# Assure Records community exists tests
# -------------------------------------
from invenio_records_resources.services.errors import CommunityNotSelectedError, CannotRemoveCommunityError


@contextmanager
def ensure_record_community_exists_config(app):
"""
Context manager to ensure record community exists config
is set to True during a specific code block.
Parameters:
app: app fixture
Usage:
with ensure_record_community_exists_config(app):
# code block that requires the flag to be set
Context manager to ensure record community exists config
is set to True during a specific code block.
Parameters:
app: app fixture
Usage:
with ensure_record_community_exists_config(app):
# code block that requires the flag to be set
"""
try:
app.config["RDM_RECORD_ALWAYS_IN_COMMUNITY"] = True
yield
finally:
app.config["RDM_RECORD_ALWAYS_IN_COMMUNITY"] = False


def test_restricted_record_creation(
app, record_community, uploader, curator, community_owner, test_user, superuser
):
Expand Down Expand Up @@ -953,17 +957,17 @@ def test_remove_last_existing_non_existing_community(
client = uploader.login(client)
record = record_community.create_record()
with ensure_record_community_exists_config(app):
with pytest.raises(CannotRemoveCommunityError):
response = client.delete(
f"/records/{record.pid.pid_value}/communities",
headers=headers,
json=data,
)
assert response.status_code == 400
# Should get 3 errors: Can't remove community, 2 bad IDs
assert len(response.json["errors"]) == 3
record_saved = client.get(f"/records/{record.pid.pid_value}", headers=headers)
assert record_saved.json["parent"]["communities"]
response = client.delete(
f"/records/{record.pid.pid_value}/communities",
headers=headers,
json=data,
)
assert response.is_json
assert response.status_code == 200
# Should get 3 errors: Can't remove community, 2 bad IDs
assert len(response.json["errors"]) == 3
record_saved = client.get(f"/records/{record.pid.pid_value}", headers=headers)
assert record_saved.json["parent"]["communities"]


def test_remove_last_community_api_error_handling(
Expand Down Expand Up @@ -995,12 +999,13 @@ def test_remove_last_community_api_error_handling(
json=data,
)
assert response.is_json
assert response.status_code == 400
assert response.status_code == 200

record_saved = client.get(
f"/records/{record.pid.pid_value}", headers=headers
)
assert record_saved.json["parent"]["communities"]
assert len(response.json["errors"]) == 1

client = user.logout(client)
# check communities number
Expand Down

0 comments on commit cd57bfd

Please sign in to comment.