Model/applications/abri/dispatch.py
Daniel Roth 1c5a43df56 One message dispatches the fired Abri flows in a fixed, retry-safe order 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 10:09:20 +00:00

59 lines
2.1 KiB
Python

"""Dispatches the flows named in a trigger message through the orchestrator.
The scraper decides which flows fire; this is a dumb dispatcher. Flows run
in a fixed, retry-safe order — amend, then log, then tenant sync — so that
redelivery re-runs idempotent flows before non-idempotent ones: amend is
naturally idempotent, log is guarded by the job-number check, and tenant
contact creation (not idempotent) runs last so its failure can never cause
a duplicate job log, and a log failure can never duplicate contacts.
"""
from typing import Dict, Protocol
from applications.abri.abri_trigger_request import AbriTriggerRequest
from domain.abri.models import AbriRequestRejected, PlaceRef
from orchestration.abri_orchestrator import (
AmendJobOrchestrationResult,
AppointmentChange,
ConfirmedSurveyBooking,
DealDatabaseGateway,
LogJobOrchestrationResult,
TenantDataSyncResult,
)
class AbriFlows(Protocol):
"""The per-flow surface of the AbriOrchestrator that dispatch drives."""
def amend_job(self, change: AppointmentChange) -> AmendJobOrchestrationResult: ...
def log_job(self, booking: ConfirmedSurveyBooking) -> LogJobOrchestrationResult: ...
def sync_tenant_data(
self, place_ref: PlaceRef, deal_id: str
) -> TenantDataSyncResult: ...
class AbriFlowRejectedError(Exception):
"""An OpenHousing rejection, surfaced as a failure so the task fails.
Retrying a permanent rejection is wasteful but harmless — every flow is
safe to re-run — and redelivery eventually parks the message in the
dead-letter queue, which is the ops inbox.
"""
def __init__(self, flow: str, rejection: AbriRequestRejected) -> None:
self.flow = flow
self.rejection = rejection
super().__init__(
f"{flow} rejected by OpenHousing: {rejection.code}: {rejection.message}"
)
def dispatch_abri_flows(
request: AbriTriggerRequest,
flows: AbriFlows,
deal_database: DealDatabaseGateway,
) -> Dict[str, str]:
"""Run the fired flows in the fixed order; returns a PII-free summary."""
raise NotImplementedError