Skip to content

Commit

Permalink
Merge pull request #38 from oarepo/krist/be-377-bridge-workflows-to-o…
Browse files Browse the repository at this point in the history
…arepo-requests

Krist/be 377 bridge workflows to oarepo requests
  • Loading branch information
mesemus authored Aug 2, 2024
2 parents 026bf01 + c31fded commit 521b2b2
Show file tree
Hide file tree
Showing 51 changed files with 1,044 additions and 385 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ dist/

tests/thesis
thesis
thesis_workflows/

todo.md
node_modules
Expand Down
77 changes: 77 additions & 0 deletions oarepo_requests/actions/components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import abc
import contextlib
from typing import Generator

from invenio_requests.customizations import RequestActions
from invenio_requests.errors import CannotExecuteActionError

from oarepo_requests.services.permissions.identity import request_active


class RequestActionComponent:
@abc.abstractmethod
def apply(
self, identity, request_type, action, topic, uow, *args, **kwargs
) -> Generator:
"""
:param action:
:param identity:
:param uow:
:param args:
:param kwargs:
:return:
"""


class RequestIdentityComponent(RequestActionComponent):
@contextlib.contextmanager
def apply(self, identity, request_type, action, topic, uow, *args, **kwargs):

identity.provides.add(request_active)
try:
yield
finally:
if request_active in identity.provides:
identity.provides.remove(request_active)


class WorkflowTransitionComponent(RequestActionComponent):

@contextlib.contextmanager
def apply(self, identity, request_type, action, topic, uow, *args, **kwargs):
from oarepo_workflows.proxies import current_oarepo_workflows

yield
transitions = (
current_oarepo_workflows.get_workflow(topic)
.requests()[request_type.type_id]
.transitions
)
target_state = transitions[action.status_to]
if (
target_state and not topic.model.is_deleted
): # commit doesn't work on deleted record?
current_oarepo_workflows.set_state(
identity,
topic,
target_state,
request=action.request,
uow=uow,
)


class AutoAcceptComponent(RequestActionComponent):
@contextlib.contextmanager
def apply(self, identity, request_type, action, topic, uow, *args, **kwargs):
yield
if action.request.status != "submitted":
return
receiver_ref = action.request.receiver # this is <x>proxy, not dict
if not receiver_ref.reference_dict.get("auto_approve"):
return

action_obj = RequestActions.get_action(action.request, "accept")
if not action_obj.can_execute():
raise CannotExecuteActionError("accept")
action_obj.execute(identity, uow, *args, **kwargs)
14 changes: 5 additions & 9 deletions oarepo_requests/actions/delete_topic.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
from invenio_requests.customizations import actions
from oarepo_runtime.datastreams.utils import get_record_service_for_record

from ..utils import get_matching_service_for_record
from .generic import OARepoAcceptAction


class DeleteTopicAcceptAction(actions.AcceptAction):
log_event = True
class DeleteTopicAcceptAction(OARepoAcceptAction):

def execute(self, identity, uow, *args, **kwargs):
topic = self.request.topic.resolve()
topic_service = get_matching_service_for_record(topic)
def apply(self, identity, request_type, topic, uow, *args, **kwargs):
topic_service = get_record_service_for_record(topic)
if not topic_service:
raise KeyError(f"topic {topic} service not found")
# uow.register(RecordDeleteOp(topic, topic_service.indexer, index_refresh=True))
topic_service.delete(identity, topic["id"], uow=uow, *args, **kwargs)
super().execute(identity, uow)
17 changes: 7 additions & 10 deletions oarepo_requests/actions/edit_topic.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
from invenio_requests.customizations import actions
from oarepo_runtime.datastreams.utils import get_record_service_for_record

from ..utils import get_matching_service_for_record
from .generic import OARepoAcceptAction


class EditTopicAcceptAction(actions.AcceptAction):
log_event = True

def execute(self, identity, uow):
topic = self.request.topic.resolve()
topic_service = get_matching_service_for_record(topic)
class EditTopicAcceptAction(OARepoAcceptAction):
def apply(self, identity, request_type, topic, uow, *args, **kwargs):
topic_service = get_record_service_for_record(topic)
if not topic_service:
raise KeyError(f"topic {topic} service not found")
topic_service.edit(identity, topic["id"], uow=uow)
super().execute(identity, uow)
edit_ret = topic_service.edit(identity, topic["id"], uow=uow)
return edit_ret
56 changes: 46 additions & 10 deletions oarepo_requests/actions/generic.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,50 @@
from functools import cached_property

from invenio_requests.customizations import actions
from invenio_requests.customizations.actions import RequestActions
from invenio_requests.errors import CannotExecuteActionError

from oarepo_requests.proxies import current_oarepo_requests


class OARepoGenericActionMixin:
def apply(self, identity, request_type, topic, uow, *args, **kwargs):
pass

def _execute_with_components(
self, components, identity, request_type, topic, uow, *args, **kwargs
):
if not components:
self.apply(identity, request_type, topic, uow, *args, **kwargs)
super().execute(identity, uow, *args, **kwargs)
else:
with components[0].apply(
identity, request_type, self, topic, uow, *args, **kwargs
):
self._execute_with_components(
components[1:], identity, request_type, topic, uow, *args, **kwargs
)

@cached_property
def components(self):
return [
component_cls()
for component_cls in current_oarepo_requests.action_components(self)
]

def execute(self, identity, uow, *args, **kwargs):
request_type = self.request.type
topic = self.request.topic.resolve()
self._execute_with_components(
self.components, identity, request_type, topic, uow, *args, **kwargs
)


class OARepoSubmitAction(OARepoGenericActionMixin, actions.SubmitAction):
""""""


class OARepoDeclineAction(OARepoGenericActionMixin, actions.DeclineAction):
""""""

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)
class OARepoAcceptAction(OARepoGenericActionMixin, actions.AcceptAction):
""""""
29 changes: 11 additions & 18 deletions oarepo_requests/actions/publish_draft.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
from invenio_requests.customizations import actions
from oarepo_runtime.datastreams.utils import get_record_service_for_record

from ..utils import get_matching_service_for_record
from .generic import OARepoAcceptAction


def publish_draft(draft, identity, uow, *args, **kwargs):
topic_service = get_matching_service_for_record(draft)
if not topic_service:
raise KeyError(f"topic {draft} service not found")
id_ = draft["id"]
return topic_service.publish(identity, id_, uow=uow, expand=False, *args, **kwargs)


class PublishDraftAcceptAction(actions.AcceptAction):
log_event = True

def execute(self, identity, uow, *args, **kwargs):
topic = self.request.topic.resolve()
record = publish_draft(topic, identity, uow, *args, **kwargs)
super().execute(identity, uow, *args, **kwargs)
return record._record
class PublishDraftAcceptAction(OARepoAcceptAction):
def apply(self, identity, request_type, topic, uow, *args, **kwargs):
topic_service = get_record_service_for_record(topic)
if not topic_service:
raise KeyError(f"topic {topic} service not found")
id_ = topic["id"]
return topic_service.publish(
identity, id_, uow=uow, expand=False, *args, **kwargs
)._record
39 changes: 31 additions & 8 deletions oarepo_requests/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from invenio_users_resources.entity_resolvers import GroupResolver, UserResolver

from oarepo_requests.actions.components import (
AutoAcceptComponent,
RequestIdentityComponent,
WorkflowTransitionComponent,
)
from oarepo_requests.resolvers.ui import (
FallbackEntityReferenceUIResolver,
GroupEntityReferenceUIResolver,
Expand All @@ -15,14 +18,10 @@
DeletePublishedRecordRequestType(),
EditPublishedRecordRequestType(),
PublishDraftRequestType(),
# StatusChangingPublishDraftRequestType(),
]

REQUESTS_ALLOWED_RECEIVERS = ["user", "group"]

REQUESTS_ENTITY_RESOLVERS = [
UserResolver(),
GroupResolver(),
]
REQUESTS_ALLOWED_RECEIVERS = ["user", "group", "auto_approve"]

ENTITY_REFERENCE_UI_RESOLVERS = {
"user": UserEntityReferenceUIResolver("user"),
Expand All @@ -31,3 +30,27 @@
}

REQUESTS_UI_SERIALIZATION_REFERENCED_FIELDS = ["created_by", "receiver", "topic"]

REQUESTS_ACTION_COMPONENTS = {
"accepted": [
WorkflowTransitionComponent,
RequestIdentityComponent,
],
"submitted": [
AutoAcceptComponent,
WorkflowTransitionComponent,
RequestIdentityComponent,
],
"declined": [
WorkflowTransitionComponent,
RequestIdentityComponent,
],
"cancelled": [
WorkflowTransitionComponent,
RequestIdentityComponent,
],
"expired": [
WorkflowTransitionComponent,
RequestIdentityComponent,
],
}
34 changes: 15 additions & 19 deletions oarepo_requests/ext.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from functools import cached_property

from invenio_base.utils import obj_or_import_string
from invenio_requests.proxies import current_events_service

Expand Down Expand Up @@ -34,28 +32,19 @@ def entity_reference_ui_resolvers(self):
def ui_serialization_referenced_fields(self):
return self.app.config["REQUESTS_UI_SERIALIZATION_REFERENCED_FIELDS"]

def default_request_receiver(self, identity, request_type, topic, creator, data):
def default_request_receiver(self, identity, request_type, record, creator, data):
# TODO: if the topic is one of the workflow topics, use the workflow to determine the receiver
# otherwise use the default receiver
return obj_or_import_string(
self.app.config["OAREPO_REQUESTS_DEFAULT_RECEIVER"]
)(
identity=identity,
request_type=request_type,
topic=topic,
record=record,
creator=creator,
data=data,
)

@cached_property
def allowed_topic_ref_types(self):
entity_resolvers = self.app.config.get("REQUESTS_ENTITY_RESOLVERS", [])
return {x.type_key for x in entity_resolvers}

@cached_property
def requests_entity_resolvers(self):
return self.app.config.get("REQUESTS_ENTITY_RESOLVERS", [])

@property
def allowed_receiver_ref_types(self):
return self.app.config.get("REQUESTS_ALLOWED_RECEIVERS", [])
Expand Down Expand Up @@ -86,20 +75,27 @@ def init_resources(self, app):
config=OARepoRequestsCommentsResourceConfig.build(app),
)

from invenio_requests.customizations.actions import RequestAction

def action_components(self, action: RequestAction):
from . import config

components = config.REQUESTS_ACTION_COMPONENTS
if callable(components):
return components(action)
return [
obj_or_import_string(component)
for component in components[action.status_to]
]

def init_config(self, app):
"""Initialize configuration."""

from . import config

app.config.setdefault("REQUESTS_REGISTERED_TYPES", []).extend(
config.REQUESTS_REGISTERED_TYPES
)
app.config.setdefault("REQUESTS_ALLOWED_RECEIVERS", []).extend(
config.REQUESTS_ALLOWED_RECEIVERS
)
app.config.setdefault("REQUESTS_ENTITY_RESOLVERS", []).extend(
config.REQUESTS_ENTITY_RESOLVERS
)
app.config.setdefault("ENTITY_REFERENCE_UI_RESOLVERS", {}).update(
config.ENTITY_REFERENCE_UI_RESOLVERS
)
Expand Down
Loading

0 comments on commit 521b2b2

Please sign in to comment.