1use std::collections::BTreeMap;
7use std::path::PathBuf;
8
9use schemars::JsonSchema;
10use serde::Serialize;
11
12use crate::codes::{Code, ErrorCode, Kind};
13use crate::settings::Settings;
14
15type JsonMap = serde_json::Map<String, serde_json::Value>;
16
17pub const SCHEMA_VERSION: &str = "dre/run-results/v1";
19
20#[derive(Debug, Clone, Serialize, JsonSchema)]
22#[schemars(title = "DRE run results")]
23pub struct RunResults {
24 #[schemars(schema_with = "schema_version")]
26 pub schema_version: &'static str,
27 pub run_id: String,
29 pub report: String,
30 pub set: Option<String>,
32 pub binding: String,
34 pub managed: bool,
36 pub profile: Option<String>,
38 pub connections: Vec<String>,
40 pub target: String,
42 pub schedule: Option<String>,
44 pub schedule_vars: Option<JsonMap>,
46 pub vars: JsonMap,
48 pub run_date: String,
50 pub scheduled_at: Option<String>,
52 pub timezone: String,
54 pub params: Params,
56 pub status: Status,
57 pub error: Option<String>,
59 pub error_code: Option<ErrorCode>,
61 pub error_kind: Option<Kind>,
63 pub preview: bool,
65 pub row_limit: Option<u64>,
67 pub started_at: String,
69 pub duration_ms: u64,
70 pub result_sets: Vec<ResultSet>,
72 pub outputs: Vec<OutputFile>,
74 pub output_results: Vec<OutputResult>,
76 pub delivery: Option<String>,
78 pub deliveries: Vec<Delivery>,
80 pub schema_drift: Vec<String>,
82 pub target_path: PathBuf,
84 #[schemars(schema_with = "settings")]
86 pub settings: Settings,
87 pub manifest_checksum: Option<String>,
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub retry_of: Option<String>,
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub release_of: Option<String>,
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub released_by: Option<String>,
98 #[serde(skip_serializing_if = "Vec::is_empty")]
100 pub released: Vec<Released>,
101}
102
103#[derive(Debug, Clone, Serialize, serde::Deserialize, JsonSchema)]
105pub struct Released {
106 pub id: String,
108 pub output: Option<String>,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub key: Option<String>,
112 pub profile: String,
113 pub status: String,
115 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub location: Option<String>,
117 #[serde(default, skip_serializing_if = "Option::is_none")]
118 pub error: Option<String>,
119}
120
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, JsonSchema)]
123#[serde(rename_all = "snake_case")]
124pub enum Status {
125 Success,
126 Error,
127 DryRun,
129 Checked,
131 Cancelled,
133 TimedOut,
135}
136
137#[derive(Debug, Clone, Serialize, JsonSchema)]
139pub struct Params {
140 pub selector: Option<String>,
141 pub set: Option<String>,
142 pub schedule: Option<String>,
143 pub target: Option<String>,
145 pub profile: Option<String>,
147 pub vars: BTreeMap<String, String>,
149 pub run_date: String,
150 pub scheduled_at: Option<String>,
151 pub timezone: Option<String>,
153 pub output_name: Option<String>,
154 pub output_path: Option<String>,
155 pub dry_run: bool,
156 pub preview: Option<u64>,
157 pub accept_schema_change: bool,
158}
159
160#[derive(Debug, Clone, Serialize, JsonSchema)]
162pub struct ResultSet {
163 pub name: String,
165 pub query: String,
166 pub connection: String,
168 pub rows: u64,
169 pub columns: Vec<String>,
170}
171
172#[derive(Debug, Clone, Serialize, JsonSchema)]
174pub struct OutputFile {
175 pub path: PathBuf,
177 pub size: u64,
178 pub delivered_to: Option<String>,
180 pub output: Option<String>,
182}
183
184#[derive(Debug, Clone, Serialize, JsonSchema)]
186pub struct OutputResult {
187 pub name: Option<String>,
188 pub format: String,
189 pub queries: Vec<String>,
191 #[schemars(schema_with = "output_status")]
192 pub status: &'static str,
193 pub error: Option<String>,
194 pub files: Vec<OutputFileRef>,
195 pub delivery: Option<String>,
196 pub deliveries: Vec<Delivery>,
197 #[serde(skip_serializing_if = "Option::is_none")]
199 pub when: Option<bool>,
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub reason: Option<String>,
203 #[serde(skip_serializing_if = "Vec::is_empty")]
205 pub no_data: Vec<String>,
206 #[serde(skip_serializing_if = "Vec::is_empty")]
208 pub dropped: Vec<String>,
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub for_each: Option<ForEachResult>,
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub message: Option<Message>,
215}
216
217#[derive(Debug, Clone, Default, Serialize, serde::Deserialize, JsonSchema)]
220pub struct ForEachResult {
221 pub query: String,
223 pub key: Vec<String>,
225 pub rows: u64,
227 pub succeeded: u64,
228 pub skipped: u64,
230 pub failed: u64,
231 pub held: u64,
233 #[serde(default)]
235 pub not_started: u64,
236 pub orphan_rows: u64,
238 pub failed_keys: Vec<FailedKey>,
240 pub sidecar: PathBuf,
242 #[serde(default)]
244 pub partial: bool,
245}
246
247#[derive(Debug, Clone, Serialize, serde::Deserialize, JsonSchema)]
249pub struct FailedKey {
250 pub key: String,
251 pub code: String,
252 pub message: String,
253}
254
255#[derive(Debug, Clone, Serialize, serde::Deserialize, JsonSchema)]
257pub struct RowRecord {
258 pub key: String,
259 pub status: String,
261 #[serde(default, skip_serializing_if = "Option::is_none")]
263 pub reason: Option<String>,
264 #[serde(default)]
266 pub files: Vec<RowFile>,
267 #[serde(default)]
269 pub deliveries: Vec<RowDelivery>,
270 #[serde(default, skip_serializing_if = "Option::is_none")]
271 pub error: Option<FailedKey>,
272}
273
274#[derive(Debug, Clone, Serialize, serde::Deserialize, JsonSchema)]
276pub struct RowFile {
277 pub path: PathBuf,
278 pub sha256: String,
279}
280
281#[derive(Debug, Clone, Serialize, serde::Deserialize, JsonSchema)]
283pub struct RowDelivery {
284 pub profile: String,
285 #[serde(default, skip_serializing_if = "Option::is_none")]
287 pub target: Option<String>,
288 pub status: String,
290 #[serde(default, skip_serializing_if = "Option::is_none")]
291 pub location: Option<String>,
292 #[serde(default, skip_serializing_if = "Option::is_none")]
293 pub error: Option<String>,
294}
295
296#[derive(Debug, Clone, Serialize, JsonSchema)]
298pub struct OutputFileRef {
299 pub path: PathBuf,
300 pub size: u64,
301 pub delivered_to: Option<String>,
302}
303
304#[derive(Debug, Clone, Serialize, JsonSchema)]
306pub struct Message {
307 pub title: String,
308 pub text: String,
309}
310
311#[derive(Debug, Clone, Serialize, JsonSchema)]
313pub struct Delivery {
314 pub profile: String,
316 #[serde(rename = "type")]
318 pub kind: Option<String>,
319 pub target: String,
321 #[schemars(schema_with = "delivery_status")]
322 pub status: &'static str,
323 #[serde(skip_serializing_if = "Option::is_none")]
325 pub location: Option<String>,
326 #[serde(skip_serializing_if = "Option::is_none")]
328 pub attempts: Option<u32>,
329 #[serde(skip_serializing_if = "Option::is_none")]
330 pub error: Option<String>,
331 #[serde(skip_serializing_if = "Option::is_none")]
332 pub error_code: Option<ErrorCode>,
333}
334
335fn schema_version(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
336 schemars::json_schema!({"const": SCHEMA_VERSION})
337}
338
339fn output_status(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
340 schemars::json_schema!({"enum": ["delivered", "kept", "skipped", "failed", "held"]})
341}
342
343fn delivery_status(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
344 schemars::json_schema!({"enum": ["delivered", "not_delivered", "failed", "held"]})
345}
346
347fn settings(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
348 schemars::json_schema!({
349 "type": "array",
350 "items": {
351 "type": "object",
352 "properties": {
353 "name": {"type": "string"},
354 "value": {"type": ["string", "null"]},
355 "source": {"type": "string", "description": "A flag (`--target`), an environment variable (`DRE_TARGET`), a dre_project.yml key, how it was found, or `default`."}
356 },
357 "required": ["name", "value", "source"]
358 },
359 "x-dre-null": true
360 })
361}
362
363impl JsonSchema for Code {
364 fn schema_name() -> std::borrow::Cow<'static, str> {
365 "Code".into()
366 }
367 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
368 schemars::json_schema!({"type": "string", "description": "A code from the error codes reference, e.g. `query-failed`."})
369 }
370 fn inline_schema() -> bool {
371 true
372 }
373}
374
375impl JsonSchema for ErrorCode {
376 fn schema_name() -> std::borrow::Cow<'static, str> {
377 "ErrorCode".into()
378 }
379 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
380 schemars::json_schema!({"type": "string", "description": "A code from the error codes reference, e.g. `query-failed`, or a plugin's own, namespaced by the plugin, e.g. `sftp/host-key-mismatch`."})
381 }
382 fn inline_schema() -> bool {
383 true
384 }
385}
386
387impl JsonSchema for Kind {
388 fn schema_name() -> std::borrow::Cow<'static, str> {
389 "Kind".into()
390 }
391 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
392 schemars::json_schema!({"enum": ["usage", "config", "plugin", "refused", "connection", "auth", "query", "delivery", "internal", "cancelled", "timed_out"]})
393 }
394 fn inline_schema() -> bool {
395 true
396 }
397}