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