feat: add retires and errors on uncessessful attempts

This commit is contained in:
Jet Pham 2025-06-05 19:39:20 -07:00
parent 0c2e129bbd
commit cfc5f01221
No known key found for this signature in database
5 changed files with 136 additions and 28 deletions

View file

@ -13,12 +13,19 @@ async fn main() -> Result<()> {
tracing_subscriber::fmt::init();
const DEFAULT_GPIO_PIN: u8 = 17;
const DEFAULT_WEBHOOK_RETRIES: u32 = 3;
let gpio_pin = std::env::var("GPIO_PIN")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(DEFAULT_GPIO_PIN);
let webhook_notifier = webhook::WebhookNotifier::new()?;
let webhook_retries = std::env::var("WEBHOOK_RETRIES")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(DEFAULT_WEBHOOK_RETRIES);
let webhook_notifier = webhook::WebhookNotifier::new(webhook_retries)?;
let mut gpio_monitor = gpio::GpioMonitor::new(gpio_pin, Duration::from_millis(100))?;
let callback = move |event: gpio::CircuitEvent| {

View file

@ -3,6 +3,8 @@ use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::fs;
use tracing::{error, info};
use std::time::Duration;
use futures::future::join_all;
use crate::gpio::CircuitEvent;
@ -28,19 +30,75 @@ struct WebhookPayload {
pub struct WebhookNotifier {
client: Client,
endpoints: Vec<Endpoint>,
max_retries: u32,
}
impl WebhookNotifier {
pub fn new() -> Result<Self> {
pub fn new(max_retries: u32) -> Result<Self> {
let config = fs::read_to_string("endpoints.json")?;
let endpoints_config: EndpointsConfig = serde_json::from_str(&config)?;
Ok(Self {
client: Client::new(),
endpoints: endpoints_config.endpoints,
max_retries,
})
}
async fn send_webhook(&self, endpoint: &Endpoint, payload: &WebhookPayload) -> Result<()> {
match self.client
.post(&endpoint.url)
.json(payload)
.send()
.await
{
Ok(response) => {
if response.status().is_success() {
info!(
"Successfully sent webhook to {}: {}",
endpoint.description,
response.status()
);
Ok(())
} else {
error!(
"Webhook request to {} failed with status: {}",
endpoint.description,
response.status()
);
Err(anyhow::anyhow!("Failed with status: {}", response.status()))
}
}
Err(e) => {
error!(
"Failed to send webhook to {}: {}",
endpoint.description,
e
);
Err(anyhow::anyhow!("Request failed: {}", e))
}
}
}
async fn send_webhook_with_retries(&self, endpoint: &Endpoint, payload: &WebhookPayload) {
let mut attempts = 0;
while attempts < self.max_retries {
match self.send_webhook(endpoint, payload).await {
Ok(_) => break,
Err(e) => {
attempts += 1;
if attempts < self.max_retries {
tokio::time::sleep(Duration::from_secs(1)).await;
} else {
error!("Failed to send webhook to {} after {} attempts: {}",
endpoint.description, self.max_retries, e);
}
}
}
}
}
pub async fn notify_all(&self, event_type: &str, state: CircuitEvent) {
let payload = WebhookPayload {
event_type: event_type.to_string(),
@ -48,29 +106,13 @@ impl WebhookNotifier {
new_state: state.to_string(),
};
for endpoint in &self.endpoints {
info!("Sending webhook to {}: {}", endpoint.description, serde_json::to_string(&payload).unwrap());
match self.client
.post(&endpoint.url)
.json(&payload)
.send()
.await
{
Ok(response) => {
info!(
"Successfully sent webhook to {}: {}",
endpoint.description,
response.status()
);
}
Err(e) => {
error!(
"Failed to send webhook to {}: {}",
endpoint.description,
e
);
}
}
}
let webhook_futures: Vec<_> = self.endpoints.iter()
.map(|endpoint| {
info!("Sending webhook to {}: {}", endpoint.description, serde_json::to_string(&payload).unwrap());
self.send_webhook_with_retries(endpoint, &payload)
})
.collect();
join_all(webhook_futures).await;
}
}