Skip to main content

zlayer_types/api/
syncs.rs

1//! Sync DTOs.
2//!
3//! Wire types for the sync CRUD + diff/apply endpoints. A sync resource
4//! points at a directory within a project's git checkout that contains
5//! `ZLayer` resource YAMLs. The diff endpoint scans the directory and
6//! reports what would change; the apply endpoint actually reconciles.
7
8use serde::{Deserialize, Serialize};
9use utoipa::ToSchema;
10
11/// Body for `POST /api/v1/syncs`.
12#[derive(Debug, Serialize, Deserialize, ToSchema)]
13pub struct CreateSyncRequest {
14    /// Display name for this sync.
15    pub name: String,
16    /// Linked project id.
17    #[serde(default)]
18    pub project_id: Option<String>,
19    /// Path within the project's checkout to scan for resource YAMLs.
20    pub git_path: String,
21    /// Whether the sync should automatically apply on pull.
22    #[serde(default)]
23    pub auto_apply: Option<bool>,
24    /// Whether `apply` should delete resources on the API that are missing
25    /// from the manifest directory. Defaults to `false` (the safer choice).
26    #[serde(default)]
27    pub delete_missing: Option<bool>,
28    /// How often (in seconds) the continuous reconciliation controller should
29    /// re-apply this sync while `auto_apply` is enabled. `None` (the default)
30    /// uses the controller's configured default interval.
31    #[serde(default)]
32    pub reconcile_interval_secs: Option<u64>,
33}
34
35/// Result of reconciling a single resource during apply.
36#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
37pub struct SyncResourceResult {
38    /// The source manifest path (or remote resource name for deletions).
39    pub resource: String,
40    /// Resource kind: `"deployment"`, `"job"`, `"cron"`, or other.
41    pub kind: String,
42    /// Action taken: `"create"`, `"update"`, `"delete"`, or `"skip"`.
43    pub action: String,
44    /// Outcome status: `"ok"` or `"error"`.
45    pub status: String,
46    /// Optional error message (`status == "error"`) or skip reason
47    /// (`action == "skip"`). Omitted on successful `"ok"` results.
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub error: Option<String>,
50}
51
52impl SyncResourceResult {
53    /// Build a successful result for the given resource/kind/action.
54    #[must_use]
55    pub fn ok(resource: &str, kind: &str, action: &str) -> Self {
56        Self {
57            resource: resource.to_string(),
58            kind: kind.to_string(),
59            action: action.to_string(),
60            status: "ok".to_string(),
61            error: None,
62        }
63    }
64
65    /// Build an error result with a human-readable message.
66    #[must_use]
67    pub fn err(resource: &str, kind: &str, action: &str, message: String) -> Self {
68        Self {
69            resource: resource.to_string(),
70            kind: kind.to_string(),
71            action: action.to_string(),
72            status: "error".to_string(),
73            error: Some(message),
74        }
75    }
76
77    /// Build a skip result with a reason recorded in `error`.
78    #[must_use]
79    pub fn skip(resource: &str, kind: &str, message: String) -> Self {
80        Self {
81            resource: resource.to_string(),
82            kind: kind.to_string(),
83            action: "skip".to_string(),
84            status: "ok".to_string(),
85            error: Some(message),
86        }
87    }
88}
89
90/// Response for a real apply. Reports per-resource outcomes, the current
91/// commit SHA the sync was applied at (when known), and a short human-readable
92/// summary for CLI display.
93///
94/// NOTE: This is a breaking change from the previous dry-run-only
95/// `{ diff, message }` shape. Clients inspecting the apply response directly
96/// must be updated — callers that only consumed the HTTP status stay working.
97#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
98pub struct SyncApplyResponse {
99    /// Per-resource reconcile results.
100    pub results: Vec<SyncResourceResult>,
101    /// Commit SHA the sync was applied against (when resolvable).
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub applied_sha: Option<String>,
104    /// Aggregate summary suitable for CLI output, e.g.
105    /// `"3 created, 2 updated, 1 deleted, 0 skipped"`.
106    pub summary: String,
107}
108
109/// JSON-friendly wrapper around the sync diff output.
110#[derive(Debug, Serialize, Deserialize, ToSchema)]
111pub struct SyncDiffResponse {
112    /// Resources to create.
113    pub to_create: Vec<SyncResourceResponse>,
114    /// Resources to update.
115    pub to_update: Vec<SyncResourceResponse>,
116    /// Resource names to delete.
117    pub to_delete: Vec<String>,
118}
119
120/// A single resource in the diff output.
121#[derive(Debug, Serialize, Deserialize, ToSchema)]
122pub struct SyncResourceResponse {
123    /// Source file name.
124    pub file_path: String,
125    /// Resource kind.
126    pub kind: String,
127    /// Resource name.
128    pub name: String,
129}