use async_timing_util::unix_timestamp_ms;
use serde::{Deserialize, Serialize};
use strum::{AsRefStr, Display, EnumString};
use typeshare::typeshare;
use crate::entities::{
I64, MongoId, Operation, all_logs_success, komodo_timestamp,
};
use super::{ResourceTarget, Version};
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(
feature = "mongo",
derive(mongo_indexed::derive::MongoIndexed)
)]
#[cfg_attr(feature = "mongo", doc_index({ "target.type": 1 }))]
#[cfg_attr(feature = "mongo", sparse_doc_index({ "target.id": 1 }))]
pub struct Update {
#[serde(
default,
rename = "_id",
skip_serializing_if = "String::is_empty",
with = "bson::serde_helpers::hex_string_as_object_id"
)]
pub id: MongoId,
#[cfg_attr(feature = "mongo", index)]
pub operation: Operation,
#[cfg_attr(feature = "mongo", index)]
pub start_ts: I64,
#[cfg_attr(feature = "mongo", index)]
pub success: bool,
#[cfg_attr(feature = "mongo", index)]
pub operator: String,
pub target: ResourceTarget,
pub logs: Vec<Log>,
pub end_ts: Option<I64>,
#[cfg_attr(feature = "mongo", index)]
pub status: UpdateStatus,
#[serde(default, skip_serializing_if = "Version::is_none")]
pub version: Version,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub commit_hash: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub other_data: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub prev_toml: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub current_toml: String,
}
impl Update {
pub fn push_simple_log(
&mut self,
stage: &str,
msg: impl Into<String>,
) {
self.logs.push(Log::simple(stage, msg.into()));
}
pub fn push_error_log(
&mut self,
stage: &str,
msg: impl Into<String>,
) {
self.logs.push(Log::error(stage, msg.into()));
}
pub fn in_progress(&mut self) {
self.status = UpdateStatus::InProgress;
}
pub fn finalize(&mut self) {
self.success = all_logs_success(&self.logs);
self.end_ts = Some(komodo_timestamp());
self.status = UpdateStatus::Complete;
}
}
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct UpdateListItem {
pub id: String,
pub operation: Operation,
pub start_ts: I64,
pub success: bool,
pub username: String,
pub operator: String,
pub target: ResourceTarget,
pub status: UpdateStatus,
#[serde(default, skip_serializing_if = "Version::is_none")]
pub version: Version,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub other_data: String,
}
#[typeshare]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct Log {
pub stage: String,
pub command: String,
pub stdout: String,
pub stderr: String,
pub success: bool,
pub start_ts: I64,
pub end_ts: I64,
}
impl Log {
pub fn simple(stage: &str, msg: String) -> Log {
let ts = unix_timestamp_ms() as i64;
Log {
stage: stage.to_string(),
stdout: msg,
success: true,
start_ts: ts,
end_ts: ts,
..Default::default()
}
}
pub fn error(stage: &str, msg: String) -> Log {
let ts = unix_timestamp_ms() as i64;
Log {
stage: stage.to_string(),
stderr: msg,
start_ts: ts,
end_ts: ts,
success: false,
..Default::default()
}
}
pub fn combined(&self) -> String {
match (self.stdout.is_empty(), self.stderr.is_empty()) {
(false, false) => {
format!("stdout: {}\n\nstderr: {}", self.stdout, self.stderr)
}
(false, true) => self.stdout.to_string(),
(true, false) => self.stderr.to_string(),
(true, true) => String::from("No log"),
}
}
#[cfg(feature = "svi")]
pub fn sanitize(&mut self, replacers: &Vec<(String, String)>) {
self.command = svi::replace_in_string(&self.command, replacers);
self.stdout = svi::replace_in_string(&self.stdout, replacers);
self.stderr = svi::replace_in_string(&self.stderr, replacers);
}
}
#[typeshare]
#[derive(
Default,
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize,
Display,
EnumString,
AsRefStr,
)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub enum UpdateStatus {
Queued,
InProgress,
#[default]
Complete,
}