Skip to content

Commit 8c40f0f

Browse files
harlequinmilesj
authored andcommitted
new: Add webhook acknowledge setting and global trace (#2019)
* 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 * fix tests, format and lint * fix last linting issue
1 parent f138307 commit 8c40f0f

8 files changed

Lines changed: 63 additions & 12 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/cli/tests/run_webhooks_test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ fn sandbox(uri: String) -> Sandbox {
77

88
workspace_config.notifier = Some(PartialNotifierConfig {
99
webhook_url: Some(format!("{uri}/webhook")),
10+
webhook_acknowledge: Some(false),
1011
});
1112

1213
let sandbox = create_sandbox_with_config(

crates/config/src/workspace/notifier_config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ 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+
#[setting(default = false)]
13+
pub webhook_acknowledge: bool,
1114
}
1215
);

crates/config/tests/workspace_config_test.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,8 @@ extensions:
918918
assert_eq!(
919919
config.notifier,
920920
NotifierConfig {
921-
webhook_url: Some("http://localhost".into())
921+
webhook_url: Some("http://localhost".into()),
922+
webhook_acknowledge: false
922923
}
923924
);
924925
assert_eq!(

crates/notifier/src/webhooks.rs

Lines changed: 25 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(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_default(),
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;
@@ -98,9 +114,11 @@ impl WebhooksNotifier {
98114
}
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.
101-
else {
102-
self.requests.push(tokio::spawn(async {
103-
let _ = notify_webhook(url, body).await;
117+
else if require_acknowledge {
118+
let _ = notify_webhook(url.clone(), body, true).await;
119+
} else {
120+
self.requests.push(tokio::spawn(async move {
121+
let _ = notify_webhook(url, body, false).await;
104122
}));
105123
}
106124

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)