progit_plugin_sdk/traits/integration.rs
1// SPDX-License-Identifier: LSL-1.0
2// Copyright (c) 2025 Markus Maiwald
3
4//! Integration plugin traits
5//!
6//! Traits for plugins that sync with external issue trackers
7//! (Jira, Linear, GitHub Issues, Notion, etc.)
8
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12use super::core::{Issue, Plugin, PluginResult};
13
14/// Result of syncing an issue to an external system
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct SyncResult {
17 /// Local ProGit issue ID
18 pub local_id: String,
19 /// ID in the external system
20 pub external_id: String,
21 /// URL to view in external system (optional)
22 pub external_url: Option<String>,
23 /// Status of this sync operation
24 pub status: SyncStatus,
25}
26
27/// Status of a sync operation
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
29pub enum SyncStatus {
30 /// Issue was created in external system
31 Created,
32 /// Issue was updated in external system
33 Updated,
34 /// Issue was unchanged (no sync needed)
35 Unchanged,
36 /// Sync failed with error message
37 Failed(String),
38 /// Issue was skipped (e.g., filtered out)
39 Skipped,
40}
41
42/// How to resolve conflicts between local and remote issues
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub enum ConflictResolution {
45 /// Use the local version
46 TakeLocal,
47 /// Use the remote version
48 TakeRemote,
49 /// Use a custom merged result
50 Merge(Issue),
51 /// Skip this issue (don't sync)
52 Skip,
53}
54
55/// Information about an integration plugin
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct IntegrationInfo {
58 /// Display name: "Jira", "Linear", "GitHub Issues", etc.
59 pub name: String,
60 /// ASCII/Unicode icon for UI display
61 pub icon: Option<String>,
62 /// Whether this integration supports bidirectional sync
63 pub supports_bidirectional: bool,
64 /// Whether this integration can receive webhooks
65 pub supports_webhooks: bool,
66 /// Authentication method required
67 pub auth_type: AuthType,
68 /// External system base URL (if configured)
69 pub base_url: Option<String>,
70}
71
72/// Authentication methods supported by integrations
73#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
74pub enum AuthType {
75 /// API token (e.g., Jira, Linear)
76 ApiToken,
77 /// OAuth 2.0 flow
78 OAuth2,
79 /// Basic HTTP auth (username/password)
80 BasicAuth,
81 /// No authentication required
82 None,
83}
84
85/// Link to an issue in an external system
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct ExternalLink {
88 /// Provider name: "jira", "linear", "github", etc.
89 pub provider: String,
90 /// ID in the external system
91 pub external_id: String,
92 /// URL to view in external system
93 pub external_url: Option<String>,
94 /// When this link was last synced
95 pub last_synced: String,
96 /// Current sync status
97 pub sync_status: String,
98}
99
100/// Configuration for field mapping between ProGit and external systems
101#[derive(Debug, Clone, Serialize, Deserialize, Default)]
102pub struct FieldMappings {
103 /// Status mappings: local -> external
104 pub status_outbound: HashMap<String, String>,
105 /// Status mappings: external -> local
106 pub status_inbound: HashMap<String, String>,
107 /// Tag/label mappings
108 pub tags: HashMap<String, String>,
109 /// Custom field mappings (external field ID -> local field)
110 pub custom_fields: HashMap<String, String>,
111}
112
113/// Trait for plugins that sync with external issue trackers
114pub trait IntegrationPlugin: Plugin {
115 /// Push issues to external system
116 ///
117 /// Called periodically or on-demand to sync issues TO the external system.
118 /// Returns list of sync results indicating what happened for each issue.
119 fn push_issues(&mut self, issues: &[Issue]) -> PluginResult<Vec<SyncResult>>;
120
121 /// Pull issues from external system
122 ///
123 /// Called to fetch issues FROM the external system.
124 /// Returns issues that should be merged into local storage.
125 fn pull_issues(&mut self) -> PluginResult<Vec<Issue>>;
126
127 /// Resolve conflict between local and remote versions
128 ///
129 /// Called when local and remote versions of an issue have diverged.
130 /// Default implementation uses timestamp-based resolution (newer wins).
131 fn resolve_conflict(
132 &mut self,
133 local: &Issue,
134 remote: &Issue,
135 ) -> PluginResult<ConflictResolution> {
136 // Default: remote wins if newer
137 Ok(if remote.updated > local.updated {
138 ConflictResolution::TakeRemote
139 } else {
140 ConflictResolution::TakeLocal
141 })
142 }
143
144 /// Map local status to external status
145 ///
146 /// Converts ProGit status (e.g., "in-progress") to external system
147 /// status (e.g., "In Progress" for Jira).
148 fn map_status_outbound(&self, status: &str) -> String;
149
150 /// Map external status to local status
151 ///
152 /// Converts external system status to ProGit status.
153 fn map_status_inbound(&self, external_status: &str) -> String;
154
155 /// Get integration metadata
156 ///
157 /// Returns information about this integration for UI display and
158 /// capability detection.
159 fn integration_info(&self) -> IntegrationInfo;
160
161 /// Handle incoming webhook payload
162 ///
163 /// Process a webhook from the external system. Returns issues that
164 /// were affected by the webhook.
165 fn handle_webhook(&mut self, _payload: &serde_json::Value) -> PluginResult<Vec<Issue>> {
166 // Default: no webhook support
167 Ok(vec![])
168 }
169
170 /// Get field mappings configuration
171 ///
172 /// Returns the current field mapping configuration for this integration.
173 fn field_mappings(&self) -> FieldMappings {
174 FieldMappings::default()
175 }
176
177 /// Validate connection to external system
178 ///
179 /// Tests that the integration can connect to the external system
180 /// with the current configuration.
181 fn validate_connection(&mut self) -> PluginResult<bool> {
182 // Default: assume connected
183 Ok(true)
184 }
185}