Skip to main content

agent_deploy_contract/
runtime_repository.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{CursorPage, GitLabVisibility, OwnerRef};
4
5pub type RuntimeRepositoryRecordPage = CursorPage<RuntimeRepositoryRecord>;
6
7#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
8#[serde(rename_all = "snake_case")]
9pub enum RuntimeRepositoryStatus {
10    Importing,
11    #[default]
12    Ready,
13    Failed,
14}
15
16impl RuntimeRepositoryStatus {
17    pub fn as_str(self) -> &'static str {
18        match self {
19            Self::Importing => "importing",
20            Self::Ready => "ready",
21            Self::Failed => "failed",
22        }
23    }
24
25    pub fn parse(value: &str) -> Self {
26        match value.trim() {
27            "importing" => Self::Importing,
28            "failed" => Self::Failed,
29            _ => Self::Ready,
30        }
31    }
32
33    pub fn from_gitlab_import(import_status: Option<&str>, default_branch: Option<&str>) -> Self {
34        match import_status.map(str::trim).unwrap_or_default() {
35            "failed" => Self::Failed,
36            "scheduled" | "started" => Self::Importing,
37            "finished" => Self::Ready,
38            "none" | "" if default_branch.is_some() => Self::Ready,
39            _ => Self::Importing,
40        }
41    }
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
45#[serde(rename_all = "camelCase")]
46pub struct RuntimeRepositoryRecord {
47    pub id: String,
48    pub tenant_id: String,
49    pub project_id: String,
50    pub template_id: String,
51    pub owners: Vec<OwnerRef>,
52    pub namespace: String,
53    pub gitlab_project_id: u64,
54    pub path_with_namespace: String,
55    /// HTTPS clone URL derived from GitLab. Not an identity key; clients clone locally.
56    pub repository_url: String,
57    pub default_branch: Option<String>,
58    #[serde(default)]
59    pub status: RuntimeRepositoryStatus,
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub error_message: Option<String>,
62    pub version: i64,
63    pub created_at: String,
64    pub updated_at: String,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
68#[serde(rename_all = "camelCase", deny_unknown_fields)]
69pub struct CreateRuntimeRepositoryRequest {
70    pub template_id: String,
71    pub project_name: String,
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub project_path: Option<String>,
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub namespace: Option<String>,
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub description: Option<String>,
78    #[serde(default)]
79    pub visibility: GitLabVisibility,
80    #[serde(default)]
81    pub owners: Vec<OwnerRef>,
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn gitlab_import_status_maps_to_repository_status() {
90        assert_eq!(
91            RuntimeRepositoryStatus::from_gitlab_import(Some("started"), None),
92            RuntimeRepositoryStatus::Importing
93        );
94        assert_eq!(
95            RuntimeRepositoryStatus::from_gitlab_import(Some("finished"), Some("master")),
96            RuntimeRepositoryStatus::Ready
97        );
98        assert_eq!(
99            RuntimeRepositoryStatus::from_gitlab_import(Some("none"), Some("master")),
100            RuntimeRepositoryStatus::Ready
101        );
102        assert_eq!(
103            RuntimeRepositoryStatus::from_gitlab_import(Some("none"), None),
104            RuntimeRepositoryStatus::Importing
105        );
106        assert_eq!(
107            RuntimeRepositoryStatus::from_gitlab_import(Some("failed"), None),
108            RuntimeRepositoryStatus::Failed
109        );
110    }
111
112    #[test]
113    fn record_defaults_missing_status_to_ready() {
114        let record: RuntimeRepositoryRecord = serde_json::from_value(serde_json::json!({
115            "id": "rrepo-1",
116            "tenantId": "local",
117            "projectId": "default",
118            "templateId": "rtpl-1",
119            "owners": [],
120            "namespace": "runtimes",
121            "gitlabProjectId": 1,
122            "pathWithNamespace": "runtimes/demo",
123            "repositoryUrl": "https://git.test/runtimes/demo.git",
124            "defaultBranch": "master",
125            "version": 1,
126            "createdAt": "t",
127            "updatedAt": "t"
128        }))
129        .unwrap();
130        assert_eq!(record.status, RuntimeRepositoryStatus::Ready);
131        assert_eq!(record.error_message, None);
132    }
133}