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}, }, )