Skip to main content

github_bot_sdk/webhook/
handler.rs

1//! Webhook handler trait and types for application-provided processing logic.
2//!
3//! This module defines the interface that applications implement to process
4//! GitHub webhook events. Handlers receive normalized EventEnvelope instances
5//! and can perform async processing without blocking webhook HTTP responses.
6//!
7//! # Fire-and-Forget Pattern
8//!
9//! Handlers execute asynchronously after the HTTP response is sent to GitHub.
10//! This ensures GitHub receives a response within the 10-second timeout while
11//! allowing handlers to perform longer-running operations.
12//!
13//! # Examples
14//!
15//! ```rust,no_run
16//! use github_bot_sdk::webhook::WebhookHandler;
17//! use github_bot_sdk::events::EventEnvelope;
18//! use async_trait::async_trait;
19//!
20//! struct MyHandler;
21//!
22//! #[async_trait]
23//! impl WebhookHandler for MyHandler {
24//!     async fn handle_event(&self, envelope: &EventEnvelope) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
25//!         println!("Processing event: {}", envelope.event_id);
26//!         // Perform async processing here
27//!         Ok(())
28//!     }
29//! }
30//! ```
31
32use crate::events::EventEnvelope;
33use async_trait::async_trait;
34use std::error::Error;
35
36/// Application-provided webhook event handler.
37///
38/// Implementations of this trait define custom processing logic for GitHub
39/// webhook events. Handlers are invoked asynchronously after the HTTP response
40/// is sent, allowing long-running operations without blocking GitHub's webhook
41/// delivery.
42///
43/// # Error Handling
44///
45/// Handler errors are logged but do not affect the HTTP response to GitHub.
46/// Failed handler executions should implement their own retry/recovery logic
47/// if needed.
48///
49/// # Concurrency
50///
51/// Multiple handlers can be registered and will execute concurrently for each
52/// webhook event. Handlers must be `Send + Sync` to support concurrent execution.
53///
54/// # Examples
55///
56/// ```rust,no_run
57/// use github_bot_sdk::webhook::WebhookHandler;
58/// use github_bot_sdk::events::EventEnvelope;
59/// use async_trait::async_trait;
60///
61/// struct PullRequestHandler;
62///
63/// #[async_trait]
64/// impl WebhookHandler for PullRequestHandler {
65///     async fn handle_event(&self, envelope: &EventEnvelope) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
66///         if envelope.event_type == "pull_request" {
67///             println!("Processing PR event for {}", envelope.repository.full_name);
68///             // Add PR processing logic
69///         }
70///         Ok(())
71///     }
72/// }
73/// ```
74#[async_trait]
75pub trait WebhookHandler: Send + Sync {
76    /// Handle a webhook event asynchronously.
77    ///
78    /// This method is called after the HTTP response has been sent to GitHub.
79    /// It should process the event and return a result indicating success or failure.
80    ///
81    /// # Arguments
82    ///
83    /// * `envelope` - The normalized event envelope containing event data and metadata
84    ///
85    /// # Returns
86    ///
87    /// * `Ok(())` - Event processed successfully
88    /// * `Err(e)` - Event processing failed with error details
89    ///
90    /// # Examples
91    ///
92    /// ```rust,no_run
93    /// # use github_bot_sdk::webhook::WebhookHandler;
94    /// # use github_bot_sdk::events::EventEnvelope;
95    /// # use async_trait::async_trait;
96    /// # struct MyHandler;
97    /// # #[async_trait]
98    /// # impl WebhookHandler for MyHandler {
99    /// async fn handle_event(&self, envelope: &EventEnvelope) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
100    ///     match envelope.event_type.as_str() {
101    ///         "pull_request" => {
102    ///             // Handle PR events
103    ///             println!("PR event: {}", envelope.event_id);
104    ///         }
105    ///         "issues" => {
106    ///             // Handle issue events
107    ///             println!("Issue event: {}", envelope.event_id);
108    ///         }
109    ///         _ => {
110    ///             // Ignore other event types
111    ///         }
112    ///     }
113    ///     Ok(())
114    /// }
115    /// # }
116    /// ```
117    async fn handle_event(
118        &self,
119        envelope: &EventEnvelope,
120    ) -> Result<(), Box<dyn Error + Send + Sync>>;
121}
122
123#[cfg(test)]
124#[path = "handler_tests.rs"]
125mod tests;