diff --git a/backend/.sqlx/query-28837bb394c70e0bdbc269eb7cd3c457d176c09f626b5a98657089951bb50a08.json b/backend/.sqlx/query-28837bb394c70e0bdbc269eb7cd3c457d176c09f626b5a98657089951bb50a08.json new file mode 100644 index 0000000000..132e94e9dc --- /dev/null +++ b/backend/.sqlx/query-28837bb394c70e0bdbc269eb7cd3c457d176c09f626b5a98657089951bb50a08.json @@ -0,0 +1,78 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT c.id, created_at, created_by, status = 'success'::job_status AS \"success!\"\n FROM v2_job_completed c JOIN v2_job USING (id)\n WHERE (runnable_id = $1 OR runnable_path = $2) AND kind = ANY($3) AND c.workspace_id = $4\n ORDER BY created_at DESC LIMIT $5 OFFSET $6", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 2, + "name": "created_by", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "success!", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8", + "Text", + { + "Custom": { + "name": "job_kind[]", + "kind": { + "Array": { + "Custom": { + "name": "job_kind", + "kind": { + "Enum": [ + "script", + "preview", + "flow", + "dependencies", + "flowpreview", + "script_hub", + "identity", + "flowdependencies", + "http", + "graphql", + "postgresql", + "noop", + "appdependencies", + "deploymentcallback", + "singlescriptflow", + "flowscript", + "flownode", + "appscript" + ] + } + } + } + } + } + }, + "Text", + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + null + ] + }, + "hash": "28837bb394c70e0bdbc269eb7cd3c457d176c09f626b5a98657089951bb50a08" +} diff --git a/backend/.sqlx/query-3d953400c2456400a02d84c5163ecf9b6589dce38f9c39cb171a683d401dc34f.json b/backend/.sqlx/query-3d953400c2456400a02d84c5163ecf9b6589dce38f9c39cb171a683d401dc34f.json new file mode 100644 index 0000000000..cb57b02ca2 --- /dev/null +++ b/backend/.sqlx/query-3d953400c2456400a02d84c5163ecf9b6589dce38f9c39cb171a683d401dc34f.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE input SET name = $1, is_public = $2 WHERE id = $3 and workspace_id = $4", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Bool", + "Uuid", + "Text" + ] + }, + "nullable": [] + }, + "hash": "3d953400c2456400a02d84c5163ecf9b6589dce38f9c39cb171a683d401dc34f" +} diff --git a/backend/.sqlx/query-8cc3b1a58287b27d9ae11289b17b613161b60d5a78674d94e74e30bdcc9d33f3.json b/backend/.sqlx/query-8cc3b1a58287b27d9ae11289b17b613161b60d5a78674d94e74e30bdcc9d33f3.json new file mode 100644 index 0000000000..60fb0292c8 --- /dev/null +++ b/backend/.sqlx/query-8cc3b1a58287b27d9ae11289b17b613161b60d5a78674d94e74e30bdcc9d33f3.json @@ -0,0 +1,74 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, name, 'null'::JSONB AS args, created_by, created_at, is_public,\n TRUE AS \"success!\"\n FROM input\n WHERE runnable_id = $1 AND runnable_type = $2 AND workspace_id = $3\n AND (is_public IS TRUE OR created_by = $4)\n ORDER BY created_at DESC LIMIT $5 OFFSET $6", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "args", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "created_by", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "is_public", + "type_info": "Bool" + }, + { + "ordinal": 6, + "name": "success!", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "runnable_type", + "kind": { + "Enum": [ + "ScriptHash", + "ScriptPath", + "FlowPath" + ] + } + } + }, + "Text", + "Text", + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + null, + false, + false, + false, + null + ] + }, + "hash": "8cc3b1a58287b27d9ae11289b17b613161b60d5a78674d94e74e30bdcc9d33f3" +} diff --git a/backend/.sqlx/query-9085998ed693e15f18abbb9f0605003c1e9da868b60083f4dddfaa0f41b73c5c.json b/backend/.sqlx/query-9085998ed693e15f18abbb9f0605003c1e9da868b60083f4dddfaa0f41b73c5c.json new file mode 100644 index 0000000000..770803fd57 --- /dev/null +++ b/backend/.sqlx/query-9085998ed693e15f18abbb9f0605003c1e9da868b60083f4dddfaa0f41b73c5c.json @@ -0,0 +1,31 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO input (id, workspace_id, runnable_id, runnable_type, name, args, created_by)\n VALUES ($1, $2, $3, $4, $5, $6, $7)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Varchar", + "Varchar", + { + "Custom": { + "name": "runnable_type", + "kind": { + "Enum": [ + "ScriptHash", + "ScriptPath", + "FlowPath" + ] + } + } + }, + "Text", + "Jsonb", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "9085998ed693e15f18abbb9f0605003c1e9da868b60083f4dddfaa0f41b73c5c" +} diff --git a/backend/.sqlx/query-bf33348e36aec3d35d8a76424b024bcdb5eedb25b0472d026c65a775eec82da3.json b/backend/.sqlx/query-bf33348e36aec3d35d8a76424b024bcdb5eedb25b0472d026c65a775eec82da3.json new file mode 100644 index 0000000000..5e50586e95 --- /dev/null +++ b/backend/.sqlx/query-bf33348e36aec3d35d8a76424b024bcdb5eedb25b0472d026c65a775eec82da3.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM input WHERE id = $1 and workspace_id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [] + }, + "hash": "bf33348e36aec3d35d8a76424b024bcdb5eedb25b0472d026c65a775eec82da3" +} diff --git a/backend/windmill-api/src/inputs.rs b/backend/windmill-api/src/inputs.rs index 1ce3fa9226..fe7daa3fd4 100644 --- a/backend/windmill-api/src/inputs.rs +++ b/backend/windmill-api/src/inputs.rs @@ -15,11 +15,8 @@ use axum::{ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use serde_json::Value; -use sqlx::{types::Uuid, FromRow}; -use std::{ - fmt::{Display, Formatter}, - vec, -}; +use sqlx::types::Uuid; +use std::fmt::{Display, Formatter}; use windmill_common::{ db::UserDB, error::JsonResult, @@ -27,6 +24,7 @@ use windmill_common::{ scripts::to_i64, utils::{not_found_if_none, paginate, Pagination}, }; + pub fn workspaced_service() -> Router { Router::new() .route("/history", get(get_input_history)) @@ -40,22 +38,9 @@ pub fn workspaced_service() -> Router { ) } -#[derive(Debug, sqlx::FromRow, Serialize, Deserialize)] -pub struct InputRow { - pub id: Uuid, - pub workspace_id: String, - pub runnable_id: String, - pub runnable_type: RunnableType, - pub name: String, - pub args: sqlx::types::Json>, - pub created_at: DateTime, - pub created_by: String, - pub is_public: bool, -} - -#[derive(Debug, Serialize, Deserialize, sqlx::Type)] +#[derive(Debug, Serialize, Deserialize, sqlx::Type, Copy, Clone)] #[sqlx(type_name = "runnable_type")] -pub enum RunnableType { +enum RunnableType { ScriptHash, ScriptPath, FlowPath, @@ -71,50 +56,23 @@ impl Display for RunnableType { } } -impl RunnableType { - fn job_kind(&self) -> JobKind { - match self { - RunnableType::ScriptHash => JobKind::Script, - RunnableType::ScriptPath => JobKind::Script, - RunnableType::FlowPath => JobKind::Flow, - } - } - - fn column_name(&self) -> &'static str { - match self { - RunnableType::ScriptHash => "script_hash", - RunnableType::ScriptPath => "script_path", - RunnableType::FlowPath => "script_path", - } - } -} - #[derive(Debug, Serialize, Deserialize)] -pub struct RunnableParams { +struct RunnableParams { pub runnable_id: String, pub runnable_type: RunnableType, } #[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] -pub struct Input { +struct Input { id: Uuid, name: String, - created_at: chrono::DateTime, - args: sqlx::types::Json>, + created_at: DateTime, + args: Value, created_by: String, is_public: bool, success: bool, } -#[derive(Debug, Serialize, Deserialize, FromRow)] -pub struct CompletedJobMini { - id: Uuid, - created_at: chrono::DateTime, - args: Option>>, - created_by: String, - success: bool, -} - #[derive(Deserialize)] struct GetInputHistory { include_preview: Option, @@ -132,60 +90,43 @@ async fn get_input_history( let mut tx = user_db.begin(&authed).await?; - let sql = &format!( - "select id, created_at, created_by, 'null'::jsonb as args, success from v2_as_completed_job \ - where {} = $1 and job_kind = any($2) and workspace_id = $3 \ - order by created_at desc limit $4 offset $5", - r.runnable_type.column_name() - ); - - let query = sqlx::query_as::<_, CompletedJobMini>(sql); - - let query = match r.runnable_type { - RunnableType::ScriptHash => query.bind(to_i64(&r.runnable_id)?), - _ => query.bind(&r.runnable_id), + let job_kinds = match (r.runnable_type, g.include_preview.unwrap_or(false)) { + (RunnableType::FlowPath, true) => [JobKind::Flow, JobKind::FlowPreview].as_slice(), + (RunnableType::FlowPath, _) => [JobKind::Flow].as_slice(), + (_, true) => [JobKind::Script, JobKind::Preview].as_slice(), + _ => [JobKind::Script].as_slice(), }; - let job_kinds = match r.runnable_type.job_kind() { - kind @ JobKind::Script if g.include_preview.unwrap_or(false) => { - vec![kind, JobKind::Preview] - } - kind @ JobKind::Flow if g.include_preview.unwrap_or(false) => { - vec![kind, JobKind::FlowPreview] - } - kind => vec![kind], + let (runnable_id, runnable_path) = match r.runnable_type { + RunnableType::ScriptHash => (Some(to_i64(&r.runnable_id)?), None), + _ => (None, Some(&r.runnable_id)), }; - let rows = query - .bind(job_kinds) - .bind(&w_id) - .bind(per_page as i32) - .bind(offset as i32) - .fetch_all(&mut *tx) - .await?; + let inputs = sqlx::query!( + "SELECT c.id, created_at, created_by, status = 'success'::job_status AS \"success!\" + FROM v2_job_completed c JOIN v2_job USING (id) + WHERE (runnable_id = $1 OR runnable_path = $2) AND kind = ANY($3) AND c.workspace_id = $4 + ORDER BY created_at DESC LIMIT $5 OFFSET $6", + runnable_id, + runnable_path, + job_kinds as &[JobKind], + &w_id, + per_page as i32, + offset as i32 + ) + .map(|r| Input { + id: r.id, + name: format!("{} {}", r.created_at.format("%H:%M %-d/%-m"), r.created_by), + created_at: r.created_at, + args: Value::Null, + created_by: r.created_by, + is_public: true, + success: r.success, + }) + .fetch_all(&mut *tx) + .await?; tx.commit().await?; - - let mut inputs = vec![]; - - for row in rows { - inputs.push(Input { - id: row.id, - name: format!( - "{} {}", - row.created_at.format("%H:%M %-d/%-m"), - row.created_by - ), - created_at: row.created_at, - args: row.args.unwrap_or(sqlx::types::Json( - serde_json::value::RawValue::from_string("null".to_string()).unwrap(), - )), - created_by: row.created_by, - is_public: true, - success: row.success, - }); - } - Ok(Json(inputs)) } @@ -194,6 +135,7 @@ struct GetArgs { input: Option, allow_large: Option, } + async fn get_args_from_history_or_saved_input( authed: ApiAuthed, Extension(user_db): Extension, @@ -252,42 +194,31 @@ async fn list_saved_inputs( let mut tx = user_db.begin(&authed).await?; - let rows = sqlx::query_as::<_, InputRow>( - "select id, workspace_id, runnable_id, runnable_type, name, 'null'::jsonb as args, created_at, created_by, is_public from input \ - where runnable_id = $1 and runnable_type = $2 and workspace_id = $3 \ - and (is_public IS true OR created_by = $4) \ - order by created_at desc limit $5 offset $6", + let inputs = sqlx::query_as!( + Input, + "SELECT id, name, 'null'::JSONB AS args, created_by, created_at, is_public, + TRUE AS \"success!\" + FROM input + WHERE runnable_id = $1 AND runnable_type = $2 AND workspace_id = $3 + AND (is_public IS TRUE OR created_by = $4) + ORDER BY created_at DESC LIMIT $5 OFFSET $6", + &r.runnable_id, + &r.runnable_type as &RunnableType, + &w_id, + &authed.username, + per_page as i32, + offset as i32 ) - .bind(&r.runnable_id) - .bind(&r.runnable_type) - .bind(&w_id) - .bind(&authed.username) - .bind(per_page as i32) - .bind(offset as i32) .fetch_all(&mut *tx) .await?; tx.commit().await?; - let mut inputs: Vec = Vec::new(); - - for row in rows { - inputs.push(Input { - id: row.id, - name: row.name, - args: row.args, - created_by: row.created_by, - created_at: row.created_at, - is_public: row.is_public, - success: true, - }) - } - Ok(Json(inputs)) } #[derive(Debug, Serialize, Deserialize)] -pub struct CreateInput { +struct CreateInput { name: String, args: Box, } @@ -303,16 +234,17 @@ async fn create_input( let id = Uuid::new_v4(); - sqlx::query( - "INSERT INTO input (id, workspace_id, runnable_id, runnable_type, name, args, created_by) VALUES ($1, $2, $3, $4, $5, $6, $7)", + sqlx::query!( + "INSERT INTO input (id, workspace_id, runnable_id, runnable_type, name, args, created_by) + VALUES ($1, $2, $3, $4, $5, $6, $7)", + &id, + &w_id, + &r.runnable_id, + &r.runnable_type as &RunnableType, + &input.name, + sqlx::types::Json(&input.args) as sqlx::types::Json<&Box>, + &authed.username ) - .bind(&id) - .bind(&w_id) - .bind(&r.runnable_id) - .bind(&r.runnable_type) - .bind(&input.name) - .bind(sqlx::types::Json(&input.args)) - .bind(&authed.username) .execute(&mut *tx) .await?; @@ -322,7 +254,7 @@ async fn create_input( } #[derive(Debug, Serialize, Deserialize)] -pub struct UpdateInput { +struct UpdateInput { id: Uuid, name: String, is_public: bool, @@ -336,13 +268,15 @@ async fn update_input( ) -> JsonResult { let mut tx = user_db.begin(&authed).await?; - sqlx::query("UPDATE input SET name = $1, is_public = $2 WHERE id = $3 and workspace_id = $4") - .bind(&input.name) - .bind(&input.is_public) - .bind(&input.id) - .bind(&w_id) - .execute(&mut *tx) - .await?; + sqlx::query!( + "UPDATE input SET name = $1, is_public = $2 WHERE id = $3 and workspace_id = $4", + &input.name, + &input.is_public, + &input.id, + &w_id + ) + .execute(&mut *tx) + .await?; tx.commit().await?; @@ -356,11 +290,13 @@ async fn delete_input( ) -> JsonResult { let mut tx = user_db.begin(&authed).await?; - sqlx::query("DELETE FROM input WHERE id = $1 and workspace_id = $2") - .bind(&i_id) - .bind(&w_id) - .execute(&mut *tx) - .await?; + sqlx::query!( + "DELETE FROM input WHERE id = $1 and workspace_id = $2", + &i_id, + &w_id + ) + .execute(&mut *tx) + .await?; tx.commit().await?;