Skip to content

Commit

Permalink
Merge pull request #11 from oarepo/krist/non-duplicable-fix
Browse files Browse the repository at this point in the history
Krist/non duplicable fix
  • Loading branch information
SilvyPuzzlewell authored Mar 27, 2024
2 parents ac60865 + e1cc775 commit 6959444
Show file tree
Hide file tree
Showing 18 changed files with 257 additions and 130 deletions.
3 changes: 1 addition & 2 deletions oarepo_requests/actions/edit_topic.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# from .generic import AcceptAction
from invenio_requests.customizations import actions

from ..utils import get_matching_service_for_record
Expand All @@ -12,5 +11,5 @@ def execute(self, identity, uow):
topic_service = get_matching_service_for_record(topic)
if not topic_service:
raise KeyError(f"topic {topic} service not found")
topic_service.edit(identity, topic.id, uow=uow)
topic_service.edit(identity, topic["id"], uow=uow)
super().execute(identity, uow)
16 changes: 16 additions & 0 deletions oarepo_requests/actions/generic.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
from invenio_requests.customizations import actions
from invenio_requests.customizations.actions import RequestActions
from invenio_requests.errors import CannotExecuteActionError


class AutoAcceptSubmitAction(actions.SubmitAction):
log_event = True

def execute(self, identity, uow):
super().execute(identity, uow)
action_obj = RequestActions.get_action(self.request, "accept")
if not action_obj.can_execute():
raise CannotExecuteActionError("accept")
action_obj.execute(identity, uow)


"""
not needed for now
Expand Down
4 changes: 2 additions & 2 deletions oarepo_requests/resources/record/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ class RecordRequestsResourceConfig:
request_view_args = RecordResourceConfig.request_view_args | {
"request_type": ma.fields.Str()
}

"""
@property
def response_handlers(self):
return {
**RequestsResourceConfig.routes,
**RecordResourceConfig.response_handlers,
"application/vnd.inveniordm.v1+json": ResponseHandler(
OARepoRequestsUIJSONSerializer()
),
**super().response_handlers,
}
"""
4 changes: 2 additions & 2 deletions oarepo_requests/types/delete_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

from oarepo_requests.actions.delete_topic import DeleteTopicAcceptAction

from .generic import OARepoRequestType
from .generic import NonDuplicableOARepoRequestType


class DeleteRecordRequestType(OARepoRequestType):
class DeleteRecordRequestType(NonDuplicableOARepoRequestType):
available_actions = {
**RequestType.available_actions,
"accept": DeleteTopicAcceptAction,
Expand Down
6 changes: 4 additions & 2 deletions oarepo_requests/types/edit_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
from oarepo_runtime.i18n import lazy_gettext as _

from oarepo_requests.actions.edit_topic import EditTopicAcceptAction
from oarepo_requests.actions.generic import AutoAcceptSubmitAction

from .generic import OARepoRequestType
from .generic import NonDuplicableOARepoRequestType


class EditRecordRequestType(OARepoRequestType):
class EditRecordRequestType(NonDuplicableOARepoRequestType):
available_actions = {
**RequestType.available_actions,
"submit": AutoAcceptSubmitAction,
"accept": EditTopicAcceptAction,
}
description = _("Request re-opening of published record")
Expand Down
29 changes: 29 additions & 0 deletions oarepo_requests/types/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
from invenio_requests.customizations import RequestType
from invenio_requests.proxies import current_requests_service

from oarepo_requests.errors import OpenRequestAlreadyExists
from oarepo_requests.utils import open_request_exists


class OARepoRequestType(RequestType):
def can_create(self, identity, data, receiver, topic, creator, *args, **kwargs):
Expand All @@ -21,3 +24,29 @@ def can_possibly_create(self, identity, topic, *args, **kwargs):
except PermissionDeniedError:
return False
return True


class NonDuplicableOARepoRequestType(OARepoRequestType):

def can_create(self, identity, data, receiver, topic, creator, *args, **kwargs):
if open_request_exists(topic, self.type_id):
raise OpenRequestAlreadyExists(self, topic)
current_requests_service.require_permission(identity, "create")

@classmethod
def can_possibly_create(self, identity, topic, *args, **kwargs):
"""
used for checking whether there is any situation where the client can create a request of this type
it's different to just using can create with no receiver and data because that checks specifically
for situation without them while this method is used to check whether there is a possible situation
a user might create this request
eg. for the purpose of serializing a link on associated record
"""

if open_request_exists(topic, self.type_id):
return False
try:
current_requests_service.require_permission(identity, "create")
except PermissionDeniedError:
return False
return True
4 changes: 2 additions & 2 deletions oarepo_requests/types/publish_draft.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

from oarepo_requests.actions.publish_draft import PublishDraftAcceptAction

from .generic import OARepoRequestType
from .generic import NonDuplicableOARepoRequestType


class PublishDraftRequestType(OARepoRequestType):
class PublishDraftRequestType(NonDuplicableOARepoRequestType):
available_actions = {
**RequestType.available_actions,
"accept": PublishDraftAcceptAction,
Expand Down
45 changes: 23 additions & 22 deletions oarepo_requests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from invenio_requests.resolvers.registry import ResolverRegistry
from invenio_search.engine import dsl

from oarepo_requests.errors import OpenRequestAlreadyExists


def allowed_request_types_for_record(record):
request_types = current_request_type_registry._registered_types
Expand Down Expand Up @@ -47,17 +45,19 @@ def allowed_request_types_for_record_cls(queryied_record_cls):
return ret


def request_exists(
def _reference_query_term(term, reference):
return dsl.Q(
"term", **{f"{term}.{list(reference.keys())[0]}": list(reference.values())[0]}
)


def search_requests(
identity,
topic,
type_id,
topic_type=None,
topic_reference=None,
receiver_reference=None,
creator_reference=None,
is_open=False,
receiver_type=None,
receiver_id=None,
creator_type=None,
creator_id=None,
topic_id=None,
add_args=None,
):
"""Return the request id if an open request already exists, else None."""
Expand All @@ -66,29 +66,30 @@ def request_exists(
dsl.Q("term", **{"type": type_id}),
dsl.Q("term", **{"is_open": is_open}),
]
if receiver_reference:
must.append(_reference_query_term("receiver", receiver_reference))
if creator_reference:
must.append(_reference_query_term("creator", creator_reference))
if topic_reference:
must.append(_reference_query_term("topic", topic_reference))
if add_args:
must += add_args
if receiver_type:
must.append(dsl.Q("term", **{f"receiver.{receiver_type}": receiver_id}))
if creator_type:
must.append(dsl.Q("term", **{f"creator.{creator_type}": creator_id}))
if topic_type:
topic_id = topic_id if topic_id is not None else topic.pid.pid_value
must.append(dsl.Q("term", **{f"topic.{topic_type}": topic_id}))
results = current_requests_service.search(
identity,
extra_filter=dsl.query.Bool(
"must",
must=must,
),
)
return next(results.hits)["id"] if results.total > 0 else None
return results.hits


def open_request_exists(topic, type_id, creator=None):
existing_request = request_exists(system_identity, topic, type_id, is_open=True)
if existing_request:
raise OpenRequestAlreadyExists(existing_request, topic)
def open_request_exists(topic_or_reference, type_id):
topic_reference = ResolverRegistry.reference_entity(topic_or_reference, raise_=True)
existing_requests = search_requests(
system_identity, type_id, topic_reference=topic_reference, is_open=True
)
return bool(list(existing_requests))


# TODO these things are related and possibly could be approached in a less convoluted manner? For example, global model->services map would help
Expand Down
1 change: 0 additions & 1 deletion run-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pip install "oarepo[tests]==$OAREPO_VERSION.*"
pip install -e "./$BUILD_TEST_DIR/${MODEL}"
pip install oarepo-ui
pip install -e .
pip install -e ./tests/mock_requests

pytest $BUILD_TEST_DIR/test_requests
pytest $BUILD_TEST_DIR/test_ui
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = oarepo-requests
version = 1.1.5
version = 1.1.6
description =
authors = Ronald Krist <[email protected]>
readme = README.md
Expand Down
15 changes: 15 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ def ret_data(record_id):
return ret_data


@pytest.fixture()
def edit_record_data_function():
def ret_data(record_id):
return {
"request_type": "thesis_edit_record",
"topic": {"thesis": record_id},
}

return ret_data


@pytest.fixture()
def delete_record_data_function():
def ret_data(record_id):
Expand Down Expand Up @@ -140,10 +151,14 @@ def app_config(app_config):
def default_receiver(*args, **kwargs):
return {"user": "2"}

def none_receiver(*args, **kwargs):
return None

app_config["OAREPO_REQUESTS_DEFAULT_RECEIVER"] = {
"thesis_publish_draft": default_receiver,
"thesis_delete_record": default_receiver,
"thesis_non_duplicable": default_receiver,
"thesis_edit_record": none_receiver,
}

"""
Expand Down
Empty file.
35 changes: 0 additions & 35 deletions tests/mock_requests/mock_requests/type.py

This file was deleted.

10 changes: 0 additions & 10 deletions tests/mock_requests/setup.py

This file was deleted.

Loading

0 comments on commit 6959444

Please sign in to comment.