pr_01m47d24b0e6n91zwymwxg0vpx/crates/contracts/src/webhooks.rs

185 lines5,409 bytesCodeBlame

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Webhooks: every event, to your own addresses, signed and retried1//! The webhooks service: events, delivered to the addresses a repository or
2//! a workspace registers.
3//!
4//! Every event g1t publishes can be delivered: an HTTPS `POST` of JSON,
5//! signed with the webhook's secret in `X-G1t-Signature-256`, and retried
6//! with growing waits when the receiver does not answer with a 2xx. Each
7//! delivery is kept, with what was sent and what came back, and can be sent
8//! again.
9//!
10//! Mirrors `packages/contracts/src/webhooks.ts`.
11
12use serde::{Deserialize, Serialize};
13
14use crate::repos::RepoPath;
15use crate::{User, Viewer};
16
17/// Every event a webhook can be sent, in the order people are shown them.
Actions: workflow_run, workflow.completed, artifacts on the run page, Node 2418pub const EVENT_TYPES: [&str; 20] = [
Webhooks: every event, to your own addresses, signed and retried19 "git.push",
20 "repo.created",
21 "repo.forked",
22 "issue.opened",
23 "issue.updated",
24 "issue.assigned",
25 "issue.closed",
26 "issue.reopened",
27 "comment.created",
28 "pull.opened",
29 "pull.ready",
30 "pull.updated",
31 "pull.merge_requested",
32 "pull.merged",
33 "pull.closed",
34 "checks.completed",
35 "review.completed",
Actions: workflow_run, workflow.completed, artifacts on the run page, Node 2436 "workflow.completed",
Webhooks: every event, to your own addresses, signed and retried37 "queue.changed",
38 "session.appended",
39];
40
41/// What a webhook belongs to.
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
43#[serde(rename_all = "snake_case")]
44pub enum HookScope {
45 /// One repository's events.
46 Repo,
47 /// The events of every repository in a workspace.
48 Workspace,
49}
50
51#[derive(Clone, Debug, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase")]
53pub struct Hook {
54 pub id: String,
55 pub scope: HookScope,
56 pub workspace: String,
57 /// For a repository's webhook: `owner/name`.
58 pub repo: Option<String>,
59 pub url: String,
60 /// The event types it is sent, or `["*"]` for all.
61 pub events: Vec<String>,
62 pub active: bool,
63 /// The last four characters of its secret.
64 pub secret_hint: String,
65 pub created_by: String,
66 /// RFC 3339.
67 pub created_at: String,
68 /// How its latest delivery went: `delivered`, `pending` or `failed`.
69 pub last_status: Option<String>,
70 pub last_delivered_at: Option<String>,
71}
72
73/// One event sent, or being sent, to a webhook.
74#[derive(Clone, Debug, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct HookDelivery {
77 pub id: String,
78 pub hook_id: String,
79 /// The event's id, or empty for a ping.
80 pub event_id: String,
81 pub event: String,
82 /// `pending` while it will be tried again, `delivered`, or `failed` once
83 /// it has been tried as often as it will be.
84 pub status: String,
85 pub attempts: u32,
86 /// The receiver's HTTP status, the last time it answered.
87 pub response_status: Option<u16>,
88 /// The start of what it answered.
89 pub response_body: Option<String>,
90 /// Why the last attempt failed, when the receiver could not be reached.
91 pub error: Option<String>,
92 pub duration_ms: Option<u32>,
93 /// The JSON that was sent.
94 pub payload: String,
95 /// RFC 3339.
96 pub created_at: String,
97 pub delivered_at: Option<String>,
98 pub next_attempt_at: Option<String>,
99}
100
101/// Which webhooks a call is about: a repository's, or with `repo` left out,
102/// the workspace's own.
103#[derive(Clone, Debug, Serialize, Deserialize)]
104pub struct HookOwner {
105 pub workspace: String,
106 #[serde(default)]
107 pub repo: Option<RepoPath>,
108}
109
110/// `list`. Returns `Outcome<Vec<Hook>>`. Members of the workspace only.
111#[derive(Debug, Serialize, Deserialize)]
112pub struct ListArgs {
113 pub viewer: Viewer,
114 #[serde(flatten)]
115 pub owner: HookOwner,
116}
117
118/// `create`. Returns `Outcome<CreatedHook>`. Members, for a repository's
119/// webhooks; owners, for the workspace's.
120#[derive(Debug, Serialize, Deserialize)]
121pub struct CreateArgs {
122 pub actor: User,
123 #[serde(flatten)]
124 pub owner: HookOwner,
125 pub url: String,
126 /// Event types, or `["*"]` for all. All when empty.
127 #[serde(default)]
128 pub events: Vec<String>,
129 /// Made by g1t when left out.
130 #[serde(default)]
131 pub secret: Option<String>,
132}
133
134#[derive(Clone, Debug, Serialize, Deserialize)]
135pub struct CreatedHook {
136 pub hook: Hook,
137 /// The secret, when g1t made it: shown this once.
138 pub secret: Option<String>,
139}
140
141/// `update`: only the fields given change. Returns `Outcome<Hook>`.
142#[derive(Debug, Serialize, Deserialize)]
143pub struct UpdateArgs {
144 pub actor: User,
145 #[serde(flatten)]
146 pub owner: HookOwner,
147 pub id: String,
148 #[serde(default)]
149 pub url: Option<String>,
150 #[serde(default)]
151 pub events: Option<Vec<String>>,
152 #[serde(default)]
153 pub active: Option<bool>,
154}
155
156/// `delete` (returns `Outcome<bool>`) and `ping` (sends a `ping` event and
157/// returns `Outcome<HookDelivery>`).
158#[derive(Debug, Serialize, Deserialize)]
159pub struct HookArgs {
160 pub actor: User,
161 #[serde(flatten)]
162 pub owner: HookOwner,
163 pub id: String,
164}
165
166/// `deliveries`: a webhook's latest deliveries, newest first. Returns
167/// `Outcome<Vec<HookDelivery>>`.
168#[derive(Debug, Serialize, Deserialize)]
169pub struct DeliveriesArgs {
170 pub viewer: Viewer,
171 #[serde(flatten)]
172 pub owner: HookOwner,
173 pub id: String,
174}
175
176/// `redeliver`: sends a delivery's payload again, as a new delivery.
177/// Returns `Outcome<HookDelivery>`.
178#[derive(Debug, Serialize, Deserialize)]
179#[serde(rename_all = "camelCase")]
180pub struct RedeliverArgs {
181 pub actor: User,
182 #[serde(flatten)]
183 pub owner: HookOwner,
184 pub delivery_id: String,
185}