|
1 | 1 | use std::collections::HashMap; |
| 2 | +use std::env; |
2 | 3 | use std::sync::Arc; |
3 | 4 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; |
4 | 5 |
|
| 6 | +use engine_core::execution_options::WebhookOptions; |
5 | 7 | use hex; |
6 | 8 | use hmac::{Hmac, Mac}; |
7 | 9 | use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; |
8 | 10 | use serde::{Deserialize, Serialize}; |
9 | 11 | use twmq::error::TwmqError; |
10 | 12 | use twmq::hooks::TransactionContext; |
11 | 13 | use twmq::job::{BorrowedJob, JobError, JobResult, RequeuePosition, ToJobResult}; |
12 | | -use twmq::{DurableExecution, FailHookData, NackHookData, SuccessHookData, UserCancellable}; |
| 14 | +use twmq::{DurableExecution, FailHookData, NackHookData, Queue, SuccessHookData, UserCancellable}; |
| 15 | +use uuid::Uuid; |
| 16 | + |
| 17 | +use crate::webhook::envelope::{BareWebhookNotificationEnvelope, WebhookNotificationEnvelope}; |
13 | 18 |
|
14 | 19 | pub mod envelope; |
15 | 20 |
|
@@ -113,7 +118,10 @@ impl DurableExecution for WebhookJobHandler { |
113 | 118 | type JobData = WebhookJobPayload; |
114 | 119 |
|
115 | 120 | #[tracing::instrument(skip_all, fields(queue = "webhook", job_id = job.job.id))] |
116 | | - async fn process(&self, job: &BorrowedJob<Self::JobData>) -> JobResult<Self::Output, Self::ErrorData> { |
| 121 | + async fn process( |
| 122 | + &self, |
| 123 | + job: &BorrowedJob<Self::JobData>, |
| 124 | + ) -> JobResult<Self::Output, Self::ErrorData> { |
117 | 125 | let payload = &job.job.data; |
118 | 126 | let mut request_headers = HeaderMap::new(); |
119 | 127 |
|
@@ -423,3 +431,77 @@ impl DurableExecution for WebhookJobHandler { |
423 | 431 | ); |
424 | 432 | } |
425 | 433 | } |
| 434 | + |
| 435 | +pub fn queue_webhook_envelopes<T: Serialize + Clone>( |
| 436 | + envelope: BareWebhookNotificationEnvelope<T>, |
| 437 | + webhook_options: Vec<WebhookOptions>, |
| 438 | + tx: &mut TransactionContext<'_>, |
| 439 | + webhook_queue: Arc<Queue<WebhookJobHandler>>, |
| 440 | +) -> Result<(), TwmqError> { |
| 441 | + let now = chrono::Utc::now().timestamp().min(0) as u64; |
| 442 | + let serialised_webhook_envelopes = |
| 443 | + webhook_options |
| 444 | + .iter() |
| 445 | + .map(|webhook_option| { |
| 446 | + let webhook_notification_envelope = envelope |
| 447 | + .clone() |
| 448 | + .into_webhook_notification_envelope(now, webhook_option.url.clone()); |
| 449 | + let serialised_envelope = serde_json::to_string(&webhook_notification_envelope)?; |
| 450 | + Ok(( |
| 451 | + serialised_envelope, |
| 452 | + webhook_notification_envelope, |
| 453 | + webhook_option.clone(), |
| 454 | + )) |
| 455 | + }) |
| 456 | + .collect::<Result< |
| 457 | + Vec<(String, WebhookNotificationEnvelope<T>, WebhookOptions)>, |
| 458 | + serde_json::Error, |
| 459 | + >>()?; |
| 460 | + |
| 461 | + let webhook_payloads = serialised_webhook_envelopes |
| 462 | + .into_iter() |
| 463 | + .map( |
| 464 | + |(serialised_envelope, webhook_notification_envelope, webhook_option)| { |
| 465 | + let payload = WebhookJobPayload { |
| 466 | + url: webhook_option.url, |
| 467 | + body: serialised_envelope, |
| 468 | + headers: Some( |
| 469 | + [ |
| 470 | + ("Content-Type".to_string(), "application/json".to_string()), |
| 471 | + ( |
| 472 | + "User-Agent".to_string(), |
| 473 | + format!("{}/{}", envelope.executor_name, envelope.stage_name), |
| 474 | + ), |
| 475 | + ] |
| 476 | + .into_iter() |
| 477 | + .collect(), |
| 478 | + ), |
| 479 | + hmac_secret: webhook_option.secret, // TODO: Add HMAC support if needed |
| 480 | + http_method: Some("POST".to_string()), |
| 481 | + }; |
| 482 | + return (payload, webhook_notification_envelope); |
| 483 | + }, |
| 484 | + ) |
| 485 | + .collect::<Vec<_>>(); |
| 486 | + |
| 487 | + for (payload, webhook_notification_envelope) in webhook_payloads { |
| 488 | + let mut webhook_job = webhook_queue.clone().job(payload); |
| 489 | + webhook_job.options.id = format!( |
| 490 | + "{}_{}_webhook", |
| 491 | + webhook_notification_envelope.transaction_id, |
| 492 | + webhook_notification_envelope.notification_id |
| 493 | + ); |
| 494 | + |
| 495 | + tx.queue_job(webhook_job)?; |
| 496 | + tracing::info!( |
| 497 | + transaction_id = %webhook_notification_envelope.transaction_id, |
| 498 | + executor = %webhook_notification_envelope.executor_name, |
| 499 | + stage = %webhook_notification_envelope.stage_name, |
| 500 | + event = ?webhook_notification_envelope.event_type, |
| 501 | + notification_id = %webhook_notification_envelope.notification_id, |
| 502 | + "Queued webhook notification" |
| 503 | + ); |
| 504 | + } |
| 505 | + |
| 506 | + Ok(()) |
| 507 | +} |
0 commit comments