use crate::types::{PushNotificationConfig, Task};
use tracing::{debug, warn};
#[derive(Clone)]
pub struct PushDispatcher {
client: reqwest::Client,
}
impl Default for PushDispatcher {
fn default() -> Self {
Self {
client: reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.expect("default reqwest client must build"),
}
}
}
impl PushDispatcher {
pub fn new(client: reqwest::Client) -> Self {
Self { client }
}
pub fn deliver(&self, configs: Vec<PushNotificationConfig>, task: Task) {
if configs.is_empty() {
return;
}
let client = self.client.clone();
tokio::spawn(async move {
for cfg in configs {
let mut req = client.post(&cfg.url).json(&task);
if let Some(token) = &cfg.token {
req = req.bearer_auth(token);
}
match req.send().await {
Ok(resp) if resp.status().is_success() => {
debug!(url = %cfg.url, task = %task.id, "push delivered");
}
Ok(resp) => {
warn!(url = %cfg.url, status = %resp.status(), "push non-2xx");
}
Err(e) => {
warn!(url = %cfg.url, error = %e, "push delivery failed");
}
}
}
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bridge::task_with_status;
use crate::types::TaskState;
#[tokio::test]
async fn empty_config_list_is_noop() {
let d = PushDispatcher::default();
let task = task_with_status(
"t".into(),
"ctx".into(),
TaskState::Submitted,
vec![],
vec![],
);
d.deliver(vec![], task);
}
}