//! Repo webhooks: the event names, the hook and delivery shapes. The stored shape is in //! migrations/0011_webhooks.sql; the store functions live in store/webhooks.rs. //! //! A hook never carries its secret. The secret is sealed with the instance key (see `secret.rs`) //! by the caller and stored as `secret_sealed`; it is read back only through //! `Store::webhook_secret`. /// Every event a hook can subscribe to. The `as_str` names are stored and sent in `X-Forge-Event`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WebhookEvent { Push, PullRequest, Issues, IssueComment, PullRequestReview, Create, Delete, Ping, Release, } impl WebhookEvent { pub const ALL: [WebhookEvent; 9] = [ WebhookEvent::Push, WebhookEvent::PullRequest, WebhookEvent::Issues, WebhookEvent::IssueComment, WebhookEvent::PullRequestReview, WebhookEvent::Create, WebhookEvent::Delete, WebhookEvent::Ping, WebhookEvent::Release, ]; pub fn as_str(self) -> &'static str { match self { WebhookEvent::Push => "push", WebhookEvent::PullRequest => "pull_request", WebhookEvent::Issues => "issues", WebhookEvent::IssueComment => "issue_comment", WebhookEvent::PullRequestReview => "pull_request_review", WebhookEvent::Create => "create", WebhookEvent::Delete => "delete", WebhookEvent::Ping => "ping", WebhookEvent::Release => "release", } } pub fn parse(s: &str) -> Option { WebhookEvent::ALL.into_iter().find(|e| e.as_str() == s) } } /// Delivery state. Stored as the `as_str` name. /// `Pending`: queued, never attempted. `Failed`: an attempt failed and a retry is scheduled. /// `Ok`: a 2xx response was received. `GaveUp`: no further attempts. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DeliveryState { Pending, Ok, Failed, GaveUp, } impl DeliveryState { pub fn as_str(self) -> &'static str { match self { DeliveryState::Pending => "pending", DeliveryState::Ok => "ok", DeliveryState::Failed => "failed", DeliveryState::GaveUp => "gave_up", } } pub fn parse(s: &str) -> Option { match s { "pending" => Some(DeliveryState::Pending), "ok" => Some(DeliveryState::Ok), "failed" => Some(DeliveryState::Failed), "gave_up" => Some(DeliveryState::GaveUp), _ => None, } } } /// A repo webhook. Has no secret field; see `Store::webhook_secret`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Webhook { pub id: String, pub repo_id: String, pub url: String, pub events: Vec, pub active: bool, pub created_at: String, pub updated_at: String, } /// One delivery attempt chain for one event. `request_*` is immutable; the rest is updated as /// attempts happen. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Delivery { pub id: String, pub hook_id: String, /// The `X-Forge-Delivery` value. pub guid: String, pub event: WebhookEvent, /// The payload's action, or empty when the event has none. pub action: String, /// JSON object of the request headers that were sent. pub request_headers: String, pub request_body: String, pub state: DeliveryState, /// Attempts started. Incremented when the delivery is claimed. pub attempt: u32, /// When the next attempt is due. `None` once the delivery is `Ok` or `GaveUp`. pub next_attempt_at: Option, pub response_status: Option, /// JSON object of the response headers. pub response_headers: Option, /// The first 64 KiB of the response body. pub response_body: Option, /// Transport error text, such as a refused connection or a blocked address. pub error: Option, pub duration_ms: Option, pub created_at: String, pub delivered_at: Option, } /// The outcome of one attempt, recorded by `Store::record_delivery_result`. /// /// `ok` true sets the state to `Ok`. `ok` false with `next_attempt_at` set sets it to `Failed` /// and schedules a retry. `ok` false with `next_attempt_at` `None` sets it to `GaveUp`. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct AttemptOutcome { pub ok: bool, pub status: Option, pub response_headers: Option, /// Truncated to 64 KiB by the store, at a character boundary. pub response_body: Option, pub error: Option, pub duration_ms: Option, pub next_attempt_at: Option, /// The headers actually sent on this attempt, as a JSON object. When `Some`, it replaces the /// stored request headers, so the delivery log shows what the receiver got (including the /// GUID and signature the worker adds). `None` keeps what was stored at enqueue time. pub request_headers: Option, } /// Largest response body kept, in bytes. pub const MAX_RESPONSE_BODY_BYTES: usize = 64 * 1024;