fraiseql_server/webhooks/
config.rs1use std::collections::HashMap;
4
5use serde::Deserialize;
6
7#[derive(Debug, Clone, Deserialize)]
9pub struct WebhookConfig {
10 pub provider: Option<String>,
12
13 pub path: Option<String>,
15
16 pub secret_env: String,
18
19 pub signature_scheme: Option<String>,
21
22 pub signature_header: Option<String>,
24
25 pub timestamp_header: Option<String>,
27
28 #[serde(default = "default_timestamp_tolerance")]
30 pub timestamp_tolerance: u64,
31
32 #[serde(default = "default_idempotent")]
34 pub idempotent: bool,
35
36 #[serde(default)]
38 pub events: HashMap<String, WebhookEventConfig>,
39}
40
41fn default_timestamp_tolerance() -> u64 {
42 300
43}
44
45fn default_idempotent() -> bool {
46 true
47}
48
49#[derive(Debug, Clone, Deserialize)]
51pub struct WebhookEventConfig {
52 pub function: String,
54
55 #[serde(default)]
57 pub mapping: HashMap<String, String>,
58
59 pub condition: Option<String>,
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn test_default_values() {
69 let json = r#"{
70 "secret_env": "WEBHOOK_SECRET",
71 "events": {}
72 }"#;
73
74 let config: WebhookConfig = serde_json::from_str(json).unwrap();
75 assert_eq!(config.timestamp_tolerance, 300);
76 assert!(config.idempotent);
77 }
78
79 #[test]
80 fn test_custom_values() {
81 let json = r#"{
82 "provider": "stripe",
83 "secret_env": "STRIPE_SECRET",
84 "timestamp_tolerance": 600,
85 "idempotent": false,
86 "events": {
87 "payment_intent.succeeded": {
88 "function": "handle_payment",
89 "mapping": {
90 "payment_id": "data.object.id"
91 }
92 }
93 }
94 }"#;
95
96 let config: WebhookConfig = serde_json::from_str(json).unwrap();
97 assert_eq!(config.provider, Some("stripe".to_string()));
98 assert_eq!(config.timestamp_tolerance, 600);
99 assert!(!config.idempotent);
100 assert_eq!(config.events.len(), 1);
101 }
102}