Fence Mail provider and mailbox recovery effects
This commit is contained in:
@@ -30,6 +30,10 @@ from govoplan_mail.backend.server_hierarchy import (
|
||||
select_mail_transport,
|
||||
)
|
||||
from govoplan_mail.backend.runtime import configure_runtime
|
||||
from govoplan_mail.backend.recovery import (
|
||||
MailRecoveryError,
|
||||
begin_provider_effect_recovery,
|
||||
)
|
||||
from govoplan_mail.backend.sending.imap import (
|
||||
ImapAppendError,
|
||||
ImapConfigurationError,
|
||||
@@ -288,6 +292,9 @@ def send_campaign_email_bytes(
|
||||
expected_smtp_transport_revision: str,
|
||||
smtp_server_id: str | None = None,
|
||||
smtp_credential_id: str | None = None,
|
||||
recovery_effect_id: str | None = None,
|
||||
recovery_resource_type: str | None = None,
|
||||
recovery_resource_id: str | None = None,
|
||||
) -> CampaignSmtpDeliveryResult:
|
||||
selection = _selection_payload(
|
||||
profile_id=profile_id,
|
||||
@@ -362,6 +369,25 @@ def send_campaign_email_bytes(
|
||||
)
|
||||
except MailProfileError:
|
||||
raise MailProfileError("Mail delivery is blocked by the effective Mail policy.") from None
|
||||
try:
|
||||
recovery = begin_provider_effect_recovery(
|
||||
kind="smtp-delivery",
|
||||
effect_id=recovery_effect_id,
|
||||
tenant_id=tenant_id,
|
||||
profile_id=profile_id,
|
||||
message_bytes=message_bytes,
|
||||
expected_transport_revision=expected_smtp_transport_revision,
|
||||
recipient_count=len(envelope_recipients),
|
||||
resource_type=recovery_resource_type,
|
||||
resource_id=recovery_resource_id,
|
||||
)
|
||||
except MailRecoveryError as exc:
|
||||
raise SmtpConfigurationError(str(exc)) from None
|
||||
if recovery is not None and recovery.replayed:
|
||||
raise SmtpSendError(
|
||||
"The matching SMTP effect already succeeded; reconcile caller state without resending.",
|
||||
outcome_unknown=True,
|
||||
)
|
||||
try:
|
||||
result = send_email_bytes(
|
||||
message_bytes,
|
||||
@@ -370,18 +396,43 @@ def send_campaign_email_bytes(
|
||||
envelope_recipients=envelope_recipients,
|
||||
)
|
||||
except SmtpSendError as exc:
|
||||
raise _sanitized_smtp_error(exc) from None
|
||||
except SmtpConfigurationError:
|
||||
sanitized = _sanitized_smtp_error(exc)
|
||||
if recovery is not None:
|
||||
if sanitized.outcome_unknown:
|
||||
recovery.unknown(code="smtp_outcome_unknown", summary=str(sanitized))
|
||||
else:
|
||||
recovery.reject(code="smtp_rejected", summary=str(sanitized))
|
||||
raise sanitized from None
|
||||
except SmtpConfigurationError as exc:
|
||||
if recovery is not None:
|
||||
recovery.reject(code="smtp_configuration", summary=str(exc))
|
||||
raise SmtpConfigurationError("The selected Mail profile's SMTP configuration is unusable.") from None
|
||||
except Exception:
|
||||
if recovery is not None:
|
||||
recovery.unknown(
|
||||
code="unexpected_provider_error",
|
||||
summary="SMTP outcome is unknown after an unexpected provider failure",
|
||||
)
|
||||
raise SmtpSendError(
|
||||
"Mail delivery outcome is unknown after the provider effect started.",
|
||||
outcome_unknown=True,
|
||||
) from None
|
||||
return CampaignSmtpDeliveryResult(
|
||||
sanitized_result = CampaignSmtpDeliveryResult(
|
||||
envelope_recipients=list(result.envelope_recipients),
|
||||
refused_recipients=_sanitized_refusals(result.refused_recipients),
|
||||
)
|
||||
if recovery is not None:
|
||||
try:
|
||||
recovery.succeed_smtp(
|
||||
accepted_count=sanitized_result.accepted_count,
|
||||
refused_recipients=sanitized_result.refused_recipients,
|
||||
)
|
||||
except Exception:
|
||||
raise SmtpSendError(
|
||||
"SMTP returned an outcome, but durable recovery evidence could not be finalized.",
|
||||
outcome_unknown=True,
|
||||
) from None
|
||||
return sanitized_result
|
||||
|
||||
|
||||
def append_campaign_message_to_sent(
|
||||
@@ -398,6 +449,9 @@ def append_campaign_message_to_sent(
|
||||
smtp_credential_id: str | None = None,
|
||||
imap_server_id: str | None = None,
|
||||
imap_credential_id: str | None = None,
|
||||
recovery_effect_id: str | None = None,
|
||||
recovery_resource_type: str | None = None,
|
||||
recovery_resource_id: str | None = None,
|
||||
) -> CampaignImapAppendResult:
|
||||
selection = _selection_payload(
|
||||
profile_id=profile_id,
|
||||
@@ -487,17 +541,57 @@ def append_campaign_message_to_sent(
|
||||
)
|
||||
except MailProfileError:
|
||||
raise MailProfileError("Appending to Sent is blocked by the effective Mail policy.") from None
|
||||
try:
|
||||
recovery = begin_provider_effect_recovery(
|
||||
kind="imap-append",
|
||||
effect_id=recovery_effect_id,
|
||||
tenant_id=tenant_id,
|
||||
profile_id=profile_id,
|
||||
message_bytes=message_bytes,
|
||||
expected_transport_revision=expected_imap_transport_revision,
|
||||
folder=folder,
|
||||
resource_type=recovery_resource_type,
|
||||
resource_id=recovery_resource_id,
|
||||
)
|
||||
except MailRecoveryError as exc:
|
||||
raise ImapConfigurationError(str(exc)) from None
|
||||
if recovery is not None and recovery.replayed:
|
||||
raise ImapAppendError(
|
||||
"The matching IMAP append already succeeded; reconcile caller state without appending again.",
|
||||
outcome_unknown=True,
|
||||
)
|
||||
try:
|
||||
result = append_message_to_sent(message_bytes, imap_config=imap, folder=folder)
|
||||
except ImapAppendError as exc:
|
||||
raise _sanitized_imap_error(exc) from None
|
||||
except ImapConfigurationError:
|
||||
sanitized = _sanitized_imap_error(exc)
|
||||
if recovery is not None:
|
||||
if sanitized.outcome_unknown:
|
||||
recovery.unknown(code="imap_outcome_unknown", summary=str(sanitized))
|
||||
else:
|
||||
recovery.reject(code="imap_rejected", summary=str(sanitized))
|
||||
raise sanitized from None
|
||||
except ImapConfigurationError as exc:
|
||||
if recovery is not None:
|
||||
recovery.reject(code="imap_configuration", summary=str(exc))
|
||||
raise ImapConfigurationError("The selected Mail profile's IMAP configuration is unusable.") from None
|
||||
except Exception:
|
||||
if recovery is not None:
|
||||
recovery.unknown(
|
||||
code="unexpected_provider_error",
|
||||
summary="IMAP APPEND outcome is unknown after an unexpected provider failure",
|
||||
)
|
||||
raise ImapAppendError(
|
||||
"The Sent-folder append outcome is unknown; inspect the mailbox before retrying.",
|
||||
outcome_unknown=True,
|
||||
) from None
|
||||
if recovery is not None:
|
||||
try:
|
||||
recovery.succeed_imap(folder=result.folder)
|
||||
except Exception:
|
||||
raise ImapAppendError(
|
||||
"IMAP APPEND returned success, but durable recovery evidence could not be finalized.",
|
||||
outcome_unknown=True,
|
||||
) from None
|
||||
return CampaignImapAppendResult(folder=result.folder)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user