/* * Author: Ruben Fiszel * Copyright: Windmill Labs, Inc 2022 * This file and its contents are licensed under the AGPLv3 License. * Please see the included NOTICE for copyright information and * LICENSE-AGPL for a copy of the license. */ use std::{ fmt::Display, hash::{Hash, Hasher}, }; use serde::de::Error as _; use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize}; use serde_json::to_string_pretty; use crate::utils::StripPath; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Hash)] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr( feature = "sqlx", sqlx(type_name = "SCRIPT_LANG", rename_all = "lowercase") )] #[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))] pub enum ScriptLang { Deno, Python3, Go, } impl ScriptLang { pub fn as_str(&self) -> &'static str { match self { ScriptLang::Deno => "deno", ScriptLang::Python3 => "python3", ScriptLang::Go => "go", } } } #[derive(PartialEq, Debug, Hash, Clone, Copy)] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr(feature = "sqlx", sqlx(transparent))] pub struct ScriptHash(pub i64); #[derive(PartialEq)] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr(feature = "sqlx", sqlx(transparent))] pub struct ScriptHashes(pub Vec); impl Display for ScriptHash { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", to_hex_string(&self.0)) } } impl Serialize for ScriptHash { fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, { serializer.serialize_str(to_hex_string(&self.0).as_str()) } } impl<'de> Deserialize<'de> for ScriptHash { fn deserialize(deserializer: D) -> std::result::Result where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; let i = to_i64(&s).map_err(|e| D::Error::custom(format!("{}", e)))?; Ok(ScriptHash(i)) } } impl Serialize for ScriptHashes { fn serialize(&self, serializer: S) -> std::result::Result where S: serde::Serializer, { let mut seq = serializer.serialize_seq(Some(self.0.len()))?; for element in &self.0 { seq.serialize_element(&ScriptHash(*element))?; } seq.end() } } #[derive(Serialize, Deserialize, Debug, Hash)] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr( feature = "sqlx", sqlx(type_name = "SCRIPT_KIND", rename_all = "lowercase") )] #[serde(rename_all = "lowercase")] pub enum ScriptKind { Trigger, Failure, Script, Approval, } #[derive(Serialize)] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] pub struct Script { pub workspace_id: String, pub hash: ScriptHash, pub path: String, pub parent_hashes: Option, pub summary: String, pub description: String, pub content: String, pub created_by: String, pub created_at: chrono::DateTime, pub archived: bool, pub schema: Option, pub deleted: bool, pub is_template: bool, pub extra_perms: serde_json::Value, pub lock: Option, pub lock_error_logs: Option, pub language: ScriptLang, pub kind: ScriptKind, } #[derive(Serialize, Deserialize, Debug)] #[cfg_attr(feature = "sqlx", derive(sqlx::Type))] #[cfg_attr(feature = "sqlx", sqlx)] #[cfg_attr(feature = "sqlx", sqlx(transparent))] #[serde(transparent)] pub struct Schema(pub serde_json::Value); impl Hash for Schema { fn hash(&self, state: &mut H) { if let Ok(s) = to_string_pretty(&self.0) { s.hash(state); } } } #[derive(Serialize, Deserialize, Hash)] pub struct NewScript { pub path: String, pub parent_hash: Option, pub summary: String, pub description: String, pub content: String, pub schema: Option, pub is_template: Option, pub lock: Option>, pub language: ScriptLang, pub kind: Option, } #[derive(Deserialize)] pub struct ListScriptQuery { pub path_start: Option, pub path_exact: Option, pub created_by: Option, pub first_parent_hash: Option, pub last_parent_hash: Option, pub parent_hash: Option, pub show_archived: Option, pub order_by: Option, pub order_desc: Option, pub is_template: Option, pub kind: Option, } pub fn to_i64(s: &str) -> crate::error::Result { let v = hex::decode(s)?; let nb: u64 = u64::from_be_bytes( v[0..8] .try_into() .map_err(|_| hex::FromHexError::InvalidStringLength)?, ); Ok(nb as i64) } pub fn to_hex_string(i: &i64) -> String { hex::encode(i.to_be_bytes()) } #[cfg(feature = "reqwest")] pub async fn get_hub_script_by_path( email: Option, username: String, path: StripPath, http_client: reqwest::Client, host: String, ) -> crate::error::Result { use crate::{ error::{to_anyhow, Error}, utils::http_get_from_hub, }; let path = path .to_path() .strip_prefix("hub/") .ok_or_else(|| Error::BadRequest("Impossible to remove prefix hex".to_string()))?; let content = http_get_from_hub( http_client, &format!("https://hub.windmill.dev/raw/{path}.ts"), email, username, host, true, ) .await? .text() .await .map_err(to_anyhow)?; Ok(content) } #[cfg(feature = "reqwest")] pub async fn get_full_hub_script_by_path( email: Option, username: String, path: StripPath, http_client: reqwest::Client, host: String, ) -> crate::error::Result { use crate::{ error::{to_anyhow, Error}, utils::http_get_from_hub, }; let path = path .to_path() .strip_prefix("hub/") .ok_or_else(|| Error::BadRequest("Impossible to remove prefix hex".to_string()))?; let value = http_get_from_hub( http_client, &format!("https://hub.windmill.dev/raw2/{path}"), email, username, host, true, ) .await? .json::() .await .map_err(to_anyhow)?; Ok(value) } #[derive(Deserialize, Serialize)] pub struct HubScript { pub content: String, pub lockfile: Option, pub language: ScriptLang, pub schema: serde_json::Value, pub summary: Option, }