pipedash_plugin_api/
plugin.rs1use std::collections::HashMap;
2
3use async_trait::async_trait;
4use serde::{
5 Deserialize,
6 Serialize,
7};
8
9use crate::error::PluginResult;
10use crate::schema::{
11 ConfigSchema,
12 TableSchema,
13};
14use crate::types::*;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct PluginMetadata {
18 pub name: String,
19 pub provider_type: String,
20 pub version: String,
21 pub description: String,
22 pub author: Option<String>,
23 pub icon: Option<String>,
24 pub config_schema: ConfigSchema,
25 pub table_schema: TableSchema,
26 pub capabilities: PluginCapabilities,
27 #[serde(default)]
28 pub required_permissions: Vec<Permission>,
29 #[serde(default)]
30 pub features: Vec<Feature>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, Default)]
34pub struct PluginCapabilities {
35 pub pipelines: bool,
36 pub pipeline_runs: bool,
37 pub trigger: bool,
38 pub agents: bool,
39 pub artifacts: bool,
40 pub queues: bool,
41 pub custom_tables: bool,
42}
43
44#[async_trait]
45pub trait Plugin: Send + Sync {
46 fn metadata(&self) -> &PluginMetadata;
47
48 fn initialize(
49 &mut self, provider_id: i64, config: HashMap<String, String>,
50 http_client: Option<std::sync::Arc<reqwest::Client>>,
51 ) -> PluginResult<()>;
52
53 async fn validate_credentials(&self) -> PluginResult<bool>;
54
55 async fn fetch_available_pipelines(
56 &self, params: Option<crate::types::PaginationParams>,
57 ) -> PluginResult<crate::types::PaginatedAvailablePipelines> {
58 let _ = params;
59 Ok(crate::types::PaginatedResponse::empty())
60 }
61
62 async fn fetch_organizations(&self) -> PluginResult<Vec<crate::types::Organization>> {
63 Ok(Vec::new())
64 }
65
66 async fn fetch_available_pipelines_filtered(
67 &self, org: Option<String>, search: Option<String>,
68 params: Option<crate::types::PaginationParams>,
69 ) -> PluginResult<crate::types::PaginatedAvailablePipelines> {
70 let response = self.fetch_available_pipelines(params.clone()).await?;
71
72 let mut filtered_items = response.items;
73
74 if let Some(org_filter) = org {
75 filtered_items.retain(|p| p.organization.as_ref() == Some(&org_filter));
76 }
77
78 if let Some(search_term) = search {
79 let search_lower = search_term.to_lowercase();
80 filtered_items.retain(|p| {
81 p.name.to_lowercase().contains(&search_lower)
82 || p.id.to_lowercase().contains(&search_lower)
83 || p.description
84 .as_ref()
85 .is_some_and(|d| d.to_lowercase().contains(&search_lower))
86 });
87 }
88
89 let total_count = filtered_items.len();
90 let params = params.unwrap_or_default();
91
92 Ok(crate::types::PaginatedResponse::new(
93 filtered_items,
94 params.page,
95 params.page_size,
96 total_count,
97 ))
98 }
99
100 async fn fetch_pipelines(&self) -> PluginResult<Vec<Pipeline>>;
101
102 async fn fetch_pipelines_paginated(
103 &self, page: usize, page_size: usize,
104 ) -> PluginResult<crate::types::PaginatedResponse<Pipeline>> {
105 let all_pipelines = self.fetch_pipelines().await?;
106 let total_count = all_pipelines.len();
107 let start = (page - 1) * page_size;
108 let end = start + page_size;
109
110 let pipelines = if start < total_count {
111 all_pipelines[start..end.min(total_count)].to_vec()
112 } else {
113 Vec::new()
114 };
115
116 Ok(crate::types::PaginatedResponse::new(
117 pipelines,
118 page,
119 page_size,
120 total_count,
121 ))
122 }
123
124 async fn fetch_run_history(
125 &self, pipeline_id: &str, limit: usize,
126 ) -> PluginResult<Vec<PipelineRun>>;
127
128 async fn fetch_run_details(
129 &self, pipeline_id: &str, run_number: i64,
130 ) -> PluginResult<PipelineRun>;
131
132 async fn trigger_pipeline(&self, params: TriggerParams) -> PluginResult<String>;
133
134 async fn cancel_run(&self, _pipeline_id: &str, _run_number: i64) -> PluginResult<()> {
135 Err(crate::error::PluginError::NotSupported(
136 "Run cancellation not supported by this provider".to_string(),
137 ))
138 }
139
140 async fn fetch_workflow_parameters(
141 &self, _workflow_id: &str,
142 ) -> PluginResult<Vec<WorkflowParameter>> {
143 Ok(Vec::new())
144 }
145
146 async fn fetch_agents(&self) -> PluginResult<Vec<BuildAgent>> {
147 Err(crate::error::PluginError::NotSupported(
148 "Agent monitoring not supported by this provider".to_string(),
149 ))
150 }
151
152 async fn fetch_artifacts(&self, _run_id: &str) -> PluginResult<Vec<BuildArtifact>> {
153 Err(crate::error::PluginError::NotSupported(
154 "Artifact fetching not supported by this provider".to_string(),
155 ))
156 }
157
158 async fn fetch_queues(&self) -> PluginResult<Vec<BuildQueue>> {
159 Err(crate::error::PluginError::NotSupported(
160 "Queue monitoring not supported by this provider".to_string(),
161 ))
162 }
163
164 fn get_migrations(&self) -> Vec<String> {
165 Vec::new()
166 }
167
168 fn provider_type(&self) -> &str {
169 &self.metadata().provider_type
170 }
171
172 async fn get_field_options(
173 &self, _field_key: &str, _config: &HashMap<String, String>,
174 ) -> PluginResult<Vec<String>> {
175 Ok(Vec::new())
176 }
177
178 async fn check_permissions(&self) -> PluginResult<PermissionStatus> {
179 let required_permissions = &self.metadata().required_permissions;
180 let permissions = required_permissions
181 .iter()
182 .map(|p| PermissionCheck {
183 permission: p.clone(),
184 granted: true,
185 })
186 .collect();
187
188 Ok(PermissionStatus {
189 permissions,
190 all_granted: true,
191 checked_at: chrono::Utc::now(),
192 metadata: std::collections::HashMap::new(),
193 })
194 }
195
196 fn get_feature_availability(&self, status: &PermissionStatus) -> Vec<FeatureAvailability> {
197 let features = &self.metadata().features;
198 let granted_perms: std::collections::HashSet<String> = status
199 .permissions
200 .iter()
201 .filter(|p| p.granted)
202 .map(|p| p.permission.name.clone())
203 .collect();
204
205 features
206 .iter()
207 .map(|feature| {
208 let missing: Vec<String> = feature
209 .required_permissions
210 .iter()
211 .filter(|p| !granted_perms.contains(*p))
212 .cloned()
213 .collect();
214
215 FeatureAvailability {
216 feature: feature.clone(),
217 available: missing.is_empty(),
218 missing_permissions: missing,
219 }
220 })
221 .collect()
222 }
223}