feat: add durable mail delivery outbox
This commit is contained in:
@@ -20,6 +20,9 @@ from govoplan_mail.backend.schemas import (
|
||||
MailCredentialUpdateRequest,
|
||||
MailImapFolderListResponse,
|
||||
MailImapFolderResponse,
|
||||
MailDeliveryCommandResponse,
|
||||
MailDeliveryReconcileRequest,
|
||||
MailDeliveryResendRequest,
|
||||
MailMailboxBootstrapResponse,
|
||||
MailImapTestRequest,
|
||||
MailMailboxAttachmentResponse,
|
||||
@@ -79,6 +82,14 @@ from govoplan_mail.backend.mail_profiles import (
|
||||
)
|
||||
from govoplan_mail.backend.config import ImapConfig, SmtpConfig
|
||||
from govoplan_mail.backend.runtime import get_registry
|
||||
from govoplan_mail.backend.delivery_outbox import (
|
||||
MailDeliveryError,
|
||||
delivery_command_diagnostics,
|
||||
delivery_command_summary,
|
||||
get_delivery_command,
|
||||
reconcile_delivery_command,
|
||||
resend_delivery_command,
|
||||
)
|
||||
from govoplan_mail.backend.server_hierarchy import (
|
||||
MailHierarchyContext,
|
||||
MailServerHierarchyError,
|
||||
@@ -120,6 +131,114 @@ DEFAULT_MAILBOX_MESSAGE_LIMIT = 50
|
||||
CAPABILITY_ADDRESSES_LOOKUP = "addresses.lookup"
|
||||
|
||||
|
||||
@router.get(
|
||||
"/delivery-commands/{command_id}",
|
||||
response_model=MailDeliveryCommandResponse,
|
||||
)
|
||||
def get_mail_delivery_command(
|
||||
command_id: str,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
):
|
||||
"""Return the business-safe status of one tenant Mail command."""
|
||||
|
||||
_require_scope(principal, "mail:profile:read")
|
||||
try:
|
||||
command = get_delivery_command(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
command_id=command_id,
|
||||
)
|
||||
except MailDeliveryError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
return MailDeliveryCommandResponse(result=delivery_command_summary(command))
|
||||
|
||||
|
||||
@router.get(
|
||||
"/delivery-commands/{command_id}/diagnostics",
|
||||
response_model=MailDeliveryCommandResponse,
|
||||
)
|
||||
def get_mail_delivery_command_diagnostics(
|
||||
command_id: str,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
):
|
||||
"""Return bounded recipient and attempt evidence to diagnostic actors."""
|
||||
|
||||
_require_scope(principal, "mail:delivery:diagnostic")
|
||||
try:
|
||||
result = delivery_command_diagnostics(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
command_id=command_id,
|
||||
)
|
||||
except MailDeliveryError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
return MailDeliveryCommandResponse(result=result)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/delivery-commands/{command_id}/reconcile",
|
||||
response_model=MailDeliveryCommandResponse,
|
||||
)
|
||||
def reconcile_mail_delivery_command(
|
||||
command_id: str,
|
||||
payload: MailDeliveryReconcileRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
):
|
||||
_require_scope(principal, "mail:delivery:reconcile")
|
||||
try:
|
||||
result = reconcile_delivery_command(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
command_id=command_id,
|
||||
decision=payload.decision,
|
||||
evidence_reference=payload.evidence_reference,
|
||||
note=payload.note,
|
||||
user_id=principal.user.id,
|
||||
)
|
||||
except MailDeliveryError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
return MailDeliveryCommandResponse(result=result)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/delivery-commands/{command_id}/resend",
|
||||
response_model=MailDeliveryCommandResponse,
|
||||
)
|
||||
def resend_mail_delivery_command(
|
||||
command_id: str,
|
||||
payload: MailDeliveryResendRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
):
|
||||
_require_scope(principal, "mail:delivery:reconcile")
|
||||
try:
|
||||
result = resend_delivery_command(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
command_id=command_id,
|
||||
idempotency_key=payload.idempotency_key,
|
||||
user_id=principal.user.id,
|
||||
)
|
||||
except MailDeliveryError as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail=str(exc),
|
||||
) from exc
|
||||
return MailDeliveryCommandResponse(result=result)
|
||||
|
||||
|
||||
def _capability_payload(value: object) -> dict[str, Any]:
|
||||
if dataclasses.is_dataclass(value):
|
||||
return dataclasses.asdict(value)
|
||||
|
||||
Reference in New Issue
Block a user