use std::time::Duration;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Endpoint {
pub id: EndpointId,
pub url: String,
pub timeout: Duration,
pub max_retries: u32,
pub max_concurrent: usize,
pub secret: Option<Vec<u8>>,
pub include_timestamp: bool,
pub signature_header: String,
pub timestamp_header: String,
pub max_rps: Option<u32>,
pub burst: Option<u32>,
pub tenant_id: Option<TenantId>,
pub retry_base_ms: Option<u64>,
pub retry_max_ms: Option<u64>,
pub retry_jitter_ms: Option<u64>,
}
impl Endpoint {
pub fn new(id: impl Into<String>, url: impl Into<String>) -> Self {
Self {
id: EndpointId(id.into()),
url: url.into(),
timeout: Duration::from_secs(5),
max_retries: 3,
max_concurrent: 10,
secret: None,
include_timestamp: true,
signature_header: "X-Webhook-Signature".to_string(),
timestamp_header: "X-Webhook-Timestamp".to_string(),
max_rps: None,
burst: None,
tenant_id: None,
retry_base_ms: None,
retry_max_ms: None,
retry_jitter_ms: None,
}
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn with_max_retries(mut self, max_retries: u32) -> Self {
self.max_retries = max_retries;
self
}
pub fn with_max_concurrent(mut self, max_concurrent: usize) -> Self {
self.max_concurrent = max_concurrent;
self
}
pub fn with_secret(mut self, secret: impl Into<Vec<u8>>) -> Self {
self.secret = Some(secret.into());
self
}
pub fn with_timestamped_signatures(mut self, include: bool) -> Self {
self.include_timestamp = include;
self
}
pub fn with_signature_header(mut self, header: impl Into<String>) -> Self {
self.signature_header = header.into();
self
}
pub fn with_timestamp_header(mut self, header: impl Into<String>) -> Self {
self.timestamp_header = header.into();
self
}
pub fn with_rate_limit(mut self, max_rps: u32, burst: u32) -> Self {
self.max_rps = Some(max_rps);
self.burst = Some(burst);
self
}
pub fn with_tenant_id(mut self, tenant_id: impl Into<String>) -> Self {
self.tenant_id = Some(TenantId(tenant_id.into()));
self
}
pub fn with_retry_policy(mut self, base_ms: u64, max_ms: u64, jitter_ms: u64) -> Self {
self.retry_base_ms = Some(base_ms);
self.retry_max_ms = Some(max_ms);
self.retry_jitter_ms = Some(jitter_ms);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Event {
pub id: EventId,
pub payload: Vec<u8>,
pub tenant_id: Option<TenantId>,
}
impl Event {
pub fn new(id: impl Into<String>, payload: impl Into<Vec<u8>>) -> Self {
Self {
id: EventId(id.into()),
payload: payload.into(),
tenant_id: None,
}
}
pub fn with_tenant_id(mut self, tenant_id: impl Into<String>) -> Self {
self.tenant_id = Some(TenantId(tenant_id.into()));
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct EndpointId(pub String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct EventId(pub String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TenantId(pub String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct IdempotencyKey {
pub event_id: EventId,
pub endpoint_id: EndpointId,
pub tenant_id: Option<TenantId>,
}
impl IdempotencyKey {
pub fn new(event_id: EventId, endpoint_id: EndpointId, tenant_id: Option<TenantId>) -> Self {
Self { event_id, endpoint_id, tenant_id }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DlqEntry {
pub key: IdempotencyKey,
pub payload: Vec<u8>,
pub endpoint_id: EndpointId,
pub failure: String,
pub created_at_secs: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum DeliveryStatus {
Queued,
Retrying,
Delivered,
Failed,
Dropped,
Dlq,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeliveryState {
pub status: DeliveryStatus,
pub attempts: u32,
pub last_error: Option<String>,
pub last_updated_secs: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum OverflowPolicy {
DropNewest,
Block,
DropOldest,
SpillToStorage,
}