flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/crates/contracts/src/webhooks.rs

186 lines5,428 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.
Agents asked while not at work are woken to answer18pub const EVENT_TYPES: [&str; 21] = [
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",
Agents asked while not at work are woken to answer34 "agent.asked",
Webhooks: every event, to your own addresses, signed and retried35 "checks.completed",
36 "review.completed",
Actions: workflow_run, workflow.completed, artifacts on the run page, Node 2437 "workflow.completed",
Webhooks: every event, to your own addresses, signed and retried38 "queue.changed",
39 "session.appended",
40];
41
42/// What a webhook belongs to.
43#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "snake_case")]
45pub enum HookScope {
46 /// One repository's events.
47 Repo,
48 /// The events of every repository in a workspace.
49 Workspace,
50}
51
52#[derive(Clone, Debug, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct Hook {
55 pub id: String,
56 pub scope: HookScope,
57 pub workspace: String,
58 /// For a repository's webhook: `owner/name`.
59 pub repo: Option<String>,
60 pub url: String,
61 /// The event types it is sent, or `["*"]` for all.
62 pub events: Vec<String>,
63 pub active: bool,
64 /// The last four characters of its secret.
65 pub secret_hint: String,
66 pub created_by: String,
67 /// RFC 3339.
68 pub created_at: String,
69 /// How its latest delivery went: `delivered`, `pending` or `failed`.
70 pub last_status: Option<String>,
71 pub last_delivered_at: Option<String>,
72}
73
74/// One event sent, or being sent, to a webhook.
75#[derive(Clone, Debug, Serialize, Deserialize)]
76#[serde(rename_all = "camelCase")]
77pub struct HookDelivery {
78 pub id: String,
79 pub hook_id: String,
80 /// The event's id, or empty for a ping.
81 pub event_id: String,
82 pub event: String,
83 /// `pending` while it will be tried again, `delivered`, or `failed` once
84 /// it has been tried as often as it will be.
85 pub status: String,
86 pub attempts: u32,
87 /// The receiver's HTTP status, the last time it answered.
88 pub response_status: Option<u16>,
89 /// The start of what it answered.
90 pub response_body: Option<String>,
91 /// Why the last attempt failed, when the receiver could not be reached.
92 pub error: Option<String>,
93 pub duration_ms: Option<u32>,
94 /// The JSON that was sent.
95 pub payload: String,
96 /// RFC 3339.
97 pub created_at: String,
98 pub delivered_at: Option<String>,
99 pub next_attempt_at: Option<String>,
100}
101
102/// Which webhooks a call is about: a repository's, or with `repo` left out,
103/// the workspace's own.
104#[derive(Clone, Debug, Serialize, Deserialize)]
105pub struct HookOwner {
106 pub workspace: String,
107 #[serde(default)]
108 pub repo: Option<RepoPath>,
109}
110
111/// `list`. Returns `Outcome<Vec<Hook>>`. Members of the workspace only.
112#[derive(Debug, Serialize, Deserialize)]
113pub struct ListArgs {
114 pub viewer: Viewer,
115 #[serde(flatten)]
116 pub owner: HookOwner,
117}
118
119/// `create`. Returns `Outcome<CreatedHook>`. Members, for a repository's
120/// webhooks; owners, for the workspace's.
121#[derive(Debug, Serialize, Deserialize)]
122pub struct CreateArgs {
123 pub actor: User,
124 #[serde(flatten)]
125 pub owner: HookOwner,
126 pub url: String,
127 /// Event types, or `["*"]` for all. All when empty.
128 #[serde(default)]
129 pub events: Vec<String>,
130 /// Made by g1t when left out.
131 #[serde(default)]
132 pub secret: Option<String>,
133}
134
135#[derive(Clone, Debug, Serialize, Deserialize)]
136pub struct CreatedHook {
137 pub hook: Hook,
138 /// The secret, when g1t made it: shown this once.
139 pub secret: Option<String>,
140}
141
142/// `update`: only the fields given change. Returns `Outcome<Hook>`.
143#[derive(Debug, Serialize, Deserialize)]
144pub struct UpdateArgs {
145 pub actor: User,
146 #[serde(flatten)]
147 pub owner: HookOwner,
148 pub id: String,
149 #[serde(default)]
150 pub url: Option<String>,
151 #[serde(default)]
152 pub events: Option<Vec<String>>,
153 #[serde(default)]
154 pub active: Option<bool>,
155}
156
157/// `delete` (returns `Outcome<bool>`) and `ping` (sends a `ping` event and
158/// returns `Outcome<HookDelivery>`).
159#[derive(Debug, Serialize, Deserialize)]
160pub struct HookArgs {
161 pub actor: User,
162 #[serde(flatten)]
163 pub owner: HookOwner,
164 pub id: String,
165}
166
167/// `deliveries`: a webhook's latest deliveries, newest first. Returns
168/// `Outcome<Vec<HookDelivery>>`.
169#[derive(Debug, Serialize, Deserialize)]
170pub struct DeliveriesArgs {
171 pub viewer: Viewer,
172 #[serde(flatten)]
173 pub owner: HookOwner,
174 pub id: String,
175}
176
177/// `redeliver`: sends a delivery's payload again, as a new delivery.
178/// Returns `Outcome<HookDelivery>`.
179#[derive(Debug, Serialize, Deserialize)]
180#[serde(rename_all = "camelCase")]
181pub struct RedeliverArgs {
182 pub actor: User,
183 #[serde(flatten)]
184 pub owner: HookOwner,
185 pub delivery_id: String,
186}