road-runner-common 0.22.0

Shared Rust utilities for exchange ecosystem backend services.
Documentation
//! The HTTP transport to the notification center's trigger endpoint.
//!
//! Deliberately thin: one POST, one timeout, one error type. Everything about *who* to
//! notify and *what* to say lives above it in [`Notifier`](super::Notifier).

use std::time::Duration;

use super::trigger::TriggerRequest;

/// Environment variable naming the full trigger endpoint, e.g.
/// `http://cex-notification-api/admin/api/v1/events/trigger`.
/// Named here so all thirty services read the same key.
pub const TRIGGER_URL_ENV: &str = "NOTIFICATION_API_URL";

/// Default timeout for a single trigger call.
const TRIGGER_TIMEOUT: Duration = Duration::from_secs(10);

/// A failed notification trigger.
#[derive(Debug, thiserror::Error)]
pub enum NotificationError {
    #[error("notification center unreachable: {0}")]
    Request(#[from] reqwest::Error),
    #[error("notification center returned {0}")]
    Status(reqwest::StatusCode),
}

/// Reusable client bound to the internal cex-notification trigger endpoint.
///
/// Cheap to clone (the connection pool is shared) — build one per process and clone it,
/// rather than calling [`Self::new`] per message, which would build a fresh pool each
/// time.
#[derive(Clone)]
pub struct NotificationClient {
    http: reqwest::Client,
    trigger_url: String,
}

impl NotificationClient {
    /// Build a client for the given trigger URL (e.g. the value of
    /// [`TRIGGER_URL_ENV`]).
    pub fn new(trigger_url: impl Into<String>) -> Self {
        Self {
            http: reqwest::Client::new(),
            trigger_url: trigger_url.into(),
        }
    }

    /// Build a client reusing an existing `reqwest::Client` (shared connection pool).
    pub fn with_client(http: reqwest::Client, trigger_url: impl Into<String>) -> Self {
        Self {
            http,
            trigger_url: trigger_url.into(),
        }
    }

    /// Fire the workflow. Returns `Err` on transport failure or a non-2xx status.
    pub async fn trigger(&self, request: &TriggerRequest) -> Result<(), NotificationError> {
        let response = self
            .http
            .post(&self.trigger_url)
            .json(request)
            .timeout(TRIGGER_TIMEOUT)
            .send()
            .await?;
        if !response.status().is_success() {
            return Err(NotificationError::Status(response.status()));
        }
        Ok(())
    }
}