Skip to main content

origin_events/
platform.rs

1//! Events every Origin application shares.
2//!
3//! Products add their own enums (`GitHubEvent`, `AnalyticsEvent`, ...) and publish
4//! them on the same bus.
5
6use crate::bus::Event;
7use origin_domain::{AccountId, Alert, AlertId, ConnectorId, ErrorKind, JobId, JobStatus, SyncId};
8use serde::{Deserialize, Serialize};
9use time::OffsetDateTime;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
13pub struct SyncCompleted {
14    pub sync: SyncId,
15    pub connector: ConnectorId,
16    pub account: AccountId,
17    /// How many records changed. `0` means the service reported no change.
18    pub changed: u64,
19    #[serde(with = "time::serde::rfc3339")]
20    #[cfg_attr(feature = "ts", ts(type = "string"))]
21    pub at: OffsetDateTime,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
26pub struct SyncFailed {
27    pub sync: SyncId,
28    pub connector: ConnectorId,
29    pub account: AccountId,
30    pub kind: ErrorKind,
31    pub message: String,
32    /// When the platform intends to try again, if it does.
33    #[serde(with = "time::serde::rfc3339::option")]
34    #[cfg_attr(feature = "ts", ts(type = "string | null"))]
35    pub retry_at: Option<OffsetDateTime>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
40pub struct AlertRaised {
41    pub alert: Alert,
42    /// `true` when an alert with the same fingerprint was already active, so
43    /// notification sinks can stay quiet.
44    pub deduplicated: bool,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
49pub struct AlertResolved {
50    pub alert: AlertId,
51    #[serde(with = "time::serde::rfc3339")]
52    #[cfg_attr(feature = "ts", ts(type = "string"))]
53    pub at: OffsetDateTime,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
58pub struct AccountExpired {
59    pub account: AccountId,
60    pub connector: ConnectorId,
61}
62
63/// A product-provided tray menu item was selected (G10).
64///
65/// The host does not call product code: it publishes this, and the module that owns
66/// the menu reacts. `id` is exactly the string the product passed to
67/// `TrayService::set_menu`, so a renamed menu entry stays a compile-time concern on
68/// the product side.
69#[derive(Debug, Clone, Serialize, Deserialize)]
70#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
71pub struct TrayItemSelected {
72    pub id: String,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
77pub struct JobStarted {
78    pub job: JobId,
79    pub kind: String,
80}
81
82/// Progress of a running job.
83///
84/// Deliberately throttled by the job registry: a job that reports every one of ten
85/// thousand steps would flood the bus and make slow subscribers lag, losing the
86/// *finished* event they actually care about.
87#[derive(Debug, Clone, Serialize, Deserialize)]
88#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
89pub struct JobProgress {
90    pub job: JobId,
91    pub current: u64,
92    pub total: Option<u64>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
97pub struct JobFinished {
98    pub job: JobId,
99    pub kind: String,
100    pub status: JobStatus,
101    pub error: Option<String>,
102}
103
104/// The platform-level event enum.
105///
106/// Adding a variant is a breaking change for exhaustive subscribers — deliberately so.
107#[derive(Debug, Clone, Serialize, Deserialize)]
108#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
109#[serde(tag = "type", rename_all = "snake_case")]
110pub enum PlatformEvent {
111    SyncCompleted(SyncCompleted),
112    SyncFailed(SyncFailed),
113    AlertRaised(AlertRaised),
114    AlertResolved(AlertResolved),
115    AccountExpired(AccountExpired),
116    TrayItemSelected(TrayItemSelected),
117    JobStarted(JobStarted),
118    JobProgress(JobProgress),
119    JobFinished(JobFinished),
120}
121
122impl Event for PlatformEvent {
123    fn name(&self) -> &'static str {
124        match self {
125            Self::SyncCompleted(_) => "platform.sync.completed",
126            Self::SyncFailed(_) => "platform.sync.failed",
127            Self::AlertRaised(_) => "platform.alert.raised",
128            Self::AlertResolved(_) => "platform.alert.resolved",
129            Self::AccountExpired(_) => "platform.account.expired",
130            Self::TrayItemSelected(_) => "platform.tray.selected",
131            Self::JobStarted(_) => "platform.job.started",
132            Self::JobProgress(_) => "platform.job.progress",
133            Self::JobFinished(_) => "platform.job.finished",
134        }
135    }
136}