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;