use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize)]
pub struct Webhook {
pub id: String,
pub url: String,
pub events: Vec<String>,
pub collections: Option<Vec<String>>,
pub description: String,
pub active: bool,
pub created_by: Option<String>,
pub created_at: String,
pub updated_at: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CreateWebhookResponse {
#[serde(flatten)]
pub webhook: Webhook,
pub secret: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct CreateWebhookBody {
pub url: String,
pub events: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub collections: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct UpdateWebhookBody {
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub events: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub collections: Option<Option<Vec<String>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub active: Option<bool>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct WebhookListResponse {
pub webhooks: Vec<Webhook>,
pub total: u32,
}
#[derive(Debug, Clone, Deserialize)]
pub struct WebhookDelivery {
pub id: String,
pub webhook_id: String,
pub event: String,
pub payload: serde_json::Value,
pub status: String,
pub attempts: u32,
pub last_status_code: Option<u16>,
pub last_error: Option<String>,
pub created_at: String,
pub completed_at: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct WebhookDeliveryListResponse {
pub deliveries: Vec<WebhookDelivery>,
pub total: u32,
}
#[derive(Debug, Clone, Deserialize)]
pub struct WebhookTestResponse {
pub status: String,
pub status_code: Option<u16>,
pub error: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deserialize_webhook() {
let json = r#"{"id":"wh-1","url":"https://example.com/hook","events":["document.ready"],"collections":null,"description":"test","active":true,"created_by":null,"created_at":"","updated_at":""}"#;
let wh: Webhook = serde_json::from_str(json).unwrap();
assert_eq!(wh.id, "wh-1");
assert_eq!(wh.events, vec!["document.ready"]);
assert_eq!(wh.collections, None);
assert!(wh.active);
}
#[test]
fn test_deserialize_create_webhook_response_with_flatten() {
let json = r#"{"id":"wh-1","url":"https://example.com","events":["document.ready"],"collections":null,"description":"","active":true,"created_by":null,"created_at":"","updated_at":"","secret":"whsec_abc123"}"#;
let resp: CreateWebhookResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.secret, "whsec_abc123");
assert_eq!(resp.webhook.id, "wh-1");
}
#[test]
fn test_deserialize_webhook_test_response() {
let json = r#"{"status":"ok","status_code":200,"error":null}"#;
let resp: WebhookTestResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.status_code, Some(200));
assert_eq!(resp.error, None);
}
#[test]
fn test_serialize_update_webhook_body_skips_none() {
let body = UpdateWebhookBody {
active: Some(false),
..Default::default()
};
let json = serde_json::to_value(&body).unwrap();
assert_eq!(json["active"], false);
assert!(json.get("url").is_none());
}
}