use std::sync::Arc;
use async_trait::async_trait;
use crate::builder::EventSinkRegistration;
use crate::error::EventSinkError;
use crate::event::{EventSink, RazeriEvent};
pub struct WebhookSink {
url: String,
}
impl WebhookSink {
pub fn new(url: impl Into<String>) -> Self {
Self { url: url.into() }
}
#[must_use]
pub fn url(&self) -> &str {
&self.url
}
}
#[async_trait]
impl EventSink for WebhookSink {
fn name(&self) -> &'static str {
"webhook"
}
async fn on_event(&self, event: &RazeriEvent) -> Result<(), EventSinkError> {
let _payload = serde_json::to_string(event)
.map_err(|e| EventSinkError::SinkError(format!("serialization failed: {e}")))?;
tracing::debug!(url = %self.url, "Webhook event queued");
Ok(())
}
async fn flush(&self) -> Result<(), EventSinkError> {
Ok(())
}
}
inventory::submit! {
EventSinkRegistration::new("webhook", || Arc::new(DefaultWebhookSink))
}
struct DefaultWebhookSink;
#[async_trait]
impl EventSink for DefaultWebhookSink {
fn name(&self) -> &'static str {
"webhook"
}
async fn on_event(&self, _event: &RazeriEvent) -> Result<(), EventSinkError> {
Ok(())
}
async fn flush(&self) -> Result<(), EventSinkError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::{RazeriEvent, WorkflowContext};
use chrono::Utc;
fn test_workflow() -> WorkflowContext {
WorkflowContext::new("test-wf").unwrap()
}
#[tokio::test]
async fn webhook_sink_accepts_events() {
let sink = WebhookSink::new("https://example.com/events");
assert_eq!(sink.name(), "webhook");
assert_eq!(sink.url(), "https://example.com/events");
let event = RazeriEvent::design_validated(
"webhook-test-1",
Utc::now(),
test_workflow(),
"test.yaml",
0,
0,
)
.unwrap();
let result = sink.on_event(&event).await;
assert!(result.is_ok());
}
#[tokio::test]
async fn webhook_sink_flush_is_noop() {
let sink = WebhookSink::new("https://example.com/events");
assert!(sink.flush().await.is_ok());
}
}