Skip to main content

pipedash_plugin_api/
types.rs

1use std::collections::HashMap;
2
3use chrono::{
4    DateTime,
5    Utc,
6};
7use serde::{
8    Deserialize,
9    Serialize,
10};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "lowercase")]
14pub enum PipelineStatus {
15    Success,
16    Failed,
17    Degraded,
18    Running,
19    Pending,
20    Cancelled,
21    Skipped,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(rename_all = "lowercase")]
26pub enum ReviewDecision {
27    Approve,
28    Reject,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct AvailablePipeline {
33    pub id: String,
34    pub name: String,
35    pub description: Option<String>,
36    pub organization: Option<String>,
37    pub repository: Option<String>,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct ResourceState {
42    pub label: String,
43    #[serde(default, skip_serializing_if = "Option::is_none")]
44    pub message: Option<String>,
45}
46
47impl ResourceState {
48    pub fn new(label: impl Into<String>, message: Option<&str>) -> Self {
49        Self {
50            label: label.into(),
51            message: message
52                .map(str::trim)
53                .filter(|message| !message.is_empty())
54                .map(str::to_string),
55        }
56    }
57}
58
59pub const READ_ONLY_METADATA_KEY: &str = "read_only";
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Pipeline {
63    pub id: String,
64    pub provider_id: i64,
65    pub provider_type: String,
66    pub name: String,
67    pub status: PipelineStatus,
68    pub last_run: Option<DateTime<Utc>>,
69    pub last_updated: DateTime<Utc>,
70    pub repository: String,
71    pub branch: Option<String>,
72    pub workflow_file: Option<String>,
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub resource_state: Option<ResourceState>,
75    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
76    pub metadata: HashMap<String, serde_json::Value>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct PipelineRun {
81    pub id: String,
82    pub pipeline_id: String,
83    pub run_number: i64,
84    pub status: PipelineStatus,
85    pub started_at: DateTime<Utc>,
86    pub concluded_at: Option<DateTime<Utc>>,
87    pub duration_seconds: Option<i64>,
88    pub logs_url: String,
89    pub commit_sha: Option<String>,
90    pub commit_message: Option<String>,
91    pub branch: Option<String>,
92    pub actor: Option<String>,
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub inputs: Option<serde_json::Value>,
95    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
96    pub metadata: HashMap<String, serde_json::Value>,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct TriggerParams {
101    pub workflow_id: String,
102    pub inputs: Option<serde_json::Value>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct BuildAgent {
107    pub id: String,
108    pub name: String,
109    pub hostname: String,
110    pub status: String,
111    pub job_id: Option<String>,
112    pub last_seen: DateTime<Utc>,
113    pub metadata: HashMap<String, String>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct BuildQueue {
118    pub id: String,
119    pub waiting: usize,
120    pub running: usize,
121    pub avg_wait_time: Option<i64>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct BuildArtifact {
126    pub id: String,
127    pub run_id: String,
128    pub filename: String,
129    pub size_bytes: i64,
130    pub download_url: String,
131    pub content_type: Option<String>,
132    pub created_at: DateTime<Utc>,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct Organization {
137    pub id: String,
138    pub name: String,
139    pub description: Option<String>,
140}
141
142#[derive(Debug, Clone, Default, Serialize, Deserialize)]
143pub struct PipelineCatalog {
144    pub organizations: Vec<Organization>,
145    pub pipelines: Vec<AvailablePipeline>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149#[serde(tag = "type", rename_all = "lowercase")]
150pub enum WorkflowParameterType {
151    String {
152        #[serde(default)]
153        default: Option<String>,
154    },
155    Boolean {
156        #[serde(default)]
157        default: bool,
158    },
159    Choice {
160        options: Vec<String>,
161        #[serde(default)]
162        default: Option<String>,
163    },
164    Number {
165        #[serde(default)]
166        default: Option<f64>,
167    },
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct WorkflowParameter {
172    pub name: String,
173    pub label: Option<String>,
174    pub description: Option<String>,
175    #[serde(flatten)]
176    pub param_type: WorkflowParameterType,
177    #[serde(default)]
178    pub required: bool,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize)]
182pub struct PaginationParams {
183    pub page: usize,
184    pub page_size: usize,
185}
186
187impl PaginationParams {
188    pub fn validate(&self) -> Result<(), String> {
189        if self.page == 0 {
190            return Err("Page must be >= 1".to_string());
191        }
192        if self.page_size == 0 {
193            return Err("Page size must be >= 1".to_string());
194        }
195        if self.page_size > 200 {
196            return Err("Page size must be <= 200".to_string());
197        }
198        Ok(())
199    }
200
201    pub fn calculate_offset(&self) -> Result<usize, String> {
202        self.page
203            .checked_sub(1)
204            .and_then(|p| p.checked_mul(self.page_size))
205            .ok_or_else(|| "Pagination offset overflow".to_string())
206    }
207}
208
209impl Default for PaginationParams {
210    fn default() -> Self {
211        Self {
212            page: 1,
213            page_size: 100,
214        }
215    }
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct PaginatedResponse<T> {
220    pub items: Vec<T>,
221    pub page: usize,
222    pub page_size: usize,
223    pub total_count: usize,
224    pub total_pages: usize,
225    pub has_more: bool,
226}
227
228impl<T> PaginatedResponse<T> {
229    pub fn new(items: Vec<T>, page: usize, page_size: usize, total_count: usize) -> Self {
230        let items_count = items.len();
231        let total_pages = if page_size > 0 {
232            total_count.div_ceil(page_size)
233        } else {
234            1
235        };
236        let has_more = items_count == page_size;
237
238        Self {
239            items,
240            page,
241            page_size,
242            total_count,
243            total_pages,
244            has_more,
245        }
246    }
247
248    pub fn empty() -> Self {
249        Self {
250            items: Vec::new(),
251            page: 1,
252            page_size: 100,
253            total_count: 0,
254            total_pages: 0,
255            has_more: false,
256        }
257    }
258}
259
260pub type PaginatedAvailablePipelines = PaginatedResponse<AvailablePipeline>;
261
262#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
263pub struct Permission {
264    pub name: String,
265    pub description: String,
266    pub required: bool,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct PermissionCheck {
271    pub permission: Permission,
272    pub granted: bool,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct PermissionStatus {
277    pub permissions: Vec<PermissionCheck>,
278    pub all_granted: bool,
279    pub checked_at: DateTime<Utc>,
280    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
281    pub metadata: HashMap<String, String>,
282}
283
284#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
285pub struct RateLimit {
286    pub limit: u64,
287    pub remaining: u64,
288    pub reset_at: DateTime<Utc>,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct Feature {
293    pub id: String,
294    pub name: String,
295    pub description: String,
296    pub required_permissions: Vec<String>,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct FeatureAvailability {
301    pub feature: Feature,
302    pub available: bool,
303    pub missing_permissions: Vec<String>,
304}