Skip to main content

linkrs_wire/
migration.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema, utoipa::IntoParams)]
4pub struct MigrationPlanQuery {
5    pub from_version: Option<u64>,
6    pub to_version: Option<u64>,
7    pub is_edge: Option<bool>,
8    pub expand_contract: Option<bool>,
9}
10
11impl MigrationPlanQuery {
12    pub fn require_from_version(&self) -> Result<u64, String> {
13        self.from_version
14            .ok_or_else(|| "from_version required".to_string())
15    }
16    pub fn require_to_version(&self) -> Result<u64, String> {
17        self.to_version
18            .ok_or_else(|| "to_version required".to_string())
19    }
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
23pub struct MigrationExecuteRequest {
24    pub plan_json: String,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
28pub struct MigrationRollbackRequest {
29    pub plan_json: String,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
33pub struct MigrationPlanResponse {
34    pub plan: serde_json::Value,
35    pub plan_json: String,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
39pub struct MigrationExecuteResponse {
40    pub success: bool,
41    pub steps_completed: usize,
42    pub rows_migrated: u64,
43    pub errors: Vec<String>,
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub preview: Option<bool>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
49pub struct MigrationStatusResponse {
50    pub space: Option<String>,
51    pub label: Option<String>,
52    pub is_edge: Option<bool>,
53    pub latest_applied_version: Option<u64>,
54    pub applied_versions: Vec<u64>,
55    pub history_count: usize,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
59pub struct MigrationHistoryResponse {
60    pub space: String,
61    pub label: String,
62    pub is_edge: bool,
63    pub applied_versions: Vec<u64>,
64    pub history: Vec<serde_json::Value>,
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_dto_serde_roundtrip() {
73        let req = MigrationExecuteRequest {
74            plan_json: "{}".to_string(),
75        };
76        let json = serde_json::to_string(&req).unwrap();
77        let decoded: MigrationExecuteRequest = serde_json::from_str(&json).unwrap();
78        assert_eq!(decoded.plan_json, "{}");
79
80        let query = MigrationPlanQuery {
81            from_version: Some(1),
82            to_version: Some(2),
83            is_edge: Some(false),
84            expand_contract: Some(true),
85        };
86        let json = serde_json::to_string(&query).unwrap();
87        let decoded: MigrationPlanQuery = serde_json::from_str(&json).unwrap();
88        assert_eq!(decoded.from_version, Some(1));
89    }
90
91    #[test]
92    fn test_status_response() {
93        let resp = MigrationStatusResponse {
94            space: Some("s".into()),
95            label: Some("l".into()),
96            is_edge: Some(false),
97            latest_applied_version: Some(5),
98            applied_versions: vec![1, 2, 5],
99            history_count: 3,
100        };
101        let json = serde_json::to_string(&resp).unwrap();
102        let decoded: MigrationStatusResponse = serde_json::from_str(&json).unwrap();
103        assert_eq!(decoded.latest_applied_version, Some(5));
104    }
105}