OXYGENHUB
◆ v0.3.1 oxygenhub / crates / hub-api / src / lib.rs
31.7 KiB · Rust · last changed Labels from oh: on proposals and issues, and the repository's labels 1 month ago source blame history raw
8d2caf12 Andy McCall ↶ 1
//! Types of the OxygenHub JSON API (hub spec §12.1). Everything here is
d23464c0 Andy McCall ↶ 2
//! `serde` data with no behaviour, so clients in other languages can be
3
//! generated from it and the server can never drift from them.
4
//!
5
//! Shapes reuse `oxy --json`'s where one exists (hashes as lowercase hex,
6
//! times as milliseconds since the epoch), so tools written against the
7
//! command line work against the hub.
8
b64e2537 Andy McCall ↶ 9
use schemars::JsonSchema;
d23464c0 Andy McCall ↶ 10
use serde::{Deserialize, Serialize};
11
12
/// A repository as the API lists it.
b64e2537 Andy McCall ↶ 13
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
d23464c0 Andy McCall ↶ 14
pub struct Repository {
15
    pub owner: String,
16
    pub name: String,
17
    pub description: String,
18
    pub default_bookmark: String,
19
    pub visibility: Visibility,
20
    /// Milliseconds since the epoch.
21
    pub created_ms: i64,
22
    pub updated_ms: i64,
23
    /// `https://…/{owner}/{name}`, what `oxy clone` takes.
24
    pub clone_url: String,
25
}
26
b64e2537 Andy McCall ↶ 27
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
d23464c0 Andy McCall ↶ 28
#[serde(rename_all = "lowercase")]
29
pub enum Visibility {
30
    Public,
31
    Internal,
32
    Private,
33
}
34
35
impl Visibility {
36
    pub fn as_str(self) -> &'static str {
37
        match self {
38
            Visibility::Public => "public",
39
            Visibility::Internal => "internal",
40
            Visibility::Private => "private",
41
        }
42
    }
43
44
    pub fn parse(s: &str) -> Option<Visibility> {
45
        Some(match s {
46
            "public" => Visibility::Public,
47
            "internal" => Visibility::Internal,
48
            "private" => Visibility::Private,
49
            _ => return None,
50
        })
51
    }
52
}
53
54
/// A bookmark: a name for a set of heads (spec §10.1).
b64e2537 Andy McCall ↶ 55
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
d23464c0 Andy McCall ↶ 56
pub struct Bookmark {
57
    pub name: String,
58
    pub heads: Vec<String>,
59
    /// Number of changes in the bookmark's state.
60
    pub changes: usize,
61
    pub default: bool,
62
}
63
64
/// A tag: a name for one state, with its attestation when signed.
b64e2537 Andy McCall ↶ 65
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
d23464c0 Andy McCall ↶ 66
pub struct Tag {
67
    pub name: String,
68
    pub heads: Vec<String>,
69
    pub message: String,
70
    pub signer: Option<String>,
71
}
72
73
/// One change in a history listing.
b64e2537 Andy McCall ↶ 74
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
d23464c0 Andy McCall ↶ 75
pub struct ChangeSummary {
76
    pub hash: String,
77
    pub message: String,
78
    pub author: Author,
79
    pub when_ms: u64,
80
    pub deps: Vec<String>,
81
    pub head: bool,
82
    pub signature: Signature,
83
}
84
85
/// Who wrote a change: the linked account when the signing key is linked,
86
/// else the key alone.
b64e2537 Andy McCall ↶ 87
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
d23464c0 Andy McCall ↶ 88
pub struct Author {
89
    pub name: String,
90
    pub email: String,
91
    pub key: String,
92
    /// The hub account this key is linked to, when any.
93
    pub account: Option<String>,
760a7dd3 Andy McCall ↶ 94
    /// Set when the change is signed by a key no account has linked, and
95
    /// says how its owner links it.
96
    #[serde(default, skip_serializing_if = "Option::is_none")]
97
    pub note: Option<String>,
98
}
99
100
impl Author {
101
    /// What `note` says for a key that no account has linked.
102
    pub const UNLINKED: &'static str =
103
        "signed by a key not linked to any account; its owner links it under Settings → Keys (/settings/keys)";
104
105
    /// The note for an author with this key and account.
106
    pub fn note_for(key: &str, account: &Option<String>) -> Option<String> {
107
        (!key.is_empty() && account.is_none()).then(|| Self::UNLINKED.to_string())
108
    }
d23464c0 Andy McCall ↶ 109
}
110
111
/// Signature status of a change as `oxy verify` reports it.
b64e2537 Andy McCall ↶ 112
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
d23464c0 Andy McCall ↶ 113
#[serde(tag = "status", rename_all = "lowercase")]
114
pub enum Signature {
115
    Unsigned,
116
    Valid { key: String, sealed: bool, revoked: bool },
117
    Invalid { reason: String },
118
}
119
120
/// An RFC 7807 problem response.
b64e2537 Andy McCall ↶ 121
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
d23464c0 Andy McCall ↶ 122
pub struct Problem {
123
    pub status: u16,
124
    pub title: String,
125
    pub detail: String,
126
}
127
128
#[cfg(test)]
129
mod tests {
130
    use super::*;
131
132
    #[test]
133
    fn visibility_round_trips() {
134
        for v in [Visibility::Public, Visibility::Internal, Visibility::Private] {
135
            assert_eq!(Visibility::parse(v.as_str()), Some(v));
136
        }
137
        assert_eq!(Visibility::parse("secret"), None);
138
    }
139
}
de505df5 Andy McCall ↶ 140
141
// ---------------------------------------------------------------- proposals
142
143
/// A proposal: a request to add changes to a target bookmark (hub spec §7).
b64e2537 Andy McCall ↶ 144
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
de505df5 Andy McCall ↶ 145
pub struct Proposal {
146
    pub number: i64,
147
    pub title: String,
148
    pub body: String,
149
    pub author: String,
150
    pub state: ProposalState,
151
    pub target: String,
152
    pub source: ProposalSource,
153
    /// The changes the proposal adds, dependency order, newest last.
154
    pub changes: Vec<ChangeSummary>,
155
    /// Heads of the projected state (target plus the changes), when computed.
156
    pub projected_heads: Vec<String>,
157
    pub projected_state: Option<String>,
158
    pub conflicted: bool,
159
    pub approvals: Vec<Approval>,
160
    pub landable: bool,
161
    pub blockers: Vec<String>,
162
    pub created_ms: i64,
163
    pub updated_ms: i64,
164
    /// `https://hub/{owner}/{name}/proposal/{number}`.
165
    pub url: String,
166
    /// Where `oxy` fetches the changes from.
167
    pub repository_url: String,
168
    /// The fork's clone URL when the source is a fork.
169
    pub source_url: Option<String>,
0b20f1ec Andy McCall ↶ 170
    /// Who landed it, how and when, once landed.
171
    #[serde(default)]
172
    pub landed: Option<Landed>,
cc1cd197 Andy McCall ↶ 173
    /// Asked to review and not yet reviewing, in the order asked.
174
    #[serde(default)]
175
    pub requested_reviewers: Vec<String>,
2291f8cc Andy McCall ↶ 176
    #[serde(default)]
177
    pub labels: Vec<Label>,
0b20f1ec Andy McCall ↶ 178
}
179
180
/// How a proposal was landed.
181
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
182
pub struct Landed {
183
    pub by: String,
184
    /// `web`, or `api (token NAME)`.
185
    pub via: String,
186
    pub when_ms: i64,
de505df5 Andy McCall ↶ 187
}
188
b64e2537 Andy McCall ↶ 189
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
de505df5 Andy McCall ↶ 190
#[serde(rename_all = "lowercase")]
191
pub enum ProposalState {
192
    Draft,
193
    Open,
194
    Landed,
195
    Closed,
196
}
197
198
impl ProposalState {
199
    pub fn as_str(self) -> &'static str {
200
        match self {
201
            ProposalState::Draft => "draft",
202
            ProposalState::Open => "open",
203
            ProposalState::Landed => "landed",
204
            ProposalState::Closed => "closed",
205
        }
206
    }
207
208
    pub fn parse(s: &str) -> Option<ProposalState> {
209
        Some(match s {
210
            "draft" => ProposalState::Draft,
211
            "open" => ProposalState::Open,
212
            "landed" => ProposalState::Landed,
213
            "closed" => ProposalState::Closed,
214
            _ => return None,
215
        })
216
    }
217
}
218
b64e2537 Andy McCall ↶ 219
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
de505df5 Andy McCall ↶ 220
#[serde(tag = "kind", rename_all = "lowercase")]
221
pub enum ProposalSource {
222
    /// A bookmark of the same repository.
223
    Bookmark { bookmark: String },
224
    /// A bookmark of a fork.
225
    Fork { owner: String, name: String, bookmark: String },
226
    /// An explicit list of change hashes.
227
    Changes { hashes: Vec<String> },
228
}
229
230
/// A reviewer's approval, counted only when backed by a seal over every
231
/// change of the proposal signed with a key linked to the reviewer.
b64e2537 Andy McCall ↶ 232
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
de505df5 Andy McCall ↶ 233
pub struct Approval {
234
    pub account: String,
235
    pub sealed: bool,
236
    pub key: Option<String>,
237
}
b64e2537 Andy McCall ↶ 238
239
// ------------------------------------------------------------------ issues
240
241
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
242
pub struct Issue {
243
    pub number: i64,
244
    pub title: String,
245
    pub body: String,
246
    pub author: String,
247
    pub state: String,
248
    pub reason: String,
249
    pub labels: Vec<Label>,
250
    pub assignees: Vec<String>,
251
    pub milestone: Option<String>,
252
    pub comments: i64,
253
    pub locked: bool,
254
    pub pinned: bool,
255
    pub created_ms: i64,
256
    pub updated_ms: i64,
257
    pub closed_ms: Option<i64>,
258
    pub url: String,
259
}
260
261
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
262
pub struct Label {
263
    pub name: String,
264
    pub colour: String,
265
    pub description: String,
266
}
267
268
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
269
pub struct Milestone {
270
    pub title: String,
271
    pub description: String,
272
    pub due_ms: Option<i64>,
273
    pub state: String,
274
    pub open: i64,
275
    pub closed: i64,
276
}
277
278
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
279
pub struct Comment {
280
    pub id: i64,
281
    pub author: String,
282
    pub body: String,
283
    pub created_ms: i64,
284
    /// For proposal comments: the file and the atom the comment anchors to.
285
    pub path: Option<String>,
286
    pub anchor: Option<Atom>,
c0fac31d Andy McCall ↶ 287
    /// Where the anchor sits now: its 1-based line in `path` in the
288
    /// projected state, when the atom is still live there.
289
    #[serde(default)]
290
    pub line: Option<usize>,
b64e2537 Andy McCall ↶ 291
    pub resolved: bool,
292
}
293
42a0ba4b Andy McCall ↶ 294
/// One entry in the caller's inbox (hub spec §10).
295
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
296
pub struct Notification {
297
    pub id: i64,
298
    pub at_ms: i64,
299
    /// `issue.opened`, `proposal.comment`, `mention`, `review`, …
300
    pub kind: String,
301
    pub title: String,
302
    /// The page on the hub, absolute.
303
    pub url: String,
304
    pub actor: String,
305
    pub read: bool,
306
}
307
308
/// `GET /notifications`: the unread count and the newest entries, unread first.
309
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
310
pub struct Inbox {
311
    pub unread: i64,
312
    pub items: Vec<Notification>,
313
}
314
3f02e5ea Andy McCall ↶ 315
/// A file a proposal changes, between the target's state and the
316
/// projected state (hub spec §7.2). Fetch either side's bytes from
317
/// `/proposals/{number}/content/{base|head}/{path}`.
b64e2537 Andy McCall ↶ 318
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
3f02e5ea Andy McCall ↶ 319
pub struct ProposalFile {
320
    pub path: String,
321
    /// For a rename, the path in the target's state.
322
    pub from: Option<String>,
323
    /// `added`, `removed`, `modified`, `renamed`, `mode` or `replaced`.
324
    pub kind: String,
325
    pub added: usize,
326
    pub removed: usize,
327
}
328
329
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
b64e2537 Andy McCall ↶ 330
pub struct Atom {
331
    pub change: String,
332
    pub index: u32,
333
}
334
335
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
336
pub struct Review {
337
    pub id: i64,
338
    pub author: String,
339
    pub verdict: String,
340
    pub body: String,
341
    pub sealed: bool,
342
    pub created_ms: i64,
343
}
344
345
// ------------------------------------------------------------- repository
346
347
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
348
pub struct TreeEntry {
349
    pub name: String,
350
    pub path: String,
351
    pub kind: String,
352
    pub exec: bool,
353
    pub size: Option<u64>,
354
    pub conflicted: bool,
355
}
356
357
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
358
pub struct BlameLine {
359
    pub line: usize,
360
    pub change: String,
361
    pub index: u32,
362
    pub author: String,
363
    pub when_ms: u64,
364
    pub text: String,
365
}
366
367
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
368
pub struct FileChange {
369
    pub path: String,
370
    pub kind: String,
371
    pub from: Option<String>,
372
    pub added: usize,
373
    pub removed: usize,
374
}
375
376
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
377
pub struct ChangeDetail {
378
    pub change: ChangeSummary,
379
    pub body: String,
380
    pub dependents: Vec<String>,
381
    pub in_bookmarks: Vec<String>,
382
    pub in_tags: Vec<String>,
383
    pub files: Vec<FileChange>,
384
}
385
386
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
387
pub struct History {
388
    pub reference: String,
389
    pub heads: Vec<String>,
390
    pub total: usize,
391
    pub offset: usize,
392
    pub changes: Vec<ChangeSummary>,
393
}
394
395
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
396
pub struct Conflict {
397
    pub bookmark: String,
398
    pub path: String,
399
    pub kind: String,
400
    pub contributors: Vec<String>,
401
}
402
403
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
404
pub struct Push {
405
    pub at_ms: i64,
406
    pub pusher: String,
407
    pub reference: String,
408
    pub ok: bool,
409
    pub reason: String,
410
    pub old_state: String,
411
    pub new_state: String,
412
    pub heads: i64,
413
    pub changes: i64,
414
}
415
416
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
417
pub struct Trust {
418
    pub policy: Option<Policy>,
419
    pub signed: usize,
420
    pub sealed: usize,
421
    pub unsigned: usize,
422
    pub invalid: Vec<String>,
423
    pub hub_key: String,
424
}
425
426
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
427
pub struct Policy {
428
    pub serial: u64,
429
    pub expires_ms: u64,
430
    pub roots: Vec<String>,
431
    pub root_threshold: u32,
432
    pub rules: Vec<Rule>,
433
}
434
435
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
436
pub struct Rule {
437
    pub pattern: String,
438
    pub keys: Vec<String>,
439
    pub threshold: u32,
440
    pub require: String,
441
    pub allow_conflicts: bool,
442
}
443
444
// ---------------------------------------------------------- hooks, checks
445
446
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
447
pub struct Hook {
448
    pub id: i64,
449
    pub url: String,
450
    pub events: String,
451
    pub active: bool,
452
    pub created_ms: i64,
453
}
454
455
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
456
pub struct NewHook {
457
    pub url: String,
458
    pub secret: String,
459
    #[serde(default = "star")]
460
    pub events: String,
461
}
462
463
fn star() -> String {
464
    "*".into()
465
}
466
90326c96 Andy McCall ↶ 467
/// An OxygenFlow run (hub spec §13.2).
468
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
469
pub struct FlowRun {
470
    /// The run's number within its repository.
471
    pub number: i64,
472
    pub bookmark: String,
473
    /// The state's hash, as checks are keyed.
474
    pub state: String,
475
    /// Its heads, full change hashes.
476
    pub heads: Vec<String>,
477
    /// `queued`, `running`, `passed`, `failed` or `error` (an invalid pipeline).
478
    pub status: String,
479
    /// `push`, `manual` or `rerun:N`.
480
    pub trigger: String,
481
    pub actor: String,
482
    pub pipeline_hash: String,
483
    /// An invalid pipeline's problems.
484
    pub error: String,
485
    pub created_ms: i64,
486
    pub finished_ms: Option<i64>,
487
    pub url: String,
bf3d0754 Andy McCall ↶ 488
    /// For a run waiting for a runner, its first queued job's 1-based
489
    /// place among all queued jobs.
490
    #[serde(default, skip_serializing_if = "Option::is_none")]
491
    pub queue_position: Option<i64>,
90326c96 Andy McCall ↶ 492
    /// Filled by the single-run endpoint.
493
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
494
    pub jobs: Vec<FlowJob>,
495
}
496
497
/// A job of a run.
498
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
499
pub struct FlowJob {
500
    pub name: String,
501
    /// `queued`, `running`, `passed`, `failed`, `skipped` or `error`.
502
    pub status: String,
503
    pub depends_on: Vec<String>,
504
    pub runner: String,
505
    pub exit_code: Option<i64>,
506
    pub error: String,
507
    pub started_ms: Option<i64>,
508
    pub finished_ms: Option<i64>,
509
    /// Log lines stored so far.
510
    pub log_lines: i64,
511
}
512
513
/// A line of a job's log.
514
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
515
pub struct FlowLogLine {
516
    /// Increasing within the job; pass the last one as `after` for more.
517
    pub seq: i64,
518
    /// Milliseconds since the job started.
519
    pub t: i64,
520
    /// `stdout`, `stderr` or `flow` (the runner's own notes).
521
    pub stream: String,
522
    pub text: String,
523
}
524
b64e2537 Andy McCall ↶ 525
/// A check on a state hash (hub spec §13.1).
526
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
527
pub struct Check {
528
    pub state: String,
529
    pub name: String,
530
    pub status: CheckStatus,
531
    pub summary: String,
532
    pub url: String,
533
    pub reporter: String,
534
    /// The seal attestation's reference when the checker sealed the changes.
535
    pub seal: Option<String>,
536
    pub started_ms: i64,
537
    pub finished_ms: Option<i64>,
538
}
539
540
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
541
#[serde(rename_all = "lowercase")]
542
pub enum CheckStatus {
543
    Queued,
544
    Running,
545
    Passed,
546
    Failed,
547
    Cancelled,
548
    Skipped,
549
}
550
551
impl CheckStatus {
552
    pub fn as_str(self) -> &'static str {
553
        match self {
554
            CheckStatus::Queued => "queued",
555
            CheckStatus::Running => "running",
556
            CheckStatus::Passed => "passed",
557
            CheckStatus::Failed => "failed",
558
            CheckStatus::Cancelled => "cancelled",
559
            CheckStatus::Skipped => "skipped",
560
        }
561
    }
562
563
    pub fn parse(s: &str) -> Option<CheckStatus> {
564
        Some(match s {
565
            "queued" => CheckStatus::Queued,
566
            "running" => CheckStatus::Running,
567
            "passed" => CheckStatus::Passed,
568
            "failed" => CheckStatus::Failed,
569
            "cancelled" => CheckStatus::Cancelled,
570
            "skipped" => CheckStatus::Skipped,
571
            _ => return None,
572
        })
573
    }
574
}
575
576
/// What a checker posts.
577
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
578
pub struct NewCheck {
579
    pub state: String,
580
    pub name: String,
581
    pub status: CheckStatus,
582
    #[serde(default)]
583
    pub summary: String,
584
    #[serde(default)]
585
    pub url: String,
586
    /// Hex of a seal attestation reference the checker pushed, if any.
587
    #[serde(default)]
588
    pub seal: Option<String>,
589
}
590
591
// ------------------------------------------------------------- accounts
592
593
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
594
pub struct User {
595
    pub username: String,
596
    pub display_name: String,
597
    pub is_admin: bool,
598
    pub keys: Vec<String>,
599
    pub created_ms: i64,
600
}
601
602
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
603
pub struct Organisation {
604
    pub name: String,
605
    pub display_name: String,
606
    pub description: String,
607
    pub members: Vec<String>,
608
}
609
610
/// Forms accepted by the write endpoints.
611
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
612
pub struct NewRepository {
613
    pub owner: String,
614
    pub name: String,
615
    #[serde(default)]
616
    pub description: String,
617
    #[serde(default = "public")]
618
    pub visibility: Visibility,
619
}
620
621
fn public() -> Visibility {
622
    Visibility::Public
623
}
624
625
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
626
pub struct NewIssue {
627
    pub title: String,
628
    #[serde(default)]
629
    pub body: String,
76f262e1 Andy McCall ↶ 630
    /// Label names; each must exist. Needs triage.
631
    #[serde(default)]
632
    pub labels: Vec<String>,
633
    /// Usernames. Needs triage.
634
    #[serde(default)]
635
    pub assignees: Vec<String>,
636
    /// A milestone's title. Needs triage.
637
    #[serde(default)]
638
    pub milestone: Option<String>,
b64e2537 Andy McCall ↶ 639
}
640
76f262e1 Andy McCall ↶ 641
/// `PATCH /repos/{o}/{n}/issues/{number}`: only the fields given change.
642
/// Title and body: the author or triage. The rest: triage.
643
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
644
pub struct IssueEdit {
645
    #[serde(default)]
646
    pub title: Option<String>,
647
    #[serde(default)]
648
    pub body: Option<String>,
649
    /// The whole set of labels.
650
    #[serde(default)]
651
    pub labels: Option<Vec<String>>,
652
    /// The whole set of assignees.
653
    #[serde(default)]
654
    pub assignees: Option<Vec<String>>,
655
    /// A milestone's title; the empty string clears it.
656
    #[serde(default)]
657
    pub milestone: Option<String>,
658
}
659
961c10be Andy McCall ↶ 660
/// The checks a proposal needs, passed, to land on the repository's
661
/// default bookmark (hub spec §13.1). `GET` and `PUT
662
/// /repos/{o}/{n}/checks/required`.
663
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
664
pub struct RequiredChecks {
665
    pub names: Vec<String>,
666
}
667
ef20fdc1 Andy McCall ↶ 668
/// A project (hub spec §8.2).
669
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
670
pub struct Project {
671
    pub owner: String,
672
    pub number: i64,
673
    pub title: String,
674
    pub description: String,
675
    pub public: bool,
676
    pub closed: bool,
677
    pub updated_ms: i64,
678
    /// The board on the hub, absolute.
679
    pub url: String,
680
}
681
682
/// A card as the caller may see it.
683
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
684
pub struct ProjectCard {
685
    pub id: i64,
686
    /// `issue`, `proposal` or `note`.
687
    pub kind: String,
688
    /// `owner/name#N`; empty for a note.
689
    pub reference: String,
690
    /// The issue's or proposal's title, or the note.
691
    pub title: String,
692
    /// The issue's or proposal's state; empty for a note.
693
    pub state: String,
694
}
695
696
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
697
pub struct ProjectColumn {
698
    pub id: i64,
699
    pub name: String,
700
    pub cards: Vec<ProjectCard>,
701
}
702
703
/// `GET /projects/{owner}/{n}`.
704
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
705
pub struct Board {
706
    pub project: Project,
707
    pub columns: Vec<ProjectColumn>,
708
}
709
710
/// `POST /projects/{owner}`.
711
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
712
pub struct NewProject {
713
    pub title: String,
714
    #[serde(default)]
715
    pub description: String,
716
    #[serde(default)]
717
    pub private: bool,
718
}
719
720
/// `PATCH /projects/{owner}/{n}`: only the fields given change.
721
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
722
pub struct ProjectEdit {
723
    #[serde(default)]
724
    pub title: Option<String>,
725
    #[serde(default)]
726
    pub description: Option<String>,
727
    #[serde(default)]
728
    pub private: Option<bool>,
729
    #[serde(default)]
730
    pub closed: Option<bool>,
731
}
732
733
/// `POST /projects/{owner}/{n}/cards`: a reference (`repo#12`) or a note,
734
/// into the column named (by id or name; the first column when absent).
735
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
736
pub struct NewCard {
737
    #[serde(default)]
738
    pub column: Option<String>,
739
    #[serde(default)]
740
    pub reference: String,
741
    #[serde(default)]
742
    pub note: String,
743
}
744
745
/// `PATCH /projects/{owner}/{n}/cards/{id}`: move to a column (id or name).
746
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
747
pub struct CardMove {
748
    pub column: String,
749
}
750
76f262e1 Andy McCall ↶ 751
/// `POST /repos/{o}/{n}/labels`: create a label, or change one of that name.
752
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
753
pub struct NewLabel {
754
    pub name: String,
755
    /// `#rrggbb`; a default when absent.
756
    #[serde(default)]
757
    pub colour: Option<String>,
758
    #[serde(default)]
759
    pub description: String,
760
}
761
b64e2537 Andy McCall ↶ 762
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
763
pub struct NewComment {
764
    pub body: String,
3f4b0ea6 Andy McCall ↶ 765
    /// For a proposal: anchor the comment to this file's line (1-based) of
766
    /// the projected state; the hub records the line's atom.
767
    #[serde(default)]
768
    pub path: Option<String>,
769
    #[serde(default)]
770
    pub line: Option<usize>,
b64e2537 Andy McCall ↶ 771
}
772
773
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
774
pub struct NewProposal {
775
    pub title: String,
776
    #[serde(default)]
777
    pub body: String,
778
    pub target: String,
779
    /// A bookmark here, or `owner/name:bookmark` of a fork.
780
    pub source: String,
781
    #[serde(default)]
782
    pub draft: bool,
cc1cd197 Andy McCall ↶ 783
}
784
785
/// `POST .../proposals/{number}/reviewers`: accounts to ask for a review.
786
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
787
pub struct ReviewRequest {
788
    pub reviewers: Vec<String>,
b64e2537 Andy McCall ↶ 789
}
790
791
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
792
pub struct NewReview {
793
    pub verdict: String,
794
    #[serde(default)]
795
    pub body: String,
796
}
797
798
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
799
pub struct IssueStateChange {
800
    /// `open` or `closed`.
801
    pub state: String,
802
    #[serde(default)]
803
    pub reason: String,
804
}
805
806
/// A webhook payload's envelope (hub spec §12.2).
807
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
808
pub struct WebhookEnvelope {
809
    pub event: String,
810
    pub delivery: String,
811
    pub repository: Option<Repository>,
812
    pub actor: String,
813
    pub at_ms: i64,
814
    pub data: serde_json::Value,
815
}
816
817
/// A server-sent event on a proposal's stream.
818
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
819
pub struct ProposalEvent {
820
    pub number: i64,
821
    pub kind: String,
822
    pub check: Option<Check>,
823
}
824
3f4b0ea6 Andy McCall ↶ 825
// --------------------------------------------------------------- releases
826
827
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
828
pub struct Release {
829
    pub tag: String,
830
    pub title: String,
831
    pub notes: String,
832
    pub prerelease: bool,
833
    pub latest: bool,
834
    pub author: String,
835
    pub heads: Vec<String>,
836
    pub signer: Option<String>,
837
    pub assets: Vec<ReleaseAsset>,
838
    pub archives: Vec<String>,
839
    pub created_ms: i64,
840
    pub url: String,
841
}
842
843
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
844
pub struct ReleaseAsset {
845
    pub name: String,
846
    pub size: i64,
847
    /// BLAKE3 of the content, hex.
848
    pub hash: String,
849
    pub content_type: String,
850
    pub downloads: i64,
851
    pub url: String,
10708ab8 Andy McCall ↶ 852
    /// The OxygenFlow run and job whose artifact has this content, if any.
853
    #[serde(default, skip_serializing_if = "Option::is_none")]
854
    pub built_by: Option<AssetBuild>,
855
}
856
857
/// Where a release asset was built (hub spec §9): a job's artifact with the
858
/// same BLAKE3, and that job's signed receipt when one was kept.
859
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
860
pub struct AssetBuild {
861
    pub run: i64,
862
    pub job: String,
863
    pub artifact: String,
864
    pub run_url: String,
865
    /// The receipt attestation, hex; `None` when the receipt was not signed.
866
    pub receipt: Option<String>,
867
    pub receipt_url: Option<String>,
868
    pub signer: Option<String>,
3f4b0ea6 Andy McCall ↶ 869
}
870
739a763a Andy McCall ↶ 871
/// `PATCH /repos/{o}/{n}/proposals/{number}`: what is given changes,
872
/// the rest stays (oxygenhub#76).
873
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
874
pub struct ProposalEdit {
875
    #[serde(default, skip_serializing_if = "Option::is_none")]
876
    pub title: Option<String>,
877
    #[serde(default, skip_serializing_if = "Option::is_none")]
878
    pub body: Option<String>,
2291f8cc Andy McCall ↶ 879
    /// The whole set of labels, by name (triage); oxygenhub#80.
880
    #[serde(default, skip_serializing_if = "Option::is_none")]
881
    pub labels: Option<Vec<String>>,
739a763a Andy McCall ↶ 882
}
883
ced6d3af Andy McCall ↶ 884
/// `PATCH /repos/{owner}/{name}/releases/{tag}`: what to change; fields
885
/// left out stay as they are.
886
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
887
pub struct ReleaseEdit {
888
    #[serde(default, skip_serializing_if = "Option::is_none")]
889
    pub title: Option<String>,
890
    #[serde(default, skip_serializing_if = "Option::is_none")]
891
    pub notes: Option<String>,
892
    #[serde(default, skip_serializing_if = "Option::is_none")]
893
    pub prerelease: Option<bool>,
894
}
895
3f4b0ea6 Andy McCall ↶ 896
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
897
pub struct NewRelease {
898
    pub tag: String,
899
    pub title: String,
900
    #[serde(default)]
901
    pub notes: Option<String>,
902
    #[serde(default)]
903
    pub prerelease: bool,
904
}
905
b64e2537 Andy McCall ↶ 906
/// Every type of the API, for schema generation.
907
pub fn schemas() -> Vec<(&'static str, schemars::Schema)> {
908
    vec![
909
        ("Repository", schemars::schema_for!(Repository)),
910
        ("Visibility", schemars::schema_for!(Visibility)),
911
        ("Bookmark", schemars::schema_for!(Bookmark)),
912
        ("Tag", schemars::schema_for!(Tag)),
913
        ("ChangeSummary", schemars::schema_for!(ChangeSummary)),
914
        ("Author", schemars::schema_for!(Author)),
915
        ("Signature", schemars::schema_for!(Signature)),
916
        ("Problem", schemars::schema_for!(Problem)),
917
        ("Proposal", schemars::schema_for!(Proposal)),
918
        ("ProposalState", schemars::schema_for!(ProposalState)),
919
        ("ProposalSource", schemars::schema_for!(ProposalSource)),
920
        ("Approval", schemars::schema_for!(Approval)),
921
        ("Issue", schemars::schema_for!(Issue)),
922
        ("Label", schemars::schema_for!(Label)),
923
        ("Milestone", schemars::schema_for!(Milestone)),
924
        ("Comment", schemars::schema_for!(Comment)),
3f02e5ea Andy McCall ↶ 925
        ("ProposalFile", schemars::schema_for!(ProposalFile)),
42a0ba4b Andy McCall ↶ 926
        ("Notification", schemars::schema_for!(Notification)),
927
        ("Inbox", schemars::schema_for!(Inbox)),
b64e2537 Andy McCall ↶ 928
        ("Atom", schemars::schema_for!(Atom)),
929
        ("Review", schemars::schema_for!(Review)),
930
        ("TreeEntry", schemars::schema_for!(TreeEntry)),
931
        ("BlameLine", schemars::schema_for!(BlameLine)),
932
        ("FileChange", schemars::schema_for!(FileChange)),
933
        ("ChangeDetail", schemars::schema_for!(ChangeDetail)),
934
        ("History", schemars::schema_for!(History)),
935
        ("Conflict", schemars::schema_for!(Conflict)),
936
        ("Push", schemars::schema_for!(Push)),
937
        ("Trust", schemars::schema_for!(Trust)),
938
        ("Policy", schemars::schema_for!(Policy)),
939
        ("Rule", schemars::schema_for!(Rule)),
940
        ("Hook", schemars::schema_for!(Hook)),
941
        ("NewHook", schemars::schema_for!(NewHook)),
942
        ("Check", schemars::schema_for!(Check)),
943
        ("CheckStatus", schemars::schema_for!(CheckStatus)),
944
        ("NewCheck", schemars::schema_for!(NewCheck)),
945
        ("User", schemars::schema_for!(User)),
946
        ("Organisation", schemars::schema_for!(Organisation)),
947
        ("NewRepository", schemars::schema_for!(NewRepository)),
948
        ("NewIssue", schemars::schema_for!(NewIssue)),
76f262e1 Andy McCall ↶ 949
        ("IssueEdit", schemars::schema_for!(IssueEdit)),
2291f8cc Andy McCall ↶ 950
        ("ReleaseEdit", schemars::schema_for!(ReleaseEdit)),
951
        ("ProposalEdit", schemars::schema_for!(ProposalEdit)),
76f262e1 Andy McCall ↶ 952
        ("NewLabel", schemars::schema_for!(NewLabel)),
961c10be Andy McCall ↶ 953
        ("RequiredChecks", schemars::schema_for!(RequiredChecks)),
ef20fdc1 Andy McCall ↶ 954
        ("Project", schemars::schema_for!(Project)),
955
        ("ProjectCard", schemars::schema_for!(ProjectCard)),
956
        ("ProjectColumn", schemars::schema_for!(ProjectColumn)),
957
        ("Board", schemars::schema_for!(Board)),
958
        ("NewProject", schemars::schema_for!(NewProject)),
959
        ("ProjectEdit", schemars::schema_for!(ProjectEdit)),
960
        ("NewCard", schemars::schema_for!(NewCard)),
961
        ("CardMove", schemars::schema_for!(CardMove)),
b64e2537 Andy McCall ↶ 962
        ("NewComment", schemars::schema_for!(NewComment)),
963
        ("NewProposal", schemars::schema_for!(NewProposal)),
964
        ("NewReview", schemars::schema_for!(NewReview)),
965
        ("IssueStateChange", schemars::schema_for!(IssueStateChange)),
966
        ("WebhookEnvelope", schemars::schema_for!(WebhookEnvelope)),
967
        ("ProposalEvent", schemars::schema_for!(ProposalEvent)),
3f4b0ea6 Andy McCall ↶ 968
        ("Release", schemars::schema_for!(Release)),
bf1ab345 Andy McCall ↶ 969
        ("FlowRun", schemars::schema_for!(FlowRun)),
970
        ("FlowJob", schemars::schema_for!(FlowJob)),
971
        ("FlowLogLine", schemars::schema_for!(FlowLogLine)),
972
        ("AssetBuild", schemars::schema_for!(AssetBuild)),
3f4b0ea6 Andy McCall ↶ 973
        ("ReleaseAsset", schemars::schema_for!(ReleaseAsset)),
974
        ("NewRelease", schemars::schema_for!(NewRelease)),
b64e2537 Andy McCall ↶ 975
    ]
976
}
b66e70b7 Andy McCall ↶ 977
978
/// An OxygenFlow runner (`GET /api/v1/runners`, site administrators).
979
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
980
pub struct Runner {
981
    pub id: i64,
982
    pub name: String,
983
    /// `online`, `offline`, `draining` or `disabled`.
984
    pub status: String,
985
    /// What it serves: empty for the whole site, an owner, or `owner/name`.
986
    pub serves: String,
987
    pub arch: String,
988
    pub os: String,
989
    pub version: String,
990
    pub labels: Vec<String>,
991
    pub max_jobs: i64,
992
    pub memory_mib: Option<i64>,
993
    pub cpus: Option<i64>,
994
    /// The key that signs its receipts (KeyId hex), empty until bound.
995
    pub key: String,
996
    /// `registered` or `config` (a hub.toml token).
997
    pub source: String,
998
    pub last_seen_ms: Option<i64>,
999
    /// Jobs it is running now.
1000
    pub running: i64,
1001
}
1002
1003
/// A job a runner ran or is running.
1004
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1005
pub struct RunnerJob {
1006
    pub repo: String,
1007
    pub run: i64,
1008
    pub job: String,
1009
    pub status: String,
1010
    pub started_ms: Option<i64>,
1011
    pub finished_ms: Option<i64>,
1012
}
1013
1014
/// A job in the queue: running ones first, then queued ones in the order
1015
/// runners claim them.
1016
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1017
pub struct QueueItem {
1018
    pub repo: String,
1019
    pub run: i64,
1020
    pub job: String,
1021
    /// `running` or `queued`.
1022
    pub status: String,
1023
    /// The runner holding it, when running.
1024
    pub runner: String,
1025
}