Source code for workflows.services.executors.approval
"""Approval step executor."""
from __future__ import annotations
from workflows.models import State, WorkflowInstance
from workflows.services.executors.factory import AbstractStepExecutor
from workflows.services.types import ExecutorResult
[docs]
class ApprovalExecutor(AbstractStepExecutor):
"""Approval step executor.
First encounter (no signal) → AWAITING (AwaitingApproval).
- On "reject" → REJECTED (terminal).
- On "approve":
• If this is the last Approval step → APPROVED.
• Otherwise → PASSED.
"""
[docs]
def do_execute(self, instance: WorkflowInstance, signal: str | None) -> ExecutorResult:
"""Execute the approval step and return the resulting state.
Args:
instance: Workflow instance being processed.
signal: External decision signal such as ``approve`` or ``reject``.
Returns:
ExecutorResult describing the new workflow state and step context.
"""
# First visit (no signal) → AWAITING
if signal is None and instance.state == State.RUNNING:
return ExecutorResult(
status=State.AWAITING,
context={
'type': 'Approval',
'status': 'AwaitingApproval',
'error': None,
'outputs': {'AwaitingApproval': True},
},
)
# Rejected → terminal
if signal == 'reject':
return ExecutorResult(
status=State.REJECTED,
context={
'type': 'Approval',
'status': 'rejected',
'error': None,
'outputs': {'decision': 'Rejected'},
},
)
# Approved
if signal == 'approve':
if instance.is_last_approval_step():
return ExecutorResult(
status=State.APPROVED,
context={
'type': 'Approval',
'status': 'approved',
'error': None,
'outputs': {'decision': 'Approved', 'last_approval': True},
},
)
return ExecutorResult(
status=State.PASSED,
context={
'type': 'Approval',
'status': 'passed',
'error': None,
'outputs': {'decision': 'Approved'},
},
)
# Any other case → still waiting
return ExecutorResult(
status=State.AWAITING,
context={
'type': 'Approval',
'status': 'AwaitingApproval',
'error': None,
'outputs': {'AwaitingApproval': True},
},
)