Skip to main content

fraiseql_server/webhooks/
config.rs

1//! Webhook configuration structures.
2
3use std::collections::HashMap;
4
5use serde::Deserialize;
6
7/// Webhook endpoint configuration
8#[derive(Debug, Clone, Deserialize)]
9pub struct WebhookConfig {
10    /// Provider type (stripe, github, etc.) - inferred from key if not specified
11    pub provider: Option<String>,
12
13    /// Endpoint path (default: /webhooks/{name})
14    pub path: Option<String>,
15
16    /// Secret environment variable name
17    pub secret_env: String,
18
19    /// Signature scheme (for custom providers)
20    pub signature_scheme: Option<String>,
21
22    /// Custom signature header (for custom providers)
23    pub signature_header: Option<String>,
24
25    /// Timestamp header (for custom providers)
26    pub timestamp_header: Option<String>,
27
28    /// Timestamp tolerance in seconds
29    #[serde(default = "default_timestamp_tolerance")]
30    pub timestamp_tolerance: u64,
31
32    /// Enable idempotency checking
33    #[serde(default = "default_idempotent")]
34    pub idempotent: bool,
35
36    /// Event mappings
37    #[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/// Event handler configuration
50#[derive(Debug, Clone, Deserialize)]
51pub struct WebhookEventConfig {
52    /// Database function to call
53    pub function: String,
54
55    /// Field mapping from webhook payload to function parameters
56    #[serde(default)]
57    pub mapping: HashMap<String, String>,
58
59    /// Condition expression (optional)
60    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}