Skip to main content

ironflow_api/entities/
signal.rs

1//! Signal request and response DTOs.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use uuid::Uuid;
7
8use ironflow_engine::signal::{SignalDelivery, SignalRejected, SignalResumed};
9use ironflow_store::entities::Signal;
10
11/// Send signal request body.
12#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
13#[derive(Debug, Deserialize)]
14pub struct SendSignalRequest {
15    /// Signal name, e.g. `"ci.pipeline_finished"` (1-255 characters).
16    pub name: String,
17    /// Occurrence key, e.g. a commit SHA (1-255 characters).
18    pub key: String,
19    /// JSON payload, validated against the schema of each waiting step.
20    #[cfg_attr(feature = "openapi", schema(value_type = Object))]
21    pub payload: Value,
22    /// Deduplication ID (1-255 characters): a second signal with the same ID
23    /// is neither stored nor delivered.
24    pub idempotency_id: Option<String>,
25}
26
27/// A run resumed by a signal.
28#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
29#[derive(Debug, Serialize)]
30pub struct ResumedRunResponse {
31    /// Run whose waiting step the signal resolved.
32    pub run_id: Uuid,
33    /// The resolved signal step.
34    pub step_id: Uuid,
35}
36
37impl From<SignalResumed> for ResumedRunResponse {
38    fn from(r: SignalResumed) -> Self {
39        Self {
40            run_id: r.run_id,
41            step_id: r.step_id,
42        }
43    }
44}
45
46/// A waiting run whose payload schema the signal did not match.
47#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
48#[derive(Debug, Serialize)]
49pub struct RejectedRunResponse {
50    /// Run that keeps waiting.
51    pub run_id: Uuid,
52    /// The signal step that keeps waiting.
53    pub step_id: Uuid,
54    /// Why the payload was refused.
55    pub error: String,
56}
57
58impl From<SignalRejected> for RejectedRunResponse {
59    fn from(r: SignalRejected) -> Self {
60        Self {
61            run_id: r.run_id,
62            step_id: r.step_id,
63            error: r.error,
64        }
65    }
66}
67
68/// Result of sending a signal.
69#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
70#[derive(Debug, Serialize)]
71pub struct SignalDeliveryResponse {
72    /// ID of the stored signal (the pre-existing one on a duplicate).
73    pub signal_id: Uuid,
74    /// `true` when the idempotency ID was already used: nothing was delivered.
75    pub duplicate: bool,
76    /// Runs the signal resumed.
77    pub resumed: Vec<ResumedRunResponse>,
78    /// Waiting runs that rejected the payload and keep waiting.
79    pub rejected: Vec<RejectedRunResponse>,
80}
81
82impl From<SignalDelivery> for SignalDeliveryResponse {
83    fn from(d: SignalDelivery) -> Self {
84        Self {
85            signal_id: d.signal_id,
86            duplicate: d.duplicate,
87            resumed: d
88                .resumed
89                .into_iter()
90                .map(ResumedRunResponse::from)
91                .collect(),
92            rejected: d
93                .rejected
94                .into_iter()
95                .map(RejectedRunResponse::from)
96                .collect(),
97        }
98    }
99}
100
101/// A stored signal.
102#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
103#[derive(Debug, Serialize)]
104pub struct SignalResponse {
105    /// Signal ID.
106    pub id: Uuid,
107    /// Signal name.
108    pub name: String,
109    /// Occurrence key.
110    pub key: String,
111    /// JSON payload.
112    #[cfg_attr(feature = "openapi", schema(value_type = Object))]
113    pub payload: Value,
114    /// Deduplication ID given by the sender.
115    pub idempotency_id: Option<String>,
116    /// When the signal was received.
117    pub received_at: DateTime<Utc>,
118}
119
120impl From<Signal> for SignalResponse {
121    fn from(s: Signal) -> Self {
122        Self {
123            id: s.id,
124            name: s.name,
125            key: s.key,
126            payload: s.payload,
127            idempotency_id: s.idempotency_id,
128            received_at: s.received_at,
129        }
130    }
131}
132
133/// Query parameters for listing signals.
134#[cfg_attr(feature = "openapi", derive(utoipa::IntoParams, utoipa::ToSchema))]
135#[derive(Debug, Deserialize)]
136pub struct ListSignalsQuery {
137    /// Only signals with this exact name.
138    pub name: Option<String>,
139    /// Only signals with this exact key.
140    pub key: Option<String>,
141    /// Page number (1-based, defaults to 1).
142    pub page: Option<u32>,
143    /// Items per page (defaults to 20, max 100).
144    pub per_page: Option<u32>,
145}