Skip to content

Commit 88f527d

Browse files
committed
feat(webhooks): Add acknowledge setting and global trace ID
This commit implements webhook acknowledgment and global trace ID features: - Add webhookAcknowledge setting in workspace config - Support MOON_TRACE_ID environment variable - Update documentation - Add test cases Closes #1961
1 parent 7fd9006 commit 88f527d

6 files changed

Lines changed: 61 additions & 11 deletions

File tree

crates/action-pipeline/src/action_pipeline.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,14 +406,20 @@ impl ActionPipeline {
406406
// For security and privacy purposes, only send webhooks from a CI environment
407407
if is_ci() || is_test_env() {
408408
if let Some(webhook_url) = &self.app_context.workspace_config.notifier.webhook_url {
409+
let require_acknowledge = self
410+
.app_context
411+
.workspace_config
412+
.notifier
413+
.webhook_acknowledge;
414+
409415
debug!(
410416
url = webhook_url,
411417
"Subscribing webhook events ({} enabled)",
412418
color::property("notifier.webhookUrl"),
413419
);
414420

415421
self.emitter
416-
.subscribe(WebhooksSubscriber::new(webhook_url))
422+
.subscribe(WebhooksSubscriber::new(webhook_url, require_acknowledge))
417423
.await;
418424
}
419425
}

crates/action-pipeline/src/subscribers/webhooks_subscriber.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ pub struct WebhooksSubscriber {
88
}
99

1010
impl WebhooksSubscriber {
11-
pub fn new(url: &str) -> Self {
11+
pub fn new(url: &str, require_acknowledge: bool) -> Self {
1212
WebhooksSubscriber {
13-
notifier: WebhooksNotifier::new(url.to_owned()),
13+
notifier: WebhooksNotifier::new(url.to_owned(), require_acknowledge),
1414
}
1515
}
1616
}

crates/config/src/workspace/notifier_config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@ config_struct!(
88
/// A secure URL in which to send webhooks to.
99
#[setting(validate = validate::url_secure)]
1010
pub webhook_url: Option<String>,
11+
/// Whether webhook requests require acknowledgment (2xx response).
12+
pub webhook_acknowledge: bool,
1113
}
1214
);

crates/notifier/src/webhooks.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use moon_time::chrono::NaiveDateTime;
44
use moon_time::now_timestamp;
55
use serde::Serialize;
66
use starbase_utils::json;
7+
use std::env;
78
use tokio::task::JoinHandle;
89
use tracing::{debug, trace, warn};
910
use uuid::Uuid;
@@ -21,21 +22,30 @@ pub struct WebhookPayload<'data, T: Serialize> {
2122
pub type_of: &'data str,
2223

2324
pub uuid: &'data str,
25+
26+
pub trace: &'data str,
2427
}
2528

2629
pub async fn notify_webhook(
2730
url: String,
2831
body: String,
32+
require_acknowledge: bool,
2933
) -> Result<reqwest::Response, reqwest::Error> {
30-
reqwest::Client::new()
34+
let response = reqwest::Client::new()
3135
.post(url)
3236
.body(body)
3337
.header("Accept", "application/json")
3438
.header("Content-Type", "application/json")
3539
.header("Connection", "keep-alive")
3640
.header("Keep-Alive", "timeout=30, max=120")
3741
.send()
38-
.await
42+
.await?;
43+
44+
if require_acknowledge && !response.status().is_success() {
45+
return Err(reqwest::Error::from(response.error_for_status().unwrap_err()));
46+
}
47+
48+
Ok(response)
3949
}
4050

4151
pub struct WebhooksNotifier {
@@ -44,11 +54,13 @@ pub struct WebhooksNotifier {
4454
requests: Vec<JoinHandle<()>>,
4555
url: String,
4656
uuid: String,
57+
trace: String,
4758
verified: bool,
59+
require_acknowledge: bool,
4860
}
4961

5062
impl WebhooksNotifier {
51-
pub fn new(url: String) -> Self {
63+
pub fn new(url: String, require_acknowledge: bool) -> Self {
5264
debug!("Creating webhooks notifier for {}", color::url(&url));
5365

5466
WebhooksNotifier {
@@ -60,8 +72,10 @@ impl WebhooksNotifier {
6072
} else {
6173
Uuid::new_v4().to_string()
6274
},
75+
trace: env::var("MOON_TRACE_ID").unwrap_or("".into()),
6376
url,
6477
verified: false,
78+
require_acknowledge,
6579
}
6680
}
6781

@@ -78,15 +92,17 @@ impl WebhooksNotifier {
7892
event,
7993
type_of: name,
8094
uuid: &self.uuid,
95+
trace: &self.trace,
8196
};
8297
let body = json::format(&payload, false)?;
8398
let url = self.url.to_owned();
99+
let require_acknowledge = self.require_acknowledge.to_owned();
84100

85101
// For the first event, we want to ensure that the webhook URL is valid
86102
// by sending the request and checking for a failure. If failed,
87103
// we will disable subsequent requests from being called.
88104
if !self.verified {
89-
let response = notify_webhook(url, body).await;
105+
let response = notify_webhook(url, body, require_acknowledge).await;
90106

91107
if response.is_err() || !response.unwrap().status().is_success() {
92108
self.enabled = false;
@@ -99,9 +115,13 @@ impl WebhooksNotifier {
99115
// For every other event, we will make the request and ignore the result.
100116
// We will also avoid awaiting the request to not slow down the overall runner.
101117
else {
102-
self.requests.push(tokio::spawn(async {
103-
let _ = notify_webhook(url, body).await;
104-
}));
118+
if require_acknowledge {
119+
let _ = notify_webhook(url.clone(), body, true).await;
120+
} else {
121+
self.requests.push(tokio::spawn(async move {
122+
let _ = notify_webhook(url, body, false).await;
123+
}));
124+
}
105125
}
106126

107127
Ok(())

website/docs/config/workspace.mdx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,26 @@ notifier:
521521
webhookUrl: 'https://api.company.com/some/endpoint'
522522
```
523523

524+
### `acknowledge`
525+
526+
<HeadingApiLink to="/api/types/interface/NotifierConfig#acknowledge" />
527+
528+
When enabled, webhook notfier will wait for request result and validates the return code for 2xx.
529+
Defaults to `false`.
530+
531+
:::warning
532+
533+
Activating this setting will slow down your pipeline, because every webhook request will be evaluated!
534+
535+
:::
536+
537+
```yaml title=".moon/workspace.yml" {2}
538+
notifier:
539+
webhookUrl: 'https://api.company.com/some/endpoint'
540+
webhookAcknowledge: true
541+
```
542+
543+
524544
## `pipeline`
525545

526546
<HeadingApiLink to="/api/types/interface/WorkspaceConfig#pipeline" />

website/docs/guides/webhooks.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Every webhook event is posted with the following request body, known as a payloa
2424
- `createdAt` (`string`) - When the event was created, as a UTC timestamp in ISO 8601 (RFC 3339)
2525
format.
2626
- `uuid` (`string`) - A unique identifier for all webhooks in the current run batch.
27+
- `trace` (`string`) - A unique identifier for all webhooks in the overall run batch. Can be defined via `MOON_TRACE_ID` environment variable.
2728

2829
```json
2930
{
@@ -33,7 +34,8 @@ Every webhook event is posted with the following request body, known as a payloa
3334
// ...
3435
},
3536
"createdAt": "...",
36-
"uuid": "..."
37+
"uuid": "...",
38+
"trace": "..."
3739
}
3840
```
3941

0 commit comments

Comments
 (0)