road-runner-common 0.21.0

Shared Rust utilities for exchange ecosystem backend services.
Documentation
//! Standard notification-trigger stack for the internal cex-notification API
//! (`/admin/api/v1/events/trigger`, Novu-shaped).
//!
//! Every service that needs to notify a user goes through this module instead of
//! hand-rolling the HTTP POST, so the wire contract lives in one place. The call is
//! an internal cluster request over `http://` (mesh mTLS handles transport).
//!
//! # Two levels
//!
//! **[`Notifier`]** is what a service should reach for. It answers the two questions
//! every trigger site has to answer — *who is the subscriber, and in what language* —
//! by delegating to an [`AudienceResolver`], then fires the workflow. The service only
//! describes **what happened**, by implementing [`NotificationEvent`]:
//!
//! ```ignore
//! struct FiatWithdrawalRefunded { amount: String, currency: String }
//!
//! impl NotificationEvent for FiatWithdrawalRefunded {
//!     fn workflow(&self) -> &str { workflows::FIAT_WITHDRAWAL_FAILED_REFUNDED }
//!     fn payload(&self) -> serde_json::Value {
//!         serde_json::json!({ "amount": self.amount, "currency": self.currency })
//!     }
//! }
//!
//! // The ledger holds an account id, cex-auth holds a subject — same call either way.
//! notifier.notify_detached(RecipientRef::AccountId(42), FiatWithdrawalRefunded { .. });
//! ```
//!
//! Wiring one up is a single line in the service's container — see
//! [`Notifier::from_env`], which reads the three environment variables the stack needs
//! and returns `None` when this service is not set up to notify.
//!
//! That is the whole extension point: a new notification is a new `NotificationEvent`
//! implementation plus a constant in [`workflows`]. No new client, no new config, no
//! copy of the locale lookup.
//!
//! **[`NotificationClient`] + [`TriggerRequest`]** are the raw wire layer underneath.
//! Use them directly only when the subscriber and locale are already in hand and an
//! [`AudienceResolver`] would be a pointless round-trip — cex-auth's OTP path, where
//! the session already carries both.
//!
//! # Delivery contact is not this module's job
//!
//! A trigger carries `subscriberId` and `locale`, never email or phone. cex-notification
//! backfills the delivery contact from cex-account itself
//! (`SubscriberContactEnrichmentService`), which is what keeps member PII out of every
//! service that merely wants to send a message. [`Recipient::email`] /
//! [`Recipient::phone`] exist for the callers that already hold a verified address and
//! would otherwise force a redundant lookup.

pub mod admin_topics;
pub mod audience;
pub mod client;
pub mod event;
pub mod mask;
pub mod notifier;
pub mod trigger;
pub mod workflows;

#[cfg(feature = "notification-grpc")]
pub mod grpc;

pub use admin_topics::{AdminChannel, AdminTopics};
pub use audience::{
    normalize_locale, Audience, AudienceError, AudienceResolver, RecipientRef,
    StaticAudienceResolver, DEFAULT_LOCALE,
};
pub use client::{NotificationClient, NotificationError, TRIGGER_URL_ENV};
pub use event::NotificationEvent;
pub use mask::mask_iban;
pub use notifier::{NotifyError, Notifier};
pub use trigger::{Recipient, Severity, TopicRecipient, TriggerRequest, TriggerTarget};

#[cfg(feature = "notification-grpc")]
pub use grpc::{
    AccountAudienceResolver, ResolverInitError, ACCOUNT_GRPC_URL_ENV, INTERNAL_SECRET_ENV,
};