Extra requests for InvenioRDM ecosystem
Project description
OARepo Requests
Extensions to Invenio Requests providing workflow-based request handling for repository records.
Installation
pip install oarepo-requests
Overview
This library extends Invenio Requests to support workflow-based request handling for repository records. It provides:
- Extended request API with additional endpoints
- Pre-built request types for common curation workflows
- Integration with oarepo-workflows for state transitions
- Action components for extensible request processing
- UI serialization support for request entities
Features
Extended Request API
The library extends the standard /api/requests endpoint and adds record-specific request endpoints:
POST /api/requests- Create a request withrequest_typeandtopicin payloadPOST /api/requests/<type>/<topic>- Create a request via URL parametersGET /api/requests/applicable?topic=<reference>- List applicable request types for a topic
Built-in Request Types
Publish Requests
publish_draft - Submit draft for review and publication
- Validates topic is initial version draft
- Supports version field in payload
- Transitions:
submitted→published(accepted) ordraft(declined) - Auto-approval when user has curator permissions
publish_new_version - Publish new version of published record
- For records with existing published versions
- Inherits base publish request behavior
publish_changed_metadata - Publish metadata changes
- For publishing edited metadata of published records
Edit Requests
edit_published_record - Request to edit published record metadata
- Creates draft from published record
- Requires record to have no existing draft
- Auto-creates draft on acceptance
new_version - Request to create new version
- Creates new version draft from published record
- Supports
keep_filesparameter (yes/no) - Only applicable to published records without drafts
Delete Requests
delete_published_record - Request permanent deletion of published record
- Requires
removal_reasonand optionalnotein payload - Executes record deletion on acceptance
- Marked as dangerous operation
Request Actions
All request types support standard workflow actions:
- submit - Submit created request for approval (transitions to
submitted) - accept - Accept submitted request (executes request-specific logic)
- decline - Decline submitted request (typically returns to previous state)
- cancel - Cancel request before acceptance (transitions to
cancelled)
Custom action implementations:
PublishDraftAcceptAction- Publishes draft, validates no unresolved requestsDeletePublishedRecordAcceptAction- Executes record deletion with metadata preservationEditTopicAcceptAction- Creates editable draft from published recordNewVersionAcceptAction- Creates new version draft, optionally copies files
Action Components
The library uses a component architecture for extending action behavior:
class RequestActionComponent:
def submit(self, identity, action, uow, *args, **kwargs): ...
def accept(self, identity, action, uow, *args, **kwargs): ...
def decline(self, identity, action, uow, *args, **kwargs): ...
def cancel(self, identity, action, uow, *args, **kwargs): ...
Built-in components:
WorkflowTransitionComponent - Applies workflow state transitions based on action outcomes
- Reads transitions from workflow request configuration
- Sets record state according to
status_tomapping
AutoAcceptComponent - Auto-accepts requests with auto-approve receivers
- Must be last in component chain
- Executes accept action if receiver has
auto_approve: true
Configure components in invenio.cfg:
REQUESTS_ACTION_COMPONENTS = [
WorkflowTransitionComponent,
AutoAcceptComponent,
]
Workflow Integration
Request types integrate with oarepo-workflows through the receiver function:
OAREPO_REQUESTS_DEFAULT_RECEIVER = "oarepo_requests.receiver:default_workflow_receiver_function"
The receiver function:
- Retrieves workflow for the record
- Looks up request configuration in workflow
- Evaluates recipient generators to determine receiver
- Returns entity reference or
Nonefor auto-approval
Example workflow request configuration:
from oarepo_workflows import WorkflowRequest, WorkflowTransitions, AutoApprove
from oarepo_requests.services.permissions import IfRequestedBy
class MyWorkflowRequests(WorkflowRequestPolicy):
publish_request = WorkflowRequest(
requesters=[
IfInState("draft", then_=[RecordOwners(), CommunityRole("curator")])
],
recipients=[
IfRequestedBy(
CommunityRole("curator"),
then_=[AutoApprove()],
else_=[CommunityRole("curator")],
)
],
transitions=WorkflowTransitions(
submitted="submitted",
accepted="published",
declined="draft"
),
)
Permission Generators
RequestActive - Matches when request action is being executed
- Useful for granting temporary elevated permissions during request processing
- Example: Allow deletion only through request acceptance
class MyWorkflowPermissions(RequestBasedWorkflowPermissions):
can_delete = [..., RequestActive()]
IfRequestedBy - Conditional generator based on request creator
- Evaluates if creator has specified permissions
- Used in workflow recipient configuration
recipients=[
IfRequestedBy(
CommunityRole("curator"),
then_=[AutoApprove()],
else_=[CommunityRole("publisher")],
)
]
IfNoNewVersionDraft - Checks if record has no new version draft
IfNoEditDraft - Checks if record has no edit draft
Request Type Properties
Base properties inherited by all request types:
class OARepoRequestType(RequestType):
dangerous = False # Marks destructive operations
allowed_on_draft = True # Can be created on draft records
allowed_on_published = True # Can be created on published records
editable = None # Whether request can be edited before submission
receiver_can_be_none = False # Whether auto-approval is allowed
payload_schema = {...} # Marshmallow schema for request payload
Request types also implement:
is_applicable_to(identity, topic)- Check if request type can be used for a topiccan_create(identity, data, receiver, topic, creator)- Validate request creationstateful_name(identity, topic, request)- Dynamic name based on request statestateful_description(identity, topic, request)- Dynamic description based on state
UI Support
The library provides UI serialization for requests:
- Expands entity references (creator, receiver, topic)
- Resolves references using configured entity resolvers
- Includes stateful names and descriptions
- Provides form definitions for request creation
Configuration:
REQUESTS_UI_SERIALIZATION_REFERENCED_FIELDS = ["created_by", "receiver", "topic"]
Configuration
Core Settings
# Default receiver function for requests
OAREPO_REQUESTS_DEFAULT_RECEIVER = "oarepo_requests.receiver:default_workflow_receiver_function"
# Allowed receiver entity types
REQUESTS_ALLOWED_RECEIVERS = ["user", "group", "auto_approve"]
# Action components to execute on request actions
REQUESTS_ACTION_COMPONENTS = [
WorkflowTransitionComponent,
AutoAcceptComponent,
]
# Request types that trigger publication
PUBLISH_REQUEST_TYPES = ["publish_draft", "publish_new_version"]
# Workflow events configuration
DEFAULT_WORKFLOW_EVENTS = {
"comment": WorkflowEvent(...),
"log": WorkflowEvent(...),
}
Service Configuration
The library overrides Invenio's default request service and resource:
REQUESTS_SERVICE_CLASS = OARepoRequestsService
REQUESTS_SERVICE_CONFIG_CLASS = OARepoRequestsServiceConfig
REQUESTS_RESOURCE_CLASS = OARepoRequestsResource
REQUESTS_RESOURCE_CONFIG_CLASS = OARepoRequestsResourceConfig
Usage
Creating Requests
Via API:
# Create request with payload
curl -X POST /api/requests \
-H "Content-Type: application/json" \
-d '{
"request_type": "publish_draft",
"topic": {"type": "record", "value": "abc123"},
"payload": {"version": "1.0"}
}'
# Create request via URL
curl -X POST /api/requests/publish_draft/record:abc123 \
-H "Content-Type: application/json" \
-d '{"payload": {"version": "1.0"}}'
Via service:
from invenio_requests.proxies import current_requests_service
result = current_requests_service.create(
identity=identity,
data={"payload": {"version": "1.0"}},
request_type="publish_draft",
topic=record,
)
Executing Actions
from invenio_requests.proxies import current_requests_service
# Submit request
current_requests_service.execute_action(
identity, request_id, "submit"
)
# Accept request
current_requests_service.execute_action(
identity, request_id, "accept"
)
Custom Request Types
from oarepo_requests.types.generic import NonDuplicableOARepoRecordRequestType
from oarepo_requests.actions.generic import OARepoAcceptAction
class MyRequestType(NonDuplicableOARepoRecordRequestType):
type_id = "my_request"
name = _("My Request")
description = _("Description of my request")
payload_schema = {
"my_field": ma.fields.Str(required=True),
}
@classproperty
def available_actions(cls):
return {
**super().available_actions,
"accept": MyAcceptAction,
}
class MyAcceptAction(OARepoAcceptAction):
name = _("Accept")
def apply(self, identity, uow, *args, **kwargs):
# Custom acceptance logic
pass
Register via entry point:
[project.entry-points."invenio_requests.types"]
my_request = "myapp.requests:MyRequestType"
Custom Action Components
from oarepo_requests.actions.components import RequestActionComponent
class MyComponent(RequestActionComponent):
def accept(self, identity, action, uow, *args, **kwargs):
# Custom logic on accept
topic = action.topic
request = action.request
# ... perform operations
Register in invenio.cfg:
REQUESTS_ACTION_COMPONENTS = [
MyComponent,
WorkflowTransitionComponent,
AutoAcceptComponent,
]
Architecture
Request Lifecycle
- Creation - Request created via API or service with validation
- Submission - Request submitted for approval (optional if auto-approved)
- Processing - Receiver accepts or declines request
- Completion - Action logic executes, state transitions applied, components run
Component Execution Order
For each action execution:
- Action's
apply()method executes - Parent class action logic executes
- Each component's action method executes in configured order
Important: Components run after state changes, so they see updated request status.
Dependencies
oarepo-runtime>=2.0.0dev13oarepo-workflows>=2.0.0dev3oarepo[rdm]>=14.0.0oarepo-model>=0.1.0.dev5
Development
# Install with dev dependencies
pip install -e .[dev,tests]
# Run tests
pytest
License
Copyright (c) 2024-2025 CESNET z.s.p.o.
OARepo Requests is free software; you can redistribute it and/or modify it under the terms of the MIT License. See LICENSE file for more details.
Links
- Documentation: https://github.com/oarepo/oarepo-requests
- PyPI: https://pypi.org/project/oarepo-requests/
- Issues: https://github.com/oarepo/oarepo-requests/issues
- OARepo Project: https://github.com/oarepo
Acknowledgments
This project builds upon Invenio Framework and is developed as part of the OARepo ecosystem.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file oarepo_requests-4.0.0.tar.gz.
File metadata
- Download URL: oarepo_requests-4.0.0.tar.gz
- Upload date:
- Size: 71.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0540a6110d15768df287b33b226dcf610f3f35fd22480472ccd990a04cf987ec
|
|
| MD5 |
e440d6b3f0d9c6f5276357e5de17e083
|
|
| BLAKE2b-256 |
b1516ed184344d21956dc9c1ac4bcd31336976e454516d7595f73b2587f33a0a
|
File details
Details for the file oarepo_requests-4.0.0-py3-none-any.whl.
File metadata
- Download URL: oarepo_requests-4.0.0-py3-none-any.whl
- Upload date:
- Size: 124.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3b90843ae5b1bba710d5631dfc4cd1dfb59e53fc275912a3476da2a8ad8107b
|
|
| MD5 |
6b5de2b77df751d40c0310d6041f77cf
|
|
| BLAKE2b-256 |
0e7d2bef2a8016ff4049af0f2d1521621865c7ddbab26d986e96de37cd0bba2a
|