circles/src/server/src/webhook/handlers/common.rs
2025-07-09 23:39:48 +02:00

119 lines
3.3 KiB
Rust

//! Common webhook handler utilities
use crate::webhook::types::{WebhookConfig, WebhookError};
use actix_web::http::header::HeaderMap;
/// Application state for webhook handling
#[derive(Clone)]
pub struct WebhookAppState {
pub config: WebhookConfig,
pub redis_url: String,
pub caller_id: String,
pub worker_id: String,
}
/// Create webhook application state
pub fn create_webhook_app_state(
config: WebhookConfig,
redis_url: String,
worker_id: String,
) -> WebhookAppState {
// For now, we'll use the worker_id as the caller_id for webhooks.
// This can be changed if a more specific caller_id is needed.
let caller_id = worker_id.clone();
WebhookAppState {
config,
redis_url,
caller_id,
worker_id,
}
}
/// Extract signature from request headers with the given header name
pub fn extract_signature_header(
headers: &HeaderMap,
header_name: &str,
) -> Result<String, WebhookError> {
match headers.get(header_name) {
Some(sig) => match sig.to_str() {
Ok(s) => Ok(s.to_string()),
Err(_) => Err(WebhookError::InvalidSignature(format!(
"Invalid {} header format", header_name
))),
},
None => Err(WebhookError::InvalidSignature(format!(
"Missing {} header", header_name
))),
}
}
/// Create a standard error response for webhook failures
pub fn create_error_response(error: &str, details: Option<String>) -> serde_json::Value {
let mut response = serde_json::json!({
"error": error
});
if let Some(details) = details {
response["details"] = serde_json::Value::String(details);
}
response
}
/// Create a standard success response for webhook processing
pub fn create_success_response(
message: &str,
additional_fields: Option<serde_json::Map<String, serde_json::Value>>,
) -> serde_json::Value {
let mut response = serde_json::json!({
"success": true,
"message": message
});
if let Some(fields) = additional_fields {
for (key, value) in fields {
response[key] = value;
}
}
response
}
#[cfg(test)]
mod tests {
use super::*;
use actix_web::http::header::HeaderName;
#[test]
fn test_extract_signature_header_success() {
let mut headers = HeaderMap::new();
headers.insert(HeaderName::from_static("test-signature"), "test_value".parse().unwrap());
let result = extract_signature_header(&headers, "test-signature");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "test_value");
}
#[test]
fn test_extract_signature_header_missing() {
let headers = HeaderMap::new();
let result = extract_signature_header(&headers, "missing-header");
assert!(result.is_err());
}
#[test]
fn test_create_error_response() {
let response = create_error_response("Test error", Some("Test details".to_string()));
assert_eq!(response["error"], "Test error");
assert_eq!(response["details"], "Test details");
}
#[test]
fn test_create_success_response() {
let response = create_success_response("Test success", None);
assert_eq!(response["success"], true);
assert_eq!(response["message"], "Test success");
}
}