diff --git a/backend/.sqlx/query-023fffd0a042a28b5be991169a506aff92f64f84e49b4c041cd369b045c31e73.json b/backend/.sqlx/query-023fffd0a042a28b5be991169a506aff92f64f84e49b4c041cd369b045c31e73.json deleted file mode 100644 index a89034ef8d..0000000000 --- a/backend/.sqlx/query-023fffd0a042a28b5be991169a506aff92f64f84e49b4c041cd369b045c31e73.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT app.id, app.path, app.summary, app.versions, app.policy,\n app.extra_perms, app_version.value, \n app_version.created_at, app_version.created_by from app, app_version \n WHERE app.workspace_id = $1 AND app_version.id = app.versions[array_upper(app.versions, 1)]", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "versions", - "type_info": "Int8Array" - }, - { - "ordinal": 4, - "name": "policy", - "type_info": "Jsonb" - }, - { - "ordinal": 5, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 6, - "name": "value", - "type_info": "Json" - }, - { - "ordinal": 7, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 8, - "name": "created_by", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "023fffd0a042a28b5be991169a506aff92f64f84e49b4c041cd369b045c31e73" -} diff --git a/backend/.sqlx/query-438fb925ee90d5115bd3c3be8ae48b56ba86017af3ca519bd3a15829edaa7d1b.json b/backend/.sqlx/query-438fb925ee90d5115bd3c3be8ae48b56ba86017af3ca519bd3a15829edaa7d1b.json deleted file mode 100644 index 9f6fa3df9a..0000000000 --- a/backend/.sqlx/query-438fb925ee90d5115bd3c3be8ae48b56ba86017af3ca519bd3a15829edaa7d1b.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT app.id, app.path, app.summary, app.versions, app.policy,\n app.extra_perms, app_version.value, \n app_version.created_at, app_version.created_by from app, app_version \n WHERE app_version.id = $1 AND app.id = app_version.app_id AND app.workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "versions", - "type_info": "Int8Array" - }, - { - "ordinal": 4, - "name": "policy", - "type_info": "Jsonb" - }, - { - "ordinal": 5, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 6, - "name": "value", - "type_info": "Json" - }, - { - "ordinal": 7, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 8, - "name": "created_by", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int8", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "438fb925ee90d5115bd3c3be8ae48b56ba86017af3ca519bd3a15829edaa7d1b" -} diff --git a/backend/.sqlx/query-4a3917318d967d96c4b6e70786a034302f04da9d1e9eec59b1eb6052c2d947be.json b/backend/.sqlx/query-4a3917318d967d96c4b6e70786a034302f04da9d1e9eec59b1eb6052c2d947be.json deleted file mode 100644 index b8df2cb5b8..0000000000 --- a/backend/.sqlx/query-4a3917318d967d96c4b6e70786a034302f04da9d1e9eec59b1eb6052c2d947be.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT success, result, started_at FROM completed_job WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4 ORDER BY created_at DESC LIMIT $5", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "success", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "result", - "type_info": "Jsonb" - }, - { - "ordinal": 2, - "name": "started_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text", - "Text", - "Text", - "Uuid", - "Int8" - ] - }, - "nullable": [ - false, - true, - false - ] - }, - "hash": "4a3917318d967d96c4b6e70786a034302f04da9d1e9eec59b1eb6052c2d947be" -} diff --git a/backend/.sqlx/query-597335a4a1eda7799303e0d930b5468af2364f5d2075aab48787b93e5774336d.json b/backend/.sqlx/query-597335a4a1eda7799303e0d930b5468af2364f5d2075aab48787b93e5774336d.json deleted file mode 100644 index bfb297e1fd..0000000000 --- a/backend/.sqlx/query-597335a4a1eda7799303e0d930b5468af2364f5d2075aab48787b93e5774336d.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO schedule (workspace_id, path, schedule, timezone, edited_by, script_path, is_flow, args, enabled, email, on_failure, on_failure_times, on_failure_exact, on_failure_extra_args, on_recovery, on_recovery_times, on_recovery_extra_args, ws_error_handler_muted, retry, summary, no_flow_overlap, tag ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22 ) RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Bool", - "Jsonb", - "Bool", - "Varchar", - "Varchar", - "Int4", - "Bool", - "Json", - "Varchar", - "Int4", - "Json", - "Bool", - "Jsonb", - "Varchar", - "Bool", - "Varchar" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "597335a4a1eda7799303e0d930b5468af2364f5d2075aab48787b93e5774336d" -} diff --git a/backend/.sqlx/query-644335d376b6554ab222d25dfa8722234661f2456b469f21b175e8607584614e.json b/backend/.sqlx/query-644335d376b6554ab222d25dfa8722234661f2456b469f21b175e8607584614e.json deleted file mode 100644 index a1e4915e40..0000000000 --- a/backend/.sqlx/query-644335d376b6554ab222d25dfa8722234661f2456b469f21b175e8607584614e.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT path, app_version.value from app LEFT JOIN app_version ON app_version.id = versions[array_upper(versions, 1)] WHERE workspace_id = $1 LIMIT $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "value", - "type_info": "Json" - } - ], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [ - false, - false - ] - }, - "hash": "644335d376b6554ab222d25dfa8722234661f2456b469f21b175e8607584614e" -} diff --git a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json index 1fa370e682..75b8108281 100644 --- a/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json +++ b/backend/.sqlx/query-661f472ff3860983322162420457f5033b9c9afc344d9c3e385ba20a3ad2197a.json @@ -5,7 +5,7 @@ "columns": [ { "ordinal": 0, - "name": "bool", + "name": "?column?", "type_info": "Bool" } ], diff --git a/backend/.sqlx/query-90435687152ccd37db7e0a6a5baeb558db50f5f96b1e8eff0b10e4398566f325.json b/backend/.sqlx/query-90435687152ccd37db7e0a6a5baeb558db50f5f96b1e8eff0b10e4398566f325.json deleted file mode 100644 index a707e0d89e..0000000000 --- a/backend/.sqlx/query-90435687152ccd37db7e0a6a5baeb558db50f5f96b1e8eff0b10e4398566f325.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE schedule SET schedule = $1, timezone = $2, args = $3, on_failure = $4, on_failure_times = $5, on_failure_exact = $6, on_failure_extra_args = $7, on_recovery = $8, on_recovery_times = $9, on_recovery_extra_args = $10, ws_error_handler_muted = $11, retry = $12, summary = $13, no_flow_overlap = $14, tag = $15\n WHERE path = $16 AND workspace_id = $17 RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Jsonb", - "Varchar", - "Int4", - "Bool", - "Json", - "Varchar", - "Int4", - "Json", - "Bool", - "Jsonb", - "Varchar", - "Bool", - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "90435687152ccd37db7e0a6a5baeb558db50f5f96b1e8eff0b10e4398566f325" -} diff --git a/backend/.sqlx/query-911b1e1f2a5ba6d5159916e5598020e680c45043b0736ad0153ee261a151dd90.json b/backend/.sqlx/query-911b1e1f2a5ba6d5159916e5598020e680c45043b0736ad0153ee261a151dd90.json deleted file mode 100644 index 1447148a44..0000000000 --- a/backend/.sqlx/query-911b1e1f2a5ba6d5159916e5598020e680c45043b0736ad0153ee261a151dd90.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS true RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "911b1e1f2a5ba6d5159916e5598020e680c45043b0736ad0153ee261a151dd90" -} diff --git a/backend/.sqlx/query-96dc1cd308f3c21cfb50b88048054dc03f93e261d25969d66aa48e9d0502960f.json b/backend/.sqlx/query-96dc1cd308f3c21cfb50b88048054dc03f93e261d25969d66aa48e9d0502960f.json deleted file mode 100644 index 71548662e2..0000000000 --- a/backend/.sqlx/query-96dc1cd308f3c21cfb50b88048054dc03f93e261d25969d66aa48e9d0502960f.json +++ /dev/null @@ -1,169 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE schedule SET enabled = $1, email = $2 WHERE path = $3 AND workspace_id = $4 RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Bool", - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "96dc1cd308f3c21cfb50b88048054dc03f93e261d25969d66aa48e9d0502960f" -} diff --git a/backend/.sqlx/query-9ae98fbcea508dfc7113621c00b856f98f38dfb701a4660ea1a4058a6d7564f2.json b/backend/.sqlx/query-9ae98fbcea508dfc7113621c00b856f98f38dfb701a4660ea1a4058a6d7564f2.json deleted file mode 100644 index ad4c227408..0000000000 --- a/backend/.sqlx/query-9ae98fbcea508dfc7113621c00b856f98f38dfb701a4660ea1a4058a6d7564f2.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT app.id, app.path, app.summary, app.versions, app.policy,\n app.extra_perms, app_version.value, \n app_version.created_at, app_version.created_by from app, app_version \n WHERE app.id = $1 AND app.workspace_id = $2 AND app_version.id = app.versions[array_upper(app.versions, 1)]", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "versions", - "type_info": "Int8Array" - }, - { - "ordinal": 4, - "name": "policy", - "type_info": "Jsonb" - }, - { - "ordinal": 5, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 6, - "name": "value", - "type_info": "Json" - }, - { - "ordinal": 7, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 8, - "name": "created_by", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int8", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "9ae98fbcea508dfc7113621c00b856f98f38dfb701a4660ea1a4058a6d7564f2" -} diff --git a/backend/.sqlx/query-a3ccf362b4f6df400b3c7a084795dbf541eb14c5c374656ffb96da7283a2a6f1.json b/backend/.sqlx/query-a3ccf362b4f6df400b3c7a084795dbf541eb14c5c374656ffb96da7283a2a6f1.json new file mode 100644 index 0000000000..bc5e2351f9 --- /dev/null +++ b/backend/.sqlx/query-a3ccf362b4f6df400b3c7a084795dbf541eb14c5c374656ffb96da7283a2a6f1.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT concurrency_key FROM script WHERE hash = $1 AND workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "concurrency_key", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int8", + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "a3ccf362b4f6df400b3c7a084795dbf541eb14c5c374656ffb96da7283a2a6f1" +} diff --git a/backend/.sqlx/query-a875cb56485b812e9d4739afd0915067f7e5abe0ca0adf264b792fccf21e005b.json b/backend/.sqlx/query-a875cb56485b812e9d4739afd0915067f7e5abe0ca0adf264b792fccf21e005b.json new file mode 100644 index 0000000000..e264db5ef2 --- /dev/null +++ b/backend/.sqlx/query-a875cb56485b812e9d4739afd0915067f7e5abe0ca0adf264b792fccf21e005b.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT value->>'concurrency_key' FROM flow WHERE path = $1 AND workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "a875cb56485b812e9d4739afd0915067f7e5abe0ca0adf264b792fccf21e005b" +} diff --git a/backend/.sqlx/query-aa2800113a8a8805f47cdc1dd0f29d94c546fe531e7edd3e91da4978af5442fb.json b/backend/.sqlx/query-aa2800113a8a8805f47cdc1dd0f29d94c546fe531e7edd3e91da4978af5442fb.json deleted file mode 100644 index da3f5abd58..0000000000 --- a/backend/.sqlx/query-aa2800113a8a8805f47cdc1dd0f29d94c546fe531e7edd3e91da4978af5442fb.json +++ /dev/null @@ -1,167 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM schedule WHERE path = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "aa2800113a8a8805f47cdc1dd0f29d94c546fe531e7edd3e91da4978af5442fb" -} diff --git a/backend/.sqlx/query-aedba9cff117a7069dfd56602110b7f66903d6afb4f87fbffe0175982ea8741f.json b/backend/.sqlx/query-aedba9cff117a7069dfd56602110b7f66903d6afb4f87fbffe0175982ea8741f.json deleted file mode 100644 index 53eade2e2a..0000000000 --- a/backend/.sqlx/query-aedba9cff117a7069dfd56602110b7f66903d6afb4f87fbffe0175982ea8741f.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT app.id, app.path, app.summary, app.versions, app.policy,\n app.extra_perms, app_version.value, \n app_version.created_at, app_version.created_by,\n app.draft_only, draft.value as \"draft?\"\n from app\n INNER JOIN app_version ON\n app_version.id = app.versions[array_upper(app.versions, 1)]\n LEFT JOIN draft ON \n app.path = draft.path AND draft.workspace_id = $2 AND draft.typ = 'app' \n WHERE app.path = $1 AND app.workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "versions", - "type_info": "Int8Array" - }, - { - "ordinal": 4, - "name": "policy", - "type_info": "Jsonb" - }, - { - "ordinal": 5, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 6, - "name": "value", - "type_info": "Json" - }, - { - "ordinal": 7, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 8, - "name": "created_by", - "type_info": "Varchar" - }, - { - "ordinal": 9, - "name": "draft_only", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "draft?", - "type_info": "Json" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - true, - false - ] - }, - "hash": "aedba9cff117a7069dfd56602110b7f66903d6afb4f87fbffe0175982ea8741f" -} diff --git a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json index 54e94cfb8f..99269c9851 100644 --- a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json +++ b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json @@ -18,8 +18,8 @@ "Left": [] }, "nullable": [ - false, - true + true, + false ] }, "hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76" diff --git a/backend/.sqlx/query-c10348d26e3e3dc9e345d9044db35db9906617eb9c98aaf58a55d9681ffb85e3.json b/backend/.sqlx/query-c10348d26e3e3dc9e345d9044db35db9906617eb9c98aaf58a55d9681ffb85e3.json deleted file mode 100644 index 8f1405fb77..0000000000 --- a/backend/.sqlx/query-c10348d26e3e3dc9e345d9044db35db9906617eb9c98aaf58a55d9681ffb85e3.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS false RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "c10348d26e3e3dc9e345d9044db35db9906617eb9c98aaf58a55d9681ffb85e3" -} diff --git a/backend/.sqlx/query-d07faf1c9501d1bc5f6fd9588bb3da7b7f4897fcfd26dcb3b8ce406b1f653077.json b/backend/.sqlx/query-d07faf1c9501d1bc5f6fd9588bb3da7b7f4897fcfd26dcb3b8ce406b1f653077.json deleted file mode 100644 index eb8d642751..0000000000 --- a/backend/.sqlx/query-d07faf1c9501d1bc5f6fd9588bb3da7b7f4897fcfd26dcb3b8ce406b1f653077.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT app.id, app.path, app.summary, app.versions, app.policy,\n app.extra_perms, app_version.value, \n app_version.created_at, app_version.created_by from app, app_version \n WHERE app.path = $1 AND app.workspace_id = $2 AND app_version.id = app.versions[array_upper(app.versions, 1)]", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "versions", - "type_info": "Int8Array" - }, - { - "ordinal": 4, - "name": "policy", - "type_info": "Jsonb" - }, - { - "ordinal": 5, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 6, - "name": "value", - "type_info": "Json" - }, - { - "ordinal": 7, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 8, - "name": "created_by", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false - ] - }, - "hash": "d07faf1c9501d1bc5f6fd9588bb3da7b7f4897fcfd26dcb3b8ce406b1f653077" -} diff --git a/backend/.sqlx/query-dd74fa9468b5fe8c8ad657ded06076c11a78d0206af2af20685c691d4d9520bb.json b/backend/.sqlx/query-dd74fa9468b5fe8c8ad657ded06076c11a78d0206af2af20685c691d4d9520bb.json deleted file mode 100644 index 4b08680ee7..0000000000 --- a/backend/.sqlx/query-dd74fa9468b5fe8c8ad657ded06076c11a78d0206af2af20685c691d4d9520bb.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS false RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "dd74fa9468b5fe8c8ad657ded06076c11a78d0206af2af20685c691d4d9520bb" -} diff --git a/backend/.sqlx/query-eced0a09ba547ce1dccb54a5419b22373603c9d01f77047b3553bde125bf71e8.json b/backend/.sqlx/query-eced0a09ba547ce1dccb54a5419b22373603c9d01f77047b3553bde125bf71e8.json deleted file mode 100644 index 25632eefec..0000000000 --- a/backend/.sqlx/query-eced0a09ba547ce1dccb54a5419b22373603c9d01f77047b3553bde125bf71e8.json +++ /dev/null @@ -1,168 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS true RETURNING *", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Text", - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "eced0a09ba547ce1dccb54a5419b22373603c9d01f77047b3553bde125bf71e8" -} diff --git a/backend/.sqlx/query-efbd9dc28ab5e53d070684b2e99d945872791171a1c8cef6088ea662f3b2cebb.json b/backend/.sqlx/query-efbd9dc28ab5e53d070684b2e99d945872791171a1c8cef6088ea662f3b2cebb.json deleted file mode 100644 index ac3b3a656c..0000000000 --- a/backend/.sqlx/query-efbd9dc28ab5e53d070684b2e99d945872791171a1c8cef6088ea662f3b2cebb.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT path, value from flow WHERE workspace_id = $1 LIMIT $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "value", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Text", - "Int8" - ] - }, - "nullable": [ - false, - false - ] - }, - "hash": "efbd9dc28ab5e53d070684b2e99d945872791171a1c8cef6088ea662f3b2cebb" -} diff --git a/backend/.sqlx/query-f4960efbb4189b595541608c9ab5cbbd56eb42059e961b6338b685105626a5c7.json b/backend/.sqlx/query-f4960efbb4189b595541608c9ab5cbbd56eb42059e961b6338b685105626a5c7.json deleted file mode 100644 index 178148361c..0000000000 --- a/backend/.sqlx/query-f4960efbb4189b595541608c9ab5cbbd56eb42059e961b6338b685105626a5c7.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT payload\n FROM capture\n WHERE workspace_id = $1\n AND path = $2\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "payload", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "f4960efbb4189b595541608c9ab5cbbd56eb42059e961b6338b685105626a5c7" -} diff --git a/backend/.sqlx/query-fdd3710a381dac33ef5ee5ec5564a4874e6d7807d43fa6ea7b8408cad9e97480.json b/backend/.sqlx/query-fdd3710a381dac33ef5ee5ec5564a4874e6d7807d43fa6ea7b8408cad9e97480.json deleted file mode 100644 index bd5ed64a3d..0000000000 --- a/backend/.sqlx/query-fdd3710a381dac33ef5ee5ec5564a4874e6d7807d43fa6ea7b8408cad9e97480.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM schedule\n WHERE workspace_id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "path", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "edited_by", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "edited_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 4, - "name": "schedule", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "enabled", - "type_info": "Bool" - }, - { - "ordinal": 6, - "name": "script_path", - "type_info": "Varchar" - }, - { - "ordinal": 7, - "name": "args", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "extra_perms", - "type_info": "Jsonb" - }, - { - "ordinal": 9, - "name": "is_flow", - "type_info": "Bool" - }, - { - "ordinal": 10, - "name": "email", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "error", - "type_info": "Text" - }, - { - "ordinal": 12, - "name": "timezone", - "type_info": "Varchar" - }, - { - "ordinal": 13, - "name": "on_failure", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "on_recovery", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "on_failure_times", - "type_info": "Int4" - }, - { - "ordinal": 16, - "name": "on_failure_exact", - "type_info": "Bool" - }, - { - "ordinal": 17, - "name": "on_failure_extra_args", - "type_info": "Json" - }, - { - "ordinal": 18, - "name": "on_recovery_times", - "type_info": "Int4" - }, - { - "ordinal": 19, - "name": "on_recovery_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "ws_error_handler_muted", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "retry", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "summary", - "type_info": "Varchar" - }, - { - "ordinal": 23, - "name": "no_flow_overlap", - "type_info": "Bool" - }, - { - "ordinal": 24, - "name": "tag", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - true, - false, - false, - false, - true, - false, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - false, - true - ] - }, - "hash": "fdd3710a381dac33ef5ee5ec5564a4874e6d7807d43fa6ea7b8408cad9e97480" -} diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index a3103e19fc..f8a46f73e9 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -e0b0494c26b63efa4aa3f41a9fb42c7733dcca2e +081d34e693a9a69fe821d3f3baa672da5705a1cb \ No newline at end of file diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index d6bc95a239..43c302bfde 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -8,7 +8,6 @@ use futures::StreamExt; use futures::{stream, Stream}; use serde::Deserialize; use serde_json::json; -use sqlx::types::Json; use sqlx::{postgres::PgListener, types::Uuid, Pool, Postgres}; #[cfg(feature = "enterprise")] @@ -868,13 +867,17 @@ impl RunJob { async fn push(self, db: &Pool) -> Uuid { let RunJob { payload, args } = self; + let mut hm_args = std::collections::HashMap::new(); + for (k, v) in args { + hm_args.insert(k, windmill_common::worker::to_raw_value(&v)); + } let tx = PushIsolationLevel::IsolatedRoot(db.clone(), None); - let (uuid, tx) = windmill_queue::push::<_, rsmq_async::MultiplexedRsmq>( + let (uuid, tx) = windmill_queue::push::( &db, tx, "test-workspace", payload, - Json(args), + hm_args.into(), /* user */ "test-user", /* email */ "test@windmill.dev", /* permissioned_as */ "u/test-user".to_string(), @@ -1070,7 +1073,7 @@ async fn test_deno_flow(db: Pool) { custom_concurrency_key: None, concurrent_limit: None, concurrency_time_window_s: None, - }, + }.into(), stop_after_if: Default::default(), summary: Default::default(), suspend: Default::default(), @@ -1108,7 +1111,7 @@ async fn test_deno_flow(db: Pool) { custom_concurrency_key: None, concurrent_limit: None, concurrency_time_window_s: None, - }, + }.into(), stop_after_if: Default::default(), summary: Default::default(), suspend: Default::default(), @@ -1121,7 +1124,7 @@ async fn test_deno_flow(db: Pool) { delete_after_use: None, continue_on_error: None, }], - }, + }.into(), stop_after_if: Default::default(), summary: Default::default(), suspend: Default::default(), @@ -1207,12 +1210,12 @@ async fn test_deno_flow_same_worker(db: Pool) { input_transforms: [ ( "loop".to_string(), - InputTransform::Static { value: json!(false) }, + InputTransform::Static { value: windmill_common::worker::to_raw_value(&false) }, ), - ("i".to_string(), InputTransform::Static { value: json!(1) }), + ("i".to_string(), InputTransform::Static { value: windmill_common::worker::to_raw_value(&1) }), ( "path".to_string(), - InputTransform::Static { value: json!("outer.txt") }, + InputTransform::Static { value: windmill_common::worker::to_raw_value(&"outer.txt") }, ), ] .into(), @@ -1224,7 +1227,7 @@ async fn test_deno_flow_same_worker(db: Pool) { custom_concurrency_key: None, concurrent_limit: None, concurrency_time_window_s: None, - }, + }.into(), stop_after_if: Default::default(), summary: Default::default(), suspend: Default::default(), @@ -1240,7 +1243,7 @@ async fn test_deno_flow_same_worker(db: Pool) { FlowModule { id: "b".to_string(), value: FlowModuleValue::ForloopFlow { - iterator: InputTransform::Static { value: json!([1, 2, 3]) }, + iterator: InputTransform::Static { value: windmill_common::worker::to_raw_value(&[1, 2, 3]) }, skip_failures: false, parallel: false, parallelism: None, @@ -1257,11 +1260,11 @@ async fn test_deno_flow_same_worker(db: Pool) { ), ( "loop".to_string(), - InputTransform::Static { value: json!(true) }, + InputTransform::Static { value: windmill_common::worker::to_raw_value(&true) }, ), ( "path".to_string(), - InputTransform::Static { value: json!("inner.txt") }, + InputTransform::Static { value: windmill_common::worker::to_raw_value(&"inner.txt") }, ), ] .into(), @@ -1273,7 +1276,7 @@ async fn test_deno_flow_same_worker(db: Pool) { custom_concurrency_key: None, concurrent_limit: None, concurrency_time_window_s: None, - }, + }.into(), stop_after_if: Default::default(), summary: Default::default(), suspend: Default::default(), @@ -1291,10 +1294,10 @@ async fn test_deno_flow_same_worker(db: Pool) { value: FlowModuleValue::RawScript { input_transforms: [( "path".to_string(), - InputTransform::Static { value: json!("inner.txt") }, + InputTransform::Static { value: windmill_common::worker::to_raw_value(&"inner.txt") }, ), ( "path2".to_string(), - InputTransform::Static { value: json!("outer.txt") }, + InputTransform::Static { value: windmill_common::worker::to_raw_value(&"outer.txt") }, )] .into(), language: ScriptLang::Deno, @@ -1308,7 +1311,7 @@ async fn test_deno_flow_same_worker(db: Pool) { custom_concurrency_key: None, concurrent_limit: None, concurrency_time_window_s: None, - }, + }.into(), stop_after_if: Default::default(), summary: Default::default(), suspend: Default::default(), @@ -1323,7 +1326,7 @@ async fn test_deno_flow_same_worker(db: Pool) { }, ], - }, + }.into(), stop_after_if: Default::default(), summary: Default::default(), suspend: Default::default(), @@ -1346,11 +1349,11 @@ async fn test_deno_flow_same_worker(db: Pool) { ), ( "path".to_string(), - InputTransform::Static { value: json!("outer.txt") }, + InputTransform::Static { value: windmill_common::worker::to_raw_value(&"outer.txt") }, ), ( "path2".to_string(), - InputTransform::Static { value: json!("inner.txt") }, + InputTransform::Static { value: windmill_common::worker::to_raw_value(&"inner.txt") }, ), ] .into(), @@ -1365,7 +1368,7 @@ async fn test_deno_flow_same_worker(db: Pool) { custom_concurrency_key: None, concurrent_limit: None, concurrency_time_window_s: None, - }, + }.into(), stop_after_if: Default::default(), summary: Default::default(), suspend: Default::default(), diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index cb7becda60..034be5969b 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -39,6 +39,7 @@ use windmill_common::{ http_get_from_hub, not_found_if_none, paginate, query_elems_from_hub, Pagination, StripPath, }, variables::build_crypt, + worker::to_raw_value, HUB_BASE_URL, }; @@ -94,37 +95,37 @@ pub struct ListableApp { pub struct AppVersion { pub id: i64, pub app_id: Uuid, - pub value: serde_json::Value, + pub value: sqlx::types::Json>, pub created_by: String, pub created_at: chrono::DateTime, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, FromRow)] pub struct AppWithLastVersion { pub id: i64, pub path: String, pub summary: String, pub policy: serde_json::Value, pub versions: Vec, - pub value: serde_json::Value, + pub value: sqlx::types::Json>, pub created_by: String, pub created_at: chrono::DateTime, pub extra_perms: serde_json::Value, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, FromRow)] pub struct AppWithLastVersionAndDraft { pub id: i64, pub path: String, pub summary: String, pub policy: serde_json::Value, pub versions: Vec, - pub value: serde_json::Value, + pub value: sqlx::types::Json>, pub created_by: String, pub created_at: chrono::DateTime, pub extra_perms: serde_json::Value, #[serde(skip_serializing_if = "Option::is_none")] - pub draft: Option, + pub draft: Option>>, #[serde(skip_serializing_if = "Option::is_none")] pub draft_only: Option, } @@ -178,7 +179,7 @@ pub struct Policy { pub struct CreateApp { pub path: String, pub summary: String, - pub value: serde_json::Value, + pub value: sqlx::types::Json>, pub policy: Policy, pub draft_only: Option, pub deployment_message: Option, @@ -188,7 +189,7 @@ pub struct CreateApp { pub struct EditApp { pub path: Option, pub summary: Option, - pub value: Option, + pub value: Option>>, pub policy: Option, pub deployment_message: Option, } @@ -196,7 +197,7 @@ pub struct EditApp { #[derive(Serialize, FromRow)] pub struct SearchApp { path: String, - value: serde_json::Value, + value: sqlx::types::Json>, } async fn list_search_apps( authed: ApiAuthed, @@ -210,12 +211,11 @@ async fn list_search_apps( let n = 3; let mut tx = user_db.begin(&authed).await?; - let rows = sqlx::query_as!( - SearchApp, + let rows = sqlx::query_as::<_, SearchApp>( "SELECT path, app_version.value from app LEFT JOIN app_version ON app_version.id = versions[array_upper(versions, 1)] WHERE workspace_id = $1 LIMIT $2", - &w_id, - n ) + .bind(&w_id) + .bind(n) .fetch_all(&mut *tx) .await? .into_iter() @@ -293,15 +293,14 @@ async fn get_app( let path = path.to_path(); let mut tx = user_db.begin(&authed).await?; - let app_o = sqlx::query_as!( - AppWithLastVersion, + let app_o = sqlx::query_as::<_, AppWithLastVersion>( "SELECT app.id, app.path, app.summary, app.versions, app.policy, app.extra_perms, app_version.value, app_version.created_at, app_version.created_by from app, app_version WHERE app.path = $1 AND app.workspace_id = $2 AND app_version.id = app.versions[array_upper(app.versions, 1)]", - path.to_owned(), - &w_id ) + .bind(path.to_owned()) + .bind(&w_id) .fetch_optional(&mut *tx) .await?; tx.commit().await?; @@ -318,21 +317,20 @@ async fn get_app_w_draft( let path = path.to_path(); let mut tx = user_db.begin(&authed).await?; - let app_o = sqlx::query_as!( - AppWithLastVersionAndDraft, + let app_o = sqlx::query_as::<_, AppWithLastVersionAndDraft>( r#"SELECT app.id, app.path, app.summary, app.versions, app.policy, app.extra_perms, app_version.value, app_version.created_at, app_version.created_by, - app.draft_only, draft.value as "draft?" + app.draft_only, draft.value as "draft" from app INNER JOIN app_version ON app_version.id = app.versions[array_upper(app.versions, 1)] LEFT JOIN draft ON app.path = draft.path AND draft.workspace_id = $2 AND draft.typ = 'app' WHERE app.path = $1 AND app.workspace_id = $2"#, - path.to_owned(), - &w_id ) + .bind(path.to_owned()) + .bind(&w_id) .fetch_optional(&mut *tx) .await?; tx.commit().await?; @@ -406,15 +404,14 @@ async fn get_app_by_id( ) -> JsonResult { let mut tx = user_db.begin(&authed).await?; - let app_o = sqlx::query_as!( - AppWithLastVersion, + let app_o = sqlx::query_as::<_, AppWithLastVersion>( "SELECT app.id, app.path, app.summary, app.versions, app.policy, app.extra_perms, app_version.value, app_version.created_at, app_version.created_by from app, app_version WHERE app_version.id = $1 AND app.id = app_version.app_id AND app.workspace_id = $2", - id, - &w_id ) + .bind(&id) + .bind(&w_id) .fetch_optional(&mut *tx) .await?; tx.commit().await?; @@ -438,15 +435,13 @@ async fn get_public_app_by_secret( let id: i64 = bytes.parse().map_err(to_anyhow)?; - let app_o = sqlx::query_as!( - AppWithLastVersion, + let app_o = sqlx::query_as::<_, AppWithLastVersion>( "SELECT app.id, app.path, app.summary, app.versions, app.policy, app.extra_perms, app_version.value, app_version.created_at, app_version.created_by from app, app_version - WHERE app.id = $1 AND app.workspace_id = $2 AND app_version.id = app.versions[array_upper(app.versions, 1)]", - id, - &w_id - ) + WHERE app.id = $1 AND app.workspace_id = $2 AND app_version.id = app.versions[array_upper(app.versions, 1)]") + .bind(&id) + .bind(&w_id) .fetch_optional(&mut *tx) .await?; tx.commit().await?; @@ -599,9 +594,9 @@ async fn create_app( ) .await?; - let mut args: HashMap = HashMap::new(); + let mut args: HashMap> = HashMap::new(); if let Some(dm) = app.deployment_message { - args.insert("deployment_message".to_string(), json!(dm)); + args.insert("deployment_message".to_string(), to_raw_value(&dm)); } let tx = PushIsolationLevel::Transaction(tx); @@ -610,7 +605,7 @@ async fn create_app( tx, &w_id, JobPayload::AppDependencies { path: app.path.clone(), version: v_id }, - args, + PushArgs { args, extra: HashMap::new() }, &authed.username, &authed.email, windmill_common::users::username_to_permissioned_as(&authed.username), @@ -889,18 +884,18 @@ async fn update_app( let tx: PushIsolationLevel<'_, rsmq_async::MultiplexedRsmq> = PushIsolationLevel::Transaction(tx); - let mut args: HashMap = HashMap::new(); + let mut args: HashMap> = HashMap::new(); if let Some(dm) = ns.deployment_message { - args.insert("deployment_message".to_string(), json!(dm)); + args.insert("deployment_message".to_string(), to_raw_value(&dm)); } - args.insert("parent_path".to_string(), json!(path)); + args.insert("parent_path".to_string(), to_raw_value(&path)); let (dependency_job_uuid, new_tx) = push( &db, tx, &w_id, JobPayload::AppDependencies { path: npath.clone(), version: v_id }, - args, + PushArgs { args, extra: HashMap::new() }, &authed.username, &authed.email, windmill_common::users::username_to_permissioned_as(&authed.username), @@ -1160,8 +1155,7 @@ fn build_args( component: &str, path: String, args: HashMap>, -) -> Result>>> { - +) -> Result { let key = format!("{}:{}", component, &path); let (static_inputs, one_of_inputs) = match policy { Policy { triggerables_v2: Some(t), .. } => { @@ -1276,5 +1270,5 @@ fn build_args( for (k, v) in static_inputs { extra.insert(k.to_string(), v.to_owned()); } - Ok(PushArgs { extra, args: sqlx::types::Json(safe_args) }) + Ok(PushArgs { extra, args: safe_args }) } diff --git a/backend/windmill-api/src/capture.rs b/backend/windmill-api/src/capture.rs index 1223f93ede..99f155b0f8 100644 --- a/backend/windmill-api/src/capture.rs +++ b/backend/windmill-api/src/capture.rs @@ -6,15 +6,12 @@ * LICENSE-AGPL for a copy of the license. */ -use std::collections::HashMap; - use axum::{ extract::{Extension, Path}, routing::{get, post, put}, Router, }; use hyper::StatusCode; -use serde_json::value::RawValue; use sqlx::types::Json; use windmill_common::{ db::UserDB, @@ -89,7 +86,7 @@ pub async fn new_payload( pub async fn update_payload( Extension(db): Extension, Path((w_id, path)): Path<(String, StripPath)>, - args: PushArgs>>, + args: PushArgs, ) -> Result { let mut tx = db.begin().await?; @@ -102,7 +99,7 @@ pub async fn update_payload( ", &w_id, &path.to_path(), - Json(args) as Json>>>, + Json(args) as Json, ) .execute(&mut *tx) .await?; @@ -112,27 +109,31 @@ pub async fn update_payload( Ok(StatusCode::NO_CONTENT) } +#[derive(sqlx::FromRow)] +struct Payload { + payload: sqlx::types::Json>, +} pub async fn get_payload( authed: ApiAuthed, Extension(user_db): Extension, Path((w_id, path)): Path<(String, StripPath)>, -) -> JsonResult { +) -> JsonResult> { let mut tx = user_db.begin(&authed).await?; - let payload = sqlx::query_scalar!( + let payload = sqlx::query_as::<_, Payload>( " SELECT payload FROM capture WHERE workspace_id = $1 AND path = $2 ", - &w_id, - &path.to_path(), ) + .bind(&w_id) + .bind(&path.to_path()) .fetch_optional(&mut *tx) .await?; tx.commit().await?; - not_found_if_none(payload, "capture", path.to_path()).map(axum::Json) + not_found_if_none(payload.map(|x| x.payload.0), "capture", path.to_path()).map(axum::Json) } diff --git a/backend/windmill-api/src/drafts.rs b/backend/windmill-api/src/drafts.rs index a56aba9e3d..a91d00d2fa 100644 --- a/backend/windmill-api/src/drafts.rs +++ b/backend/windmill-api/src/drafts.rs @@ -38,7 +38,7 @@ pub enum DraftType { #[derive(Deserialize, Serialize, Debug)] pub struct Draft { pub path: String, - pub value: serde_json::Value, + pub value: sqlx::types::Json>, pub typ: DraftType, } diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index e415003e05..b1fcd21534 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -25,12 +25,12 @@ use axum::{ use hyper::StatusCode; use serde::{Deserialize, Serialize}; -use serde_json::json; use sql_builder::prelude::*; use sqlx::{FromRow, Postgres, Transaction}; use windmill_audit::audit_ee::audit_log; use windmill_audit::ActionKind; use windmill_common::utils::query_elems_from_hub; +use windmill_common::worker::to_raw_value; use windmill_common::HUB_BASE_URL; use windmill_common::{ db::UserDB, @@ -71,7 +71,7 @@ pub fn global_service() -> Router { #[derive(Serialize, FromRow)] pub struct SearchFlow { path: String, - value: serde_json::Value, + value: sqlx::types::Json>, } async fn list_search_flows( authed: ApiAuthed, @@ -85,12 +85,11 @@ async fn list_search_flows( let n = 3; let mut tx = user_db.begin(&authed).await?; - let rows = sqlx::query_as!( - SearchFlow, + let rows = sqlx::query_as::<_, SearchFlow>( "SELECT path, value from flow WHERE workspace_id = $1 LIMIT $2", - &w_id, - n ) + .bind(&w_id) + .bind(n) .fetch_all(&mut *tx) .await? .into_iter() @@ -363,9 +362,9 @@ async fn create_flow( ) .await?; - let mut args: HashMap = HashMap::new(); + let mut args: HashMap> = HashMap::new(); if let Some(dm) = nf.deployment_message { - args.insert("deployment_message".to_string(), json!(dm)); + args.insert("deployment_message".to_string(), to_raw_value(&dm)); } let tx = PushIsolationLevel::Transaction(tx); @@ -377,7 +376,7 @@ async fn create_flow( path: nf.path.clone(), dedicated_worker: nf.dedicated_worker, }, - args, + args.into(), &authed.username, &authed.email, windmill_common::users::username_to_permissioned_as(&authed.username), @@ -515,22 +514,19 @@ async fn update_flow( } } - let mut schedulables: Vec = sqlx::query_as!( - Schedule, - "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS true RETURNING *", - nf.path, - flow_path, - w_id, - ) + let mut schedulables: Vec = sqlx::query_as::<_, Schedule>( + "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS true RETURNING *") + .bind(&nf.path) + .bind(&flow_path) + .bind(&w_id) .fetch_all(&mut tx) .await?; - let schedule = sqlx::query_as!(Schedule, - "UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS true RETURNING *", - nf.path, - flow_path, - w_id, - ) + let schedule = sqlx::query_as::<_, Schedule>( + "UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS true RETURNING *") + .bind(&nf.path) + .bind(&flow_path) + .bind(&w_id) .fetch_optional(&mut tx) .await?; @@ -582,11 +578,11 @@ async fn update_flow( let tx = PushIsolationLevel::Transaction(tx); - let mut args: HashMap = HashMap::new(); + let mut args: HashMap> = HashMap::new(); if let Some(dm) = nf.deployment_message { - args.insert("deployment_message".to_string(), json!(dm)); + args.insert("deployment_message".to_string(), to_raw_value(&dm)); } - args.insert("parent_path".to_string(), json!(flow_path)); + args.insert("parent_path".to_string(), to_raw_value(&flow_path)); let (dependency_job_uuid, mut new_tx) = push( &db, @@ -596,7 +592,7 @@ async fn update_flow( path: nf.path.clone(), dedicated_worker: nf.dedicated_worker, }, - args, + windmill_queue::PushArgs { args, extra: HashMap::new() }, &authed.username, &authed.email, windmill_common::users::username_to_permissioned_as(&authed.username), @@ -663,10 +659,10 @@ pub struct FlowWDraft { pub summary: String, pub description: String, pub schema: Option, - pub value: serde_json::Value, + pub value: sqlx::types::Json>, pub extra_perms: serde_json::Value, #[serde(skip_serializing_if = "Option::is_none")] - pub draft: Option, + pub draft: Option>>, #[serde(skip_serializing_if = "Option::is_none")] pub draft_only: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -881,16 +877,18 @@ mod tests { modules: vec![ FlowModule { id: "a".to_string(), - value: FlowModuleValue::Script { + value: windmill_common::worker::to_raw_value(&FlowModuleValue::Script { path: "test".to_string(), input_transforms: [( "test".to_string(), - InputTransform::Static { value: serde_json::json!("test2") }, + InputTransform::Static { + value: windmill_common::worker::to_raw_value(&"test2".to_string()), + }, )] .into(), hash: None, tag_override: None, - }, + }), stop_after_if: None, summary: None, suspend: Default::default(), @@ -905,7 +903,7 @@ mod tests { }, FlowModule { id: "b".to_string(), - value: FlowModuleValue::RawScript { + value: windmill_common::worker::to_raw_value(&FlowModuleValue::RawScript { input_transforms: HashMap::new(), content: "test".to_string(), language: scripts::ScriptLang::Deno, @@ -915,7 +913,7 @@ mod tests { custom_concurrency_key: None, concurrent_limit: None, concurrency_time_window_s: None, - }, + }), stop_after_if: Some(StopAfterIf { expr: "foo = 'bar'".to_string(), skip_if_stopped: false, @@ -933,13 +931,15 @@ mod tests { }, FlowModule { id: "c".to_string(), - value: FlowModuleValue::ForloopFlow { - iterator: InputTransform::Static { value: serde_json::json!([1, 2, 3]) }, + value: windmill_common::worker::to_raw_value(&FlowModuleValue::ForloopFlow { + iterator: InputTransform::Static { + value: windmill_common::worker::to_raw_value(&[1, 2, 3]), + }, modules: vec![], skip_failures: true, parallel: false, parallelism: None, - }, + }), stop_after_if: Some(StopAfterIf { expr: "previous.isEmpty()".to_string(), skip_if_stopped: false, @@ -963,7 +963,8 @@ mod tests { input_transforms: HashMap::new(), hash: None, tag_override: None, - }, + } + .into(), stop_after_if: Some(StopAfterIf { expr: "previous.isEmpty()".to_string(), skip_if_stopped: false, diff --git a/backend/windmill-api/src/inputs.rs b/backend/windmill-api/src/inputs.rs index 6427ea455e..74ff2d3028 100644 --- a/backend/windmill-api/src/inputs.rs +++ b/backend/windmill-api/src/inputs.rs @@ -47,7 +47,7 @@ pub struct InputRow { pub runnable_id: String, pub runnable_type: RunnableType, pub name: String, - pub args: Value, + pub args: sqlx::types::Json>, pub created_at: DateTime, pub created_by: String, pub is_public: bool, @@ -95,12 +95,12 @@ pub struct RunnableParams { pub runnable_type: RunnableType, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)] pub struct Input { id: Uuid, name: String, created_at: chrono::DateTime, - args: serde_json::Value, + args: sqlx::types::Json>, created_by: String, is_public: bool, success: bool, @@ -110,7 +110,7 @@ pub struct Input { pub struct CompletedJobMini { id: Uuid, created_at: chrono::DateTime, - args: Option, + args: Option>>, created_by: String, success: bool, } @@ -161,7 +161,9 @@ async fn get_input_history( row.created_by ), created_at: row.created_at, - args: row.args.unwrap_or(serde_json::json!({})), + 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, @@ -240,7 +242,7 @@ async fn list_saved_inputs( #[derive(Debug, Serialize, Deserialize)] pub struct CreateInput { name: String, - args: serde_json::Value, + args: Box, } async fn create_input( @@ -262,7 +264,7 @@ async fn create_input( .bind(&r.runnable_id) .bind(&r.runnable_type) .bind(&input.name) - .bind(&input.args) + .bind(sqlx::types::Json(&input.args)) .bind(&authed.username) .execute(&mut *tx) .await?; diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 4ca6217849..ea139756b3 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -903,7 +903,11 @@ pub struct ListQueueQuery { pub is_not_schedule: Option, } -pub fn filter_list_queue_query(mut sqlb: SqlBuilder, lq: &ListQueueQuery, w_id: &str) -> SqlBuilder { +pub fn filter_list_queue_query( + mut sqlb: SqlBuilder, + lq: &ListQueueQuery, + w_id: &str, +) -> SqlBuilder { if w_id != "admins" || !lq.all_workspaces.is_some_and(|x| x) { sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); } @@ -1004,7 +1008,6 @@ pub fn filter_list_queue_query(mut sqlb: SqlBuilder, lq: &ListQueueQuery, w_id: } sqlb - } pub fn list_queue_jobs_query(w_id: &str, lq: &ListQueueQuery, fields: &[&str]) -> SqlBuilder { @@ -2054,7 +2057,10 @@ impl Job { ) } - pub async fn concurrency_key(&self, db: &Pool) -> Result, sqlx::Error> { + pub async fn concurrency_key( + &self, + db: &Pool, + ) -> Result, sqlx::Error> { sqlx::query_scalar!( "SELECT key FROM concurrency_key WHERE job_id = $1", self.id() @@ -2202,7 +2208,7 @@ struct Preview { content: Option, kind: Option, path: Option, - args: Option>, + args: Option>>, language: Option, tag: Option, dedicated_worker: Option, @@ -2218,7 +2224,7 @@ pub struct WorkflowTask { struct PreviewFlow { value: FlowValue, path: Option, - args: Option>, + args: Option>>, tag: Option, restarted_from: Option, } @@ -2331,7 +2337,7 @@ pub async fn run_flow_by_path( Extension(rsmq): Extension>, Path((w_id, flow_path)): Path<(String, StripPath)>, Query(run_query): Query, - args: PushArgs>>, + args: PushArgs, ) -> error::Result<(StatusCode, String)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -2432,7 +2438,8 @@ pub async fn restart_flow( let push_args = completed_job .args - .map(|json| PushArgs { args: json.clone(), extra: json.0 }); + .map(|json| PushArgs { args: json.0, extra: HashMap::new() }) + .unwrap_or_else(PushArgs::empty); let scheduled_for = run_query.get_scheduled_for(&db).await?; let tx = PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into(), rsmq); @@ -2476,7 +2483,7 @@ pub async fn run_script_by_path( Extension(rsmq): Extension>, Path((w_id, script_path)): Path<(String, StripPath)>, Query(run_query): Query, - args: PushArgs>>, + args: PushArgs, ) -> error::Result<(StatusCode, String)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -2563,7 +2570,7 @@ pub async fn run_workflow_as_code( let mut extra = HashMap::new(); extra.insert(ENTRYPOINT_OVERRIDE.to_string(), to_raw_value(&entrypoint)); - let args = PushArgs { args: sqlx::types::Json(task.args), extra }; + let args = PushArgs { args: task.args.unwrap_or_else(HashMap::new), extra }; let scheduled_for = run_query.get_scheduled_for(&db).await?; let tag = run_query.tag.clone().or(tag).or(Some(job.tag)); @@ -2854,7 +2861,7 @@ pub async fn run_wait_result_job_by_path_get( }); let inner_args: HashMap> = HashMap::new(); - let args = PushArgs { extra: payload_args, args: sqlx::types::Json(inner_args) }; + let args = PushArgs { extra: payload_args, args: inner_args }; check_queue_too_long(&db, QUEUE_LIMIT_WAIT_RESULT.or(run_query.queue_limit)).await?; let script_path = script_path.to_path(); @@ -2932,7 +2939,7 @@ pub async fn run_wait_result_flow_by_path_get( payload_args.insert(k.to_string(), v.clone()); }); - let args = PushArgs { extra: payload_args, args: sqlx::types::Json(HashMap::new()) }; + let args = PushArgs { extra: payload_args, args: HashMap::new() }; run_wait_result_flow_by_path_internal( db, run_query, flow_path, authed, rsmq, user_db, args, w_id, @@ -2947,7 +2954,7 @@ pub async fn run_wait_result_script_by_path( Extension(db): Extension, Path((w_id, script_path)): Path<(String, StripPath)>, Query(run_query): Query, - args: PushArgs>>, + args: PushArgs, ) -> error::Result { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -2973,7 +2980,7 @@ async fn run_wait_result_script_by_path_internal( rsmq: Option, user_db: UserDB, w_id: String, - args: PushArgs>>, + args: PushArgs, ) -> error::Result { check_queue_too_long(&db, QUEUE_LIMIT_WAIT_RESULT.or(run_query.queue_limit)).await?; let script_path = script_path.to_path(); @@ -3026,7 +3033,7 @@ pub async fn run_wait_result_script_by_hash( Extension(db): Extension, Path((w_id, script_hash)): Path<(String, ScriptHash)>, Query(run_query): Query, - args: PushArgs>>, + args: PushArgs, ) -> error::Result { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -3106,7 +3113,7 @@ pub async fn run_wait_result_flow_by_path( Extension(db): Extension, Path((w_id, flow_path)): Path<(String, StripPath)>, Query(run_query): Query, - args: PushArgs>>, + args: PushArgs, ) -> error::Result { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -3124,7 +3131,7 @@ async fn run_wait_result_flow_by_path_internal( authed: ApiAuthed, rsmq: Option, user_db: UserDB, - args: PushArgs>>, + args: PushArgs, w_id: String, ) -> error::Result { check_queue_too_long(&db, run_query.queue_limit).await?; @@ -3220,7 +3227,7 @@ async fn run_preview_script( dedicated_worker: preview.dedicated_worker, }), }, - preview.args.unwrap_or_default(), + preview.args.unwrap_or_default().into(), authed.display_username(), &authed.email, username_to_permissioned_as(&authed.username), @@ -3301,7 +3308,7 @@ async fn run_bundle_preview_script( custom_concurrency_key: None, }), }, - args, + args.into(), authed.display_username(), &authed.email, username_to_permissioned_as(&authed.username), @@ -3421,10 +3428,7 @@ pub async fn run_dependencies_job( "raw_deps".to_string(), JsonRawValue::from_string("true".to_string()).unwrap(), ); - ( - PushArgs { extra: hm, args: sqlx::types::Json(HashMap::new()) }, - deps, - ) + (PushArgs { extra: hm, args: HashMap::new() }, deps) } else { ( PushArgs::empty(), @@ -3668,7 +3672,7 @@ async fn run_preview_flow_job( path: raw_flow.path, restarted_from: raw_flow.restarted_from, }, - raw_flow.args.unwrap_or_default(), + raw_flow.args.unwrap_or_default().into(), authed.display_username(), &authed.email, username_to_permissioned_as(&authed.username), @@ -3700,7 +3704,7 @@ pub async fn run_job_by_hash( Extension(rsmq): Extension>, Path((w_id, script_hash)): Path<(String, ScriptHash)>, Query(run_query): Query, - args: PushArgs>>, + args: PushArgs, ) -> error::Result<(StatusCode, String)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -3895,7 +3899,11 @@ async fn get_job_update( } } -pub fn filter_list_completed_query(mut sqlb: SqlBuilder, lq: &ListCompletedQuery, w_id: &str) -> SqlBuilder { +pub fn filter_list_completed_query( + mut sqlb: SqlBuilder, + lq: &ListCompletedQuery, + w_id: &str, +) -> SqlBuilder { if w_id != "admins" || !lq.all_workspaces.is_some_and(|x| x) { sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); } diff --git a/backend/windmill-api/src/schedule.rs b/backend/windmill-api/src/schedule.rs index 7cda4a1157..e084530fb1 100644 --- a/backend/windmill-api/src/schedule.rs +++ b/backend/windmill-api/src/schedule.rs @@ -153,38 +153,36 @@ async fn create_schedule( ) .await?; - let schedule = sqlx::query_as!( - Schedule, + let schedule = sqlx::query_as::<_, Schedule>( "INSERT INTO schedule (workspace_id, path, schedule, timezone, edited_by, script_path, \ is_flow, args, enabled, email, on_failure, on_failure_times, on_failure_exact, \ on_failure_extra_args, on_recovery, on_recovery_times, on_recovery_extra_args, \ ws_error_handler_muted, retry, summary, no_flow_overlap, tag \ ) VALUES ( \ $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22 \ - ) RETURNING *", - w_id, - ns.path, - ns.schedule, - ns.timezone, - &authed.username, - ns.script_path, - ns.is_flow, - ns.args, - ns.enabled.unwrap_or(false), - &authed.email, - ns.on_failure, - ns.on_failure_times, - ns.on_failure_exact, - ns.on_failure_extra_args, - ns.on_recovery, - ns.on_recovery_times, - ns.on_recovery_extra_args, - ns.ws_error_handler_muted.unwrap_or(false), - ns.retry, - ns.summary, - ns.no_flow_overlap.unwrap_or(false), - ns.tag, - ) + ) RETURNING *") + .bind(&w_id) + .bind(&ns.path) + .bind(&ns.schedule) + .bind(&ns.timezone) + .bind(&authed.username) + .bind(&ns.script_path) + .bind(&ns.is_flow) + .bind(&ns.args) + .bind(&ns.enabled.unwrap_or(false)) + .bind(&authed.email) + .bind(&ns.on_failure) + .bind(&ns.on_failure_times) + .bind(&ns.on_failure_exact) + .bind(&ns.on_failure_extra_args) + .bind(&ns.on_recovery) + .bind(&ns.on_recovery_times) + .bind(&ns.on_recovery_extra_args) + .bind(&ns.ws_error_handler_muted.unwrap_or(false)) + .bind(&ns.retry) + .bind(&ns.summary) + .bind(&ns.no_flow_overlap.unwrap_or(false)) + .bind(&ns.tag) .fetch_one(&mut tx) .await .map_err(|e| Error::InternalErr(format!("inserting schedule in {w_id}: {e}")))?; @@ -245,31 +243,29 @@ async fn edit_schedule( cron::Schedule::from_str(&es.schedule).map_err(|e| Error::BadRequest(e.to_string()))?; clear_schedule(tx.transaction_mut(), path, &w_id).await?; - let schedule = sqlx::query_as!( - Schedule, + let schedule = sqlx::query_as::<_, Schedule>( "UPDATE schedule SET schedule = $1, timezone = $2, args = $3, on_failure = $4, on_failure_times = $5, \ on_failure_exact = $6, on_failure_extra_args = $7, on_recovery = $8, on_recovery_times = $9, \ on_recovery_extra_args = $10, ws_error_handler_muted = $11, retry = $12, summary = $13, \ no_flow_overlap = $14, tag = $15 - WHERE path = $16 AND workspace_id = $17 RETURNING *", - es.schedule, - es.timezone, - es.args, - es.on_failure, - es.on_failure_times, - es.on_failure_exact, - es.on_failure_extra_args, - es.on_recovery, - es.on_recovery_times, - es.on_recovery_extra_args, - es.ws_error_handler_muted.unwrap_or(false), - es.retry, - es.summary, - es.no_flow_overlap.unwrap_or(false), - es.tag, - path, - w_id, - ) + WHERE path = $16 AND workspace_id = $17 RETURNING *") + .bind(&es.schedule) + .bind(&es.timezone) + .bind(&es.args) + .bind(&es.on_failure) + .bind(&es.on_failure_times) + .bind(&es.on_failure_exact) + .bind(&es.on_failure_extra_args) + .bind(&es.on_recovery) + .bind(&es.on_recovery_times) + .bind(&es.on_recovery_extra_args) + .bind(&es.ws_error_handler_muted.unwrap_or(false)) + .bind(&es.retry) + .bind(&es.summary) + .bind(&es.no_flow_overlap.unwrap_or(false)) + .bind(&es.tag) + .bind(&path) + .bind(&w_id) .fetch_one(&mut tx) .await .map_err(|e| Error::InternalErr(format!("updating schedule in {w_id}: {e}")))?; @@ -473,14 +469,12 @@ pub async fn set_enabled( let mut tx: QueueTransaction<'_, rsmq_async::MultiplexedRsmq> = (rsmq.clone(), user_db.begin(&authed).await?).into(); let path = path.to_path(); - let schedule_o = sqlx::query_as!( - Schedule, - "UPDATE schedule SET enabled = $1, email = $2 WHERE path = $3 AND workspace_id = $4 RETURNING *", - &payload.enabled, - authed.email, - path, - w_id - ) + let schedule_o = sqlx::query_as::<_, Schedule>( + "UPDATE schedule SET enabled = $1, email = $2 WHERE path = $3 AND workspace_id = $4 RETURNING *") + .bind(&payload.enabled) + .bind(&authed.email) + .bind(&path) + .bind(&w_id) .fetch_optional(&mut tx) .await?; diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index 150ec4adba..f45cf13360 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -25,6 +25,7 @@ use axum::{ use hyper::StatusCode; use serde::{Deserialize, Serialize}; use serde_json::json; +use serde_json::value::RawValue; use sql_builder::prelude::*; use sqlx::{FromRow, Postgres, Transaction}; use std::{ @@ -51,6 +52,7 @@ use windmill_common::{ utils::{ not_found_if_none, paginate, query_elems_from_hub, require_admin, Pagination, StripPath, }, + worker::to_raw_value, HUB_BASE_URL, }; use windmill_git_sync::{handle_deployment_metadata, DeployedObject}; @@ -70,7 +72,7 @@ pub struct ScriptWDraft { pub kind: ScriptKind, pub tag: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub draft: Option, + pub draft: Option>>, pub schema: Option, #[serde(skip_serializing_if = "Option::is_none")] pub draft_only: Option, @@ -622,22 +624,19 @@ async fn create_script_internal<'c>( .execute(&mut tx) .await?; - let mut schedulables = sqlx::query_as!( - Schedule, - "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS false RETURNING *", - ns.path, - p_path, - w_id, - ) + let mut schedulables = sqlx::query_as::<_, Schedule>( + "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS false RETURNING *") + .bind(&ns.path) + .bind(&p_path) + .bind(&w_id) .fetch_all(&mut tx) .await?; - let schedule = sqlx::query_as!(Schedule, - "UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS false RETURNING *", - ns.path, - p_path, - w_id, - ) + let schedule = sqlx::query_as::<_, Schedule>( + "UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS false RETURNING *") + .bind(&ns.path) + .bind(&p_path) + .bind(&w_id) .fetch_optional(&mut tx) .await?; @@ -716,12 +715,12 @@ async fn create_script_internal<'c>( ns.tag }; - let mut args: HashMap = HashMap::new(); + let mut args: HashMap> = HashMap::new(); if let Some(dm) = ns.deployment_message { - args.insert("deployment_message".to_string(), json!(dm)); + args.insert("deployment_message".to_string(), to_raw_value(&dm)); } if let Some(ref p_path) = p_path_opt { - args.insert("parent_path".to_string(), json!(p_path)); + args.insert("parent_path".to_string(), to_raw_value(&p_path)); } let tx = PushIsolationLevel::Transaction(tx); @@ -735,7 +734,7 @@ async fn create_script_internal<'c>( path: ns.path, dedicated_worker: ns.dedicated_worker, }, - args, + args.into(), &authed.username, &authed.email, permissioned_as, diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index f5ec286578..cbe287092c 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -41,7 +41,7 @@ use windmill_common::s3_helpers::LargeFileStorage; use windmill_common::schedule::Schedule; use windmill_common::users::username_to_permissioned_as; use windmill_common::variables::build_crypt; -use windmill_common::worker::CLOUD_HOSTED; +use windmill_common::worker::{to_raw_value, CLOUD_HOSTED}; use windmill_common::workspaces::WorkspaceGitSyncSettings; use windmill_common::{ error::{to_anyhow, Error, JsonResult, Result}, @@ -58,7 +58,7 @@ use crate::oauth2_ee::InstanceEvent; use crate::variables::{decrypt, encrypt}; use hyper::{header, StatusCode}; use serde::{Deserialize, Serialize}; -use serde_json::{json, Map, Value}; +use serde_json::Value; use sqlx::{FromRow, Postgres, Transaction}; use tempfile::TempDir; use tokio::fs::File; @@ -476,15 +476,15 @@ async fn run_slack_message_test_job( Path(w_id): Path, Json(req): Json, ) -> JsonResult { - let mut fake_result = Map::new(); - fake_result.insert("error".to_string(), json!(req.test_msg)); - fake_result.insert("success_result".to_string(), json!(req.test_msg)); + let mut fake_result = HashMap::new(); + fake_result.insert("error".to_string(), to_raw_value(&req.test_msg)); + fake_result.insert("success_result".to_string(), to_raw_value(&req.test_msg)); - let mut extra_args = Map::new(); - extra_args.insert("channel".to_string(), json!(req.channel)); + let mut extra_args = HashMap::new(); + extra_args.insert("channel".to_string(), to_raw_value(&req.channel)); extra_args.insert( "slack".to_string(), - json!(format!("$res:{WORKSPACE_SLACK_BOT_TOKEN_PATH}")), + to_raw_value(&format!("$res:{WORKSPACE_SLACK_BOT_TOKEN_PATH}")), ); let uuid = windmill_queue::push_error_handler( @@ -499,7 +499,7 @@ async fn run_slack_message_test_job( sqlx::types::Json(&fake_result), None, Some(Utc::now()), - Some(json!(extra_args)), + Some(sqlx::types::Json(to_raw_value(&extra_args))), authed.email.as_str(), false, false, @@ -2588,14 +2588,13 @@ async fn tarball_workspace( } { - let apps = sqlx::query_as!( - AppWithLastVersion, + let apps = sqlx::query_as::<_, AppWithLastVersion>( "SELECT app.id, app.path, app.summary, app.versions, app.policy, app.extra_perms, app_version.value, app_version.created_at, app_version.created_by from app, app_version WHERE app.workspace_id = $1 AND app_version.id = app.versions[array_upper(app.versions, 1)]", - &w_id ) + .bind(&w_id) .fetch_all(&mut *tx) .await?; @@ -2608,12 +2607,11 @@ async fn tarball_workspace( } if include_schedules.unwrap_or(false) { - let schedules = sqlx::query_as!( - Schedule, + let schedules = sqlx::query_as::<_, Schedule>( "SELECT * FROM schedule WHERE workspace_id = $1", - &w_id ) + .bind(&w_id) .fetch_all(&mut *tx) .await?; diff --git a/backend/windmill-common/src/flow_status.rs b/backend/windmill-common/src/flow_status.rs index 3aa849e4fb..77a9840006 100644 --- a/backend/windmill-common/src/flow_status.rs +++ b/backend/windmill-common/src/flow_status.rs @@ -13,7 +13,6 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::flows::FlowValue; -use crate::more_serde::default_false; const MINUTES: Duration = Duration::from_secs(60); const HOURS: Duration = MINUTES.saturating_mul(60); @@ -71,7 +70,7 @@ pub struct RestartedFrom { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Iterator { pub index: usize, - pub itered: Vec, + pub itered: Vec>, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -111,7 +110,23 @@ pub struct FlowCleanupModule { pub flow_jobs_to_clean: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Deserialize)] +struct UntaggedFlowStatusModule { + #[serde(rename = "type")] + type_: String, + id: Option, + count: Option, + job: Option, + iterator: Option, + flow_jobs: Option>, + branch_chosen: Option, + branchall: Option, + parallel: Option, + while_loop: Option, + approvers: Option>, +} + +#[derive(Serialize, Debug, Clone)] #[serde(tag = "type")] pub enum FlowStatusModule { WaitingForPriorSteps { @@ -138,10 +153,8 @@ pub enum FlowStatusModule { #[serde(skip_serializing_if = "Option::is_none")] branchall: Option, #[serde(skip_serializing_if = "std::ops::Not::not")] - #[serde(default = "default_false")] parallel: bool, #[serde(skip_serializing_if = "std::ops::Not::not")] - #[serde(default = "default_false")] while_loop: bool, }, Success { @@ -165,6 +178,89 @@ pub enum FlowStatusModule { }, } +impl<'de> Deserialize<'de> for FlowStatusModule { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let untagged: UntaggedFlowStatusModule = + UntaggedFlowStatusModule::deserialize(deserializer)?; + + match untagged.type_.as_str() { + "WaitingForPriorSteps" => Ok(FlowStatusModule::WaitingForPriorSteps { + id: untagged + .id + .ok_or_else(|| serde::de::Error::missing_field("id"))?, + }), + "WaitingForEvents" => Ok(FlowStatusModule::WaitingForEvents { + id: untagged + .id + .ok_or_else(|| serde::de::Error::missing_field("id"))?, + count: untagged + .count + .ok_or_else(|| serde::de::Error::missing_field("count"))?, + job: untagged + .job + .ok_or_else(|| serde::de::Error::missing_field("job"))?, + }), + "WaitingForExecutor" => Ok(FlowStatusModule::WaitingForExecutor { + id: untagged + .id + .ok_or_else(|| serde::de::Error::missing_field("id"))?, + job: untagged + .job + .ok_or_else(|| serde::de::Error::missing_field("job"))?, + }), + "InProgress" => Ok(FlowStatusModule::InProgress { + id: untagged + .id + .ok_or_else(|| serde::de::Error::missing_field("id"))?, + job: untagged + .job + .ok_or_else(|| serde::de::Error::missing_field("job"))?, + iterator: untagged.iterator, + flow_jobs: untagged.flow_jobs, + branch_chosen: untagged.branch_chosen, + branchall: untagged.branchall, + parallel: untagged.parallel.unwrap_or(false), + while_loop: untagged.while_loop.unwrap_or(false), + }), + "Success" => Ok(FlowStatusModule::Success { + id: untagged + .id + .ok_or_else(|| serde::de::Error::missing_field("id"))?, + job: untagged + .job + .ok_or_else(|| serde::de::Error::missing_field("job"))?, + flow_jobs: untagged.flow_jobs, + branch_chosen: untagged.branch_chosen, + approvers: untagged.approvers.unwrap_or_default(), + }), + "Failure" => Ok(FlowStatusModule::Failure { + id: untagged + .id + .ok_or_else(|| serde::de::Error::missing_field("id"))?, + job: untagged + .job + .ok_or_else(|| serde::de::Error::missing_field("job"))?, + flow_jobs: untagged.flow_jobs, + branch_chosen: untagged.branch_chosen, + }), + other => Err(serde::de::Error::unknown_variant( + other, + &[ + "WaitingForPriorSteps", + "WaitingForEvents", + "WaitingForExecutor", + "InProgress", + "Success", + "Failure", + ], + )), + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub enum JobResult { SingleJob(Uuid), diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index 05eca9d83b..e7986c4de4 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -16,9 +16,7 @@ use rand::Rng; use serde::{Deserialize, Serialize, Serializer}; use crate::{ - more_serde::{ - default_empty_string, default_false, default_id, default_null, default_true, is_default, - }, + more_serde::{default_empty_string, default_id, default_null, default_true, is_default}, scripts::{Schema, ScriptHash, ScriptLang}, }; @@ -220,7 +218,7 @@ pub struct Mock { pub struct FlowModule { #[serde(default = "default_id")] pub id: String, - pub value: FlowModuleValue, + pub value: Box, #[serde(skip_serializing_if = "Option::is_none")] pub stop_after_if: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -246,13 +244,82 @@ pub struct FlowModule { pub continue_on_error: Option, } +#[derive(Deserialize)] +pub struct FlowModuleValueType { + #[serde(rename = "type")] + pub type_: String, +} + +#[derive(Deserialize)] +pub struct FlowModuleValueWithParallel { + #[serde(rename = "type")] + pub type_: String, + pub parallel: Option, + pub parallelism: Option, +} + impl FlowModule { pub fn id_append(&mut self, s: &str) { self.id = format!("{}-{}", self.id, s); } + pub fn get_value(&self) -> anyhow::Result { + serde_json::from_str::(self.value.get()).map_err(crate::error::to_anyhow) + } + + pub fn is_flow(&self) -> bool { + self.get_type().is_ok_and(|x| x == "flow") + } + + pub fn get_value_with_parallel(&self) -> anyhow::Result { + serde_json::from_str::(self.value.get()) + .map_err(crate::error::to_anyhow) + } + + pub fn is_simple(&self) -> bool { + self.get_type() + .is_ok_and(|x| x == "flow" || x == "script" || x == "rawscript") + } + + pub fn get_type(&self) -> anyhow::Result { + serde_json::from_str::(self.value.get()) + .map_err(crate::error::to_anyhow) + .map(|x| x.type_) + } } -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Deserialize)] +pub struct UntaggedInputTransform { + #[serde(rename = "type")] + pub type_: String, + pub value: Option>, + pub expr: Option, +} + +impl<'de> Deserialize<'de> for InputTransform { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let untagged: UntaggedInputTransform = UntaggedInputTransform::deserialize(deserializer)?; + + match untagged.type_.as_str() { + "static" => { + let value = untagged.value.unwrap_or_else(default_null); + Ok(InputTransform::Static { value }) + } + "javascript" => { + let expr = untagged.expr.unwrap_or_else(default_empty_string); + Ok(InputTransform::Javascript { expr }) + } + other => Err(serde::de::Error::unknown_variant( + other, + &["static", "javascript"], + )), + } + } +} + +#[derive(Serialize, Debug, Clone)] #[serde( tag = "type", rename_all(serialize = "lowercase", deserialize = "lowercase") @@ -260,7 +327,7 @@ impl FlowModule { pub enum InputTransform { Static { #[serde(default = "default_null")] - value: serde_json::Value, + value: Box, }, Javascript { #[serde(default = "default_empty_string")] @@ -269,25 +336,19 @@ pub enum InputTransform { } #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct BranchOneModules { +pub struct Branch { #[serde(skip_serializing_if = "Option::is_none")] pub summary: Option, + #[serde(default = "default_empty_string")] pub expr: String, pub modules: Vec, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct BranchAllModules { - #[serde(skip_serializing_if = "Option::is_none")] - pub summary: Option, - pub modules: Vec, #[serde(default = "default_true")] pub skip_failure: bool, #[serde(default = "default_true")] pub parallel: bool, } -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Debug, Clone)] #[serde( tag = "type", rename_all(serialize = "lowercase", deserialize = "lowercase") @@ -313,7 +374,6 @@ pub enum FlowModuleValue { modules: Vec, #[serde(default = "default_true")] skip_failures: bool, - #[serde(default = "default_false")] parallel: bool, #[serde(skip_serializing_if = "Option::is_none")] parallelism: Option, @@ -324,11 +384,11 @@ pub enum FlowModuleValue { skip_failures: bool, }, BranchOne { - branches: Vec, + branches: Vec, default: Vec, }, BranchAll { - branches: Vec, + branches: Vec, #[serde(default = "default_true")] parallel: bool, }, @@ -354,6 +414,123 @@ pub enum FlowModuleValue { Identity, } +#[derive(Deserialize)] +struct UntaggedFlowModuleValue { + #[serde(rename = "type")] + type_: String, + #[serde(alias = "input_transform")] + input_transforms: Option>, + path: Option, + hash: Option, + tag_override: Option, + iterator: Option, + modules: Option>, + skip_failures: Option, + parallel: Option, + parallelism: Option, + branches: Option>, + default: Option>, + content: Option, + lock: Option, + tag: Option, + language: Option, + custom_concurrency_key: Option, + concurrent_limit: Option, + concurrency_time_window_s: Option, +} + +impl<'de> Deserialize<'de> for FlowModuleValue { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let untagged: UntaggedFlowModuleValue = UntaggedFlowModuleValue::deserialize(deserializer)?; + + match untagged.type_.as_str() { + "script" => Ok(FlowModuleValue::Script { + input_transforms: untagged.input_transforms.unwrap_or_default(), + path: untagged + .path + .ok_or_else(|| serde::de::Error::missing_field("path"))?, + hash: untagged.hash, + tag_override: untagged.tag_override, + }), + "flow" => Ok(FlowModuleValue::Flow { + input_transforms: untagged.input_transforms.unwrap_or_default(), + path: untagged + .path + .ok_or_else(|| serde::de::Error::missing_field("path"))?, + }), + "forloopflow" => Ok(FlowModuleValue::ForloopFlow { + iterator: untagged + .iterator + .ok_or_else(|| serde::de::Error::missing_field("iterator"))?, + modules: untagged + .modules + .ok_or_else(|| serde::de::Error::missing_field("modules"))?, + skip_failures: untagged.skip_failures.unwrap_or(true), + parallel: untagged.parallel.unwrap_or(false), + parallelism: untagged.parallelism, + }), + "whileloopflow" => Ok(FlowModuleValue::WhileloopFlow { + modules: untagged + .modules + .ok_or_else(|| serde::de::Error::missing_field("modules"))?, + skip_failures: untagged.skip_failures.unwrap_or(false), + }), + "branchone" => Ok(FlowModuleValue::BranchOne { + branches: untagged + .branches + .ok_or_else(|| serde::de::Error::missing_field("branches"))?, + default: untagged + .default + .ok_or_else(|| serde::de::Error::missing_field("default"))?, + }), + "branchall" => Ok(FlowModuleValue::BranchAll { + branches: untagged + .branches + .ok_or_else(|| serde::de::Error::missing_field("branches"))?, + parallel: untagged.parallel.unwrap_or(true), + }), + "rawscript" => Ok(FlowModuleValue::RawScript { + input_transforms: untagged.input_transforms.unwrap_or_default(), + content: untagged + .content + .ok_or_else(|| serde::de::Error::missing_field("content"))?, + lock: untagged.lock, + path: untagged.path, + tag: untagged.tag, + language: untagged + .language + .ok_or_else(|| serde::de::Error::missing_field("language"))?, + custom_concurrency_key: untagged.custom_concurrency_key, + concurrent_limit: untagged.concurrent_limit, + concurrency_time_window_s: untagged.concurrency_time_window_s, + }), + "identity" => Ok(FlowModuleValue::Identity), + other => Err(serde::de::Error::unknown_variant( + other, + &[ + "script", + "flow", + "forloopflow", + "whileloopflow", + "branchone", + "branchall", + "rawscript", + "identity", + ], + )), + } + } +} + +impl Into> for FlowModuleValue { + fn into(self) -> Box { + crate::worker::to_raw_value(&self) + } +} + impl FlowModuleValue { pub fn is_simple(&self) -> bool { match self { @@ -391,7 +568,7 @@ pub fn add_virtual_items_if_necessary(modules: &mut Vec) { { modules.push(FlowModule { id: format!("{}-v", modules[modules.len() - 1].id), - value: FlowModuleValue::Identity, + value: crate::worker::to_raw_value(&FlowModuleValue::Identity), stop_after_if: None, summary: Some("Virtual module needed for suspend/sleep when last module".to_string()), mock: None, diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index a059b00377..a9762b0a72 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -137,9 +137,11 @@ impl QueuedJob { } pub fn parse_raw_flow(&self) -> Option { - self.raw_flow - .as_ref() - .and_then(|v| serde_json::from_str::((**v).get()).ok()) + self.raw_flow.as_ref().and_then(|v| { + let str = (**v).get(); + // tracing::error!("raw_flow: {}", str); + return serde_json::from_str::(str).ok(); + }) } pub fn parse_flow_status(&self) -> Option { @@ -327,7 +329,7 @@ pub enum JobPayload { SingleScriptFlow { path: String, hash: ScriptHash, - args: HashMap, + args: HashMap>, retry: Retry, // for now only used to retry the script, so retry is necessarily present custom_concurrency_key: Option, concurrent_limit: Option, diff --git a/backend/windmill-common/src/more_serde.rs b/backend/windmill-common/src/more_serde.rs index f879a07abb..97df70219c 100644 --- a/backend/windmill-common/src/more_serde.rs +++ b/backend/windmill-common/src/more_serde.rs @@ -10,6 +10,7 @@ use crate::utils::rd_string; use serde::{Deserialize, Deserializer}; +use serde_json::value::RawValue; use std::{fmt::Display, str::FromStr}; pub fn default_true() -> bool { @@ -20,8 +21,8 @@ pub fn default_false() -> bool { false } -pub fn default_null() -> serde_json::Value { - serde_json::Value::Null +pub fn default_null() -> Box { + RawValue::from_string("null".to_string()).unwrap() } pub fn default_empty_string() -> String { diff --git a/backend/windmill-common/src/schedule.rs b/backend/windmill-common/src/schedule.rs index 7de389333f..c476a54b9a 100644 --- a/backend/windmill-common/src/schedule.rs +++ b/backend/windmill-common/src/schedule.rs @@ -23,7 +23,7 @@ pub struct Schedule { pub enabled: bool, pub script_path: String, pub is_flow: bool, - pub args: Option, + pub args: Option>>, pub extra_perms: serde_json::Value, pub email: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -35,13 +35,13 @@ pub struct Schedule { #[serde(skip_serializing_if = "Option::is_none")] pub on_failure_exact: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub on_failure_extra_args: Option, + pub on_failure_extra_args: Option>>, #[serde(skip_serializing_if = "Option::is_none")] pub on_recovery: Option, #[serde(skip_serializing_if = "Option::is_none")] pub on_recovery_times: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub on_recovery_extra_args: Option, + pub on_recovery_extra_args: Option>>, pub ws_error_handler_muted: bool, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 18dad02dc8..804e45eb13 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -762,13 +762,18 @@ pub async fn add_completed_job< } } if queued_job.concurrent_limit.is_some() { - let concurrency_key = concurrency_key(db, queued_job).await.unwrap_or_else(|e| { - tracing::error!( - "Could not get concurrency key for job {} defaulting to default key: {e:?}", - queued_job.id - ); - return queued_job.full_path_with_workspace(); - }); + let concurrency_key = match concurrency_key(db, queued_job).await { + Ok(c) => c, + Err(e) => { + tracing::error!( + "Could not get concurrency key for job {} defaulting to default key: {e:?}", + queued_job.id + ); + legacy_concurrency_key(db, queued_job) + .await + .unwrap_or_else(|| queued_job.full_path_with_workspace()) + } + }; if let Err(e) = sqlx::query_scalar!( "UPDATE concurrency_counter SET job_uuids = job_uuids - $2 WHERE concurrency_id = $1", concurrency_key, @@ -982,7 +987,11 @@ pub async fn add_completed_job< .unwrap_or_else(|| ScriptLang::Deno), priority: queued_job.priority, }, - queued_job.args.clone(), + queued_job + .args + .as_ref() + .map(|x| PushArgs { args: x.0.clone(), extra: HashMap::new() }) + .unwrap_or_else(PushArgs::empty), &queued_job.created_by, &queued_job.email, queued_job.permissioned_as.clone(), @@ -1063,7 +1072,7 @@ pub async fn report_error_to_workspace_handler_or_critical_side_channel< error_message: String, ) -> () { let w_id = &queued_job.workspace_id; - let (error_handler, error_handler_extra_args) = sqlx::query_as::<_, (Option, Option)>( + let (error_handler, error_handler_extra_args) = sqlx::query_as::<_, (Option, Option>>)>( "SELECT error_handler, error_handler_extra_args FROM workspace_settings WHERE workspace_id = $1", ).bind(&w_id) .fetch_optional(db) @@ -1122,7 +1131,7 @@ pub async fn send_error_to_workspace_handler< result: Json<&'a T>, ) -> Result<(), Error> { let w_id = &queued_job.workspace_id; - let (error_handler, error_handler_extra_args, error_handler_muted_on_cancel) = sqlx::query_as::<_, (Option, Option, bool)>( + let (error_handler, error_handler_extra_args, error_handler_muted_on_cancel) = sqlx::query_as::<_, (Option, Option>>, bool)>( "SELECT error_handler, error_handler_extra_args, error_handler_muted_on_cancel FROM workspace_settings WHERE workspace_id = $1", ).bind(&w_id) .fetch_optional(db) @@ -1256,10 +1265,10 @@ pub async fn handle_maybe_scheduled_job<'c, R: rsmq_async::RsmqConnection + Clon } } -#[derive(Clone, Serialize)] +#[derive(Clone, Serialize, FromRow)] struct CompletedJobSubset { success: bool, - result: Option, + result: Option>>, started_at: chrono::DateTime, } async fn apply_schedule_handlers< @@ -1285,15 +1294,15 @@ async fn apply_schedule_handlers< let times = schedule.on_failure_times.unwrap_or(1).max(1); let exact = schedule.on_failure_exact.unwrap_or(false); if times > 1 || exact { - let past_jobs = sqlx::query_as!( - CompletedJobSubset, + let past_jobs = sqlx::query_as::<_, CompletedJobSubset>( "SELECT success, result, started_at FROM completed_job WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4 ORDER BY created_at DESC LIMIT $5", - &schedule.workspace_id, - &schedule.path, - script_path, - job_id, - if exact { times } else { times - 1 } as i64, - ).fetch_all(db).await?; + ) + .bind(&schedule.workspace_id) + .bind(&schedule.path) + .bind(script_path) + .bind(job_id) + .bind(if exact { times } else { times - 1 } as i64,) + .fetch_all(db).await?; let match_times = if exact { past_jobs.len() == times as usize @@ -1334,15 +1343,15 @@ async fn apply_schedule_handlers< if let Some(on_recovery_path) = schedule.on_recovery.clone() { let mut tx: QueueTransaction<'_, R> = (rsmq.clone(), db.begin().await?).into(); let times = schedule.on_recovery_times.unwrap_or(1).max(1); - let past_jobs = sqlx::query_as!( - CompletedJobSubset, + let past_jobs = sqlx::query_as::<_, CompletedJobSubset>( "SELECT success, result, started_at FROM completed_job WHERE workspace_id = $1 AND schedule_path = $2 AND script_path = $3 AND id != $4 ORDER BY created_at DESC LIMIT $5", - &schedule.workspace_id, - &schedule.path, - script_path, - job_id, - times as i64, - ).fetch_all(db).await?; + ) + .bind(&schedule.workspace_id) + .bind(&schedule.path) + .bind(script_path) + .bind(job_id) + .bind(times as i64) + .fetch_all(db).await?; if past_jobs.len() < times as usize { return Ok(()); @@ -1399,7 +1408,7 @@ pub async fn push_error_handler< result: Json<&'a T>, failed_times: Option, started_at: Option>, - extra_args: Option, + extra_args: Option>>, email: &str, is_schedule_error_handler: bool, is_global_error_handler: bool, @@ -1428,10 +1437,8 @@ pub async fn push_error_handler< } if let Some(args_v) = extra_args { - if let serde_json::Value::Object(args_m) = args_v { - for (k, v) in args_m { - extra.insert(k, to_raw_value(&v)); - } + if let Ok(args_m) = serde_json::from_str::>>(args_v.get()) { + extra.extend(args_m); } else { return Err(error::Error::ExecutionErr( "args of scripts needs to be dict".to_string(), @@ -1457,7 +1464,7 @@ pub async fn push_error_handler< tx, handler_w_id, payload, - PushArgs { extra, args: Json(&result) }, + PushArgs { extra, args: result }, if is_global_error_handler { "global" } else if is_schedule_error_handler { @@ -1496,16 +1503,10 @@ pub async fn push_error_handler< return Ok(uuid); } -fn sanitize_result(result: Json<&T>) -> serde_json::Value { - let result = - serde_json::from_str(&serde_json::to_string(result.0).unwrap_or_else(|_| "{}".to_string())) - .unwrap_or_else(|_| json!({})); - let result = if result.is_object() || result.is_null() { - result - } else { - json!({ "error": result }) - }; - result +fn sanitize_result(result: Json<&T>) -> HashMap> { + let as_str = serde_json::to_string(result.0).unwrap_or_else(|_| "{}".to_string()); + serde_json::from_str::>>(&as_str) + .unwrap_or_else(|_| [("error".to_string(), RawValue::from_string(as_str).unwrap())].into()) } // #[derive(Serialize)] @@ -1534,33 +1535,33 @@ async fn handle_recovered_schedule< successful_job_result: Json<&'a T>, successful_times: i32, successful_job_started_at: DateTime, - extra_args: Option, + extra_args: Option>>, ) -> windmill_common::error::Result> { let (payload, tag) = get_payload_tag_from_prefixed_path(on_recovery_path, db, w_id).await?; - let mut args = error_job - .result - .unwrap_or(json!({})) - .as_object() - .unwrap() - .clone(); - args.insert("error_started_at".to_string(), json!(error_job.started_at)); - args.insert("schedule_path".to_string(), json!(schedule_path)); - args.insert("path".to_string(), json!(script_path)); - args.insert("is_flow".to_string(), json!(is_flow)); - args.insert( - "success_result".to_string(), - serde_json::from_str(&serde_json::to_string(&successful_job_result).unwrap()) - .unwrap_or_else(|_| json!("{}")), + let mut extra = HashMap::new(); + extra.insert( + "error_started_at".to_string(), + to_raw_value(&error_job.started_at), ); - args.insert("success_times".to_string(), json!(successful_times)); - args.insert( + extra.insert("schedule_path".to_string(), to_raw_value(&schedule_path)); + extra.insert("path".to_string(), to_raw_value(&script_path)); + extra.insert("is_flow".to_string(), to_raw_value(&is_flow)); + extra.insert( + "success_result".to_string(), + serde_json::from_str::>( + &serde_json::to_string(&successful_job_result).unwrap(), + ) + .unwrap_or_else(|_| serde_json::value::RawValue::from_string("{}".to_string()).unwrap()), + ); + extra.insert("success_times".to_string(), to_raw_value(&successful_times)); + extra.insert( "success_started_at".to_string(), - json!(successful_job_started_at), + to_raw_value(&successful_job_started_at), ); if let Some(args_v) = extra_args { - if let serde_json::Value::Object(args_m) = args_v { - args.extend(args_m); + if let Ok(args_m) = serde_json::from_str::>>(args_v.get()) { + extra.extend(args_m); } else { return Err(error::Error::ExecutionErr( "args of scripts needs to be dict".to_string(), @@ -1574,15 +1575,21 @@ async fn handle_recovered_schedule< { // default slack error handler being used -> we need to inject the slack token let slack_resource = format!("$res:{WORKSPACE_SLACK_BOT_TOKEN_PATH}"); - args.insert("slack".to_string(), json!(slack_resource)); + extra.insert("slack".to_string(), to_raw_value(&slack_resource)); } + + let args = error_job + .result + .and_then(|x| serde_json::from_str::>>(x.0.get()).ok()) + .unwrap_or_else(HashMap::new); + let tx = PushIsolationLevel::Transaction(tx); let (uuid, tx) = push( &db, tx, w_id, payload, - args, + PushArgs { extra: extra, args: args }, SCHEDULE_RECOVERY_HANDLER_USERNAME, SCHEDULE_RECOVERY_HANDLER_USER_EMAIL, ERROR_HANDLER_USER_GROUP.to_string(), @@ -2017,6 +2024,42 @@ pub async fn custom_concurrency_key( .await } +async fn legacy_concurrency_key(db: &Pool, queued_job: &QueuedJob) -> Option { + let r = if queued_job.is_flow() { + sqlx::query_scalar!( + "SELECT value->>'concurrency_key' FROM flow WHERE path = $1 AND workspace_id = $2", + queued_job.script_path, + queued_job.workspace_id + ) + .fetch_optional(db) + .await + } else { + sqlx::query_scalar!( + "SELECT concurrency_key FROM script WHERE hash = $1 AND workspace_id = $2", + queued_job.script_hash.unwrap_or(ScriptHash(0)).0, + queued_job.workspace_id + ) + .fetch_optional(db) + .await + } + .ok() + .flatten() + .flatten(); + + r.map(|x| { + interpolate_args( + x, + &queued_job + .args + .clone() + .map(|x| x.0) + .unwrap_or_default() + .into(), + &queued_job.workspace_id, + ) + }) +} + async fn concurrency_key( db: &Pool, queued_job: &QueuedJob, @@ -2028,25 +2071,18 @@ async fn concurrency_key( ) } -fn interpolate_args( - x: String, - args: &T, - workspace_id: &str, - parsed_args: &mut Option, -) -> String { +fn interpolate_args(x: String, args: &PushArgs, workspace_id: &str) -> String { // Save this value to avoid parsing twice - if parsed_args.is_none() { - *parsed_args = Some(serde_json::to_value(args).unwrap_or_default()); - } - let value = parsed_args.as_ref().unwrap(); let workspaced = x.as_str().replace("$workspace", workspace_id).to_string(); if RE_ARG_TAG.is_match(&workspaced) { let mut interpolated = workspaced.clone(); for cap in RE_ARG_TAG.captures_iter(&workspaced) { let arg_name = cap.get(1).unwrap().as_str(); - let arg_value = value + let arg_value = args + .args .get(arg_name) - .map(|x| serde_json::to_string(x).unwrap_or_default()) + .or(args.extra.get(arg_name)) + .map(|x| x.get()) .unwrap_or_default(); interpolated = interpolated.replace(format!("$args[{}]", arg_name).as_str(), &arg_value); @@ -2493,11 +2529,11 @@ macro_rules! fetch_scalar_isolated { use sqlx::types::JsonRawValue; #[derive(Serialize, Debug)] -pub struct PushArgs { +pub struct PushArgs { #[serde(flatten)] pub extra: HashMap>, #[serde(flatten)] - pub args: Json, + pub args: HashMap>, } #[derive(Deserialize)] @@ -2554,7 +2590,7 @@ impl DecodeQueries { } } -impl PushArgs { +impl PushArgs { pub fn insert, V: Into>>(&mut self, k: K, v: V) { self.extra.insert(k.into(), v.into()); } @@ -2567,7 +2603,7 @@ pub struct RequestQuery { } #[axum::async_trait] -impl FromRequest for PushArgs>> +impl FromRequest for PushArgs where S: Send + Sync, { @@ -2611,12 +2647,12 @@ where .unwrap_or_else(|| to_raw_value(&serde_json::Value::Null)); let mut hm = HashMap::new(); hm.insert("body".to_string(), args); - Ok(PushArgs { extra, args: Json(hm) }) + Ok(PushArgs { extra, args: hm }) } else { let hm = serde_json::from_str::>>>(&str) .map_err(|e| Error::BadRequest(format!("invalid json: {}", e)).into_response())? .unwrap_or_else(HashMap::new); - Ok(PushArgs { extra, args: Json(hm) }) + Ok(PushArgs { extra, args: hm }) } } else if content_type .unwrap() @@ -2642,7 +2678,7 @@ where .map(|(k, v)| (k, to_raw_value(&v))) .collect::>(); - return Ok(PushArgs { extra, args: Json(payload) }); + return Ok(PushArgs { extra, args: payload }); } else { Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response()) } @@ -2680,9 +2716,9 @@ pub fn build_extra( args } -impl PushArgs>> { +impl PushArgs { pub fn empty() -> Self { - PushArgs { extra: HashMap::new(), args: Json(HashMap::new()) } + PushArgs { extra: HashMap::new(), args: HashMap::new() } } } @@ -2690,9 +2726,9 @@ pub fn empty_result() -> Box { return JsonRawValue::from_string("{}".to_string()).unwrap(); } -impl From>> for PushArgs>> { +impl From>> for PushArgs { fn from(value: HashMap>) -> Self { - PushArgs { extra: HashMap::new(), args: Json(value) } + PushArgs { extra: HashMap::new(), args: value } } } @@ -2707,12 +2743,12 @@ lazy_static::lazy_static! { } // #[instrument(level = "trace", skip_all)] -pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection + Send + 'c>( +pub async fn push<'c, R: rsmq_async::RsmqConnection + Send + 'c>( _db: &Pool, mut tx: PushIsolationLevel<'c, R>, workspace_id: &str, job_payload: JobPayload, - args: T, + args: PushArgs, user: &str, mut email: &str, mut permissioned_as: String, @@ -3144,12 +3180,14 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection let flow_value = FlowValue { modules: vec![FlowModule { id: "a".to_string(), - value: windmill_common::flows::FlowModuleValue::Script { - input_transforms: input_transforms, - path: path.clone(), - hash: Some(hash), - tag_override: tag_override, - }, + value: windmill_common::worker::to_raw_value( + &windmill_common::flows::FlowModuleValue::Script { + input_transforms: input_transforms, + path: path.clone(), + hash: Some(hash), + tag_override: tag_override, + }, + ), stop_after_if: None, summary: None, suspend: None, @@ -3376,8 +3414,6 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection let per_workspace: bool = DEFAULT_TAGS_PER_WORKSPACE.load(std::sync::atomic::Ordering::Relaxed); - let mut parsed_args: Option = None; - let tag = if dedicated_worker.is_some_and(|x| x) { format!( "{}:{}{}", @@ -3400,8 +3436,7 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection tag = None; } - let interpolated_tag = - tag.map(|x| interpolate_args(x, &args, workspace_id, &mut parsed_args)); + let interpolated_tag = tag.map(|x| interpolate_args(x, &args, workspace_id)); let default = || { let ntag = if job_kind == JobKind::Flow @@ -3469,7 +3504,7 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection if concurrent_limit.is_some() { let concurrency_key = custom_concurrency_key - .map(|x| interpolate_args(x, &args, workspace_id, &mut parsed_args)) + .map(|x| interpolate_args(x, &args, workspace_id)) .unwrap_or(fullpath_with_workspace( workspace_id, script_path.as_ref(), @@ -3505,7 +3540,7 @@ pub async fn push<'c, T: Serialize + Send + Sync, R: rsmq_async::RsmqConnection script_path.clone(), raw_code, raw_lock, - Json(args) as Json, + Json(args) as Json, job_kind.clone() as JobKind, schedule_path, raw_flow.map(|f| serde_json::json!(f)), @@ -3669,8 +3704,8 @@ async fn restarted_flows_resolution( module.id() )))?; - match module_definition.value.clone() { - FlowModuleValue::BranchAll { branches, parallel, .. } => { + match module_definition.get_value() { + Ok(FlowModuleValue::BranchAll { branches, parallel, .. }) => { if parallel { return Err(Error::InternalErr(format!( "Module {} is a parallel branchall. It can only be restarted at a given branch if it's sequential", @@ -3702,7 +3737,7 @@ async fn restarted_flows_resolution( while_loop: false, }); } - FlowModuleValue::ForloopFlow { parallel, .. } => { + Ok(FlowModuleValue::ForloopFlow { parallel, .. }) => { if parallel { return Err(Error::InternalErr(format!( "Module {} is not parallel loop. It can only be restarted at a given iteration if it's sequential", diff --git a/backend/windmill-queue/src/schedule.rs b/backend/windmill-queue/src/schedule.rs index cf07df506f..04227bcc52 100644 --- a/backend/windmill-queue/src/schedule.rs +++ b/backend/windmill-queue/src/schedule.rs @@ -66,10 +66,12 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>( return Ok(tx); } - let mut args: serde_json::Map = serde_json::Map::new(); + let mut args: HashMap> = HashMap::new(); if let Some(args_v) = &schedule.args { - if let serde_json::Value::Object(args_m) = args_v { + if let Ok(args_m) = + serde_json::from_str::>>(args_v.get()) + { args = args_m.clone() } else { return Err(error::Error::ExecutionErr( @@ -121,7 +123,7 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>( err.to_string(), )) })?; - let mut static_args = HashMap::::new(); + let mut static_args = HashMap::>::new(); for (arg_name, arg_value) in args.clone() { static_args.insert(arg_name, arg_value); } @@ -186,7 +188,7 @@ pub async fn push_scheduled_job<'c, R: rsmq_async::RsmqConnection + Send + 'c>( tx, &schedule.workspace_id, payload, - args, + crate::PushArgs { args, extra: HashMap::new() }, &schedule_to_user(&schedule.path), &schedule.email, username_to_permissioned_as(&schedule.edited_by), @@ -213,12 +215,11 @@ pub async fn get_schedule_opt<'c>( w_id: &str, path: &str, ) -> Result> { - let schedule_opt = sqlx::query_as!( - Schedule, + let schedule_opt = sqlx::query_as::<_, Schedule>( "SELECT * FROM schedule WHERE path = $1 AND workspace_id = $2", - path, - w_id ) + .bind(path) + .bind(w_id) .fetch_optional(&mut **db) .await?; Ok(schedule_opt) diff --git a/backend/windmill-worker/src/js_eval.rs b/backend/windmill-worker/src/js_eval.rs index 91d9365ead..8856cb9db5 100644 --- a/backend/windmill-worker/src/js_eval.rs +++ b/backend/windmill-worker/src/js_eval.rs @@ -399,7 +399,7 @@ async function variable(path) {{ return await Deno.core.ops.op_variable(path); }} async function resource(path) {{ - return await Deno.core.ops.op_resource(path); + return JSON.parse(await Deno.core.ops.op_resource(path)); }} "#, ); @@ -518,14 +518,17 @@ async fn op_get_id( } #[op2(async)] -#[serde] +#[string] async fn op_resource( op_state: Rc>, #[string] path: String, -) -> Result { +) -> Result, anyhow::Error> { let client = op_state.borrow().borrow::().0.clone(); if let Some(client) = client { - client.get_resource_value_interpolated(&path, None).await + client + .get_resource_value_interpolated::>>(&path, None) + .await + .map(|x| x.map(|x| x.get().to_string())) } else { anyhow::bail!("No client found in op state"); } diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index cb95015468..56410351a4 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -1847,75 +1847,40 @@ async fn spawn_dedicated_workers_for_flow( let mut workers = vec![]; let mut script_path_to_worker: HashMap>> = HashMap::new(); for module in modules.iter() { - match &module.value { - FlowModuleValue::Script { path, hash, .. } => { - let key = format!( - "{}:{}", - path, - hash.clone() - .map(|x| x.to_string()) - .unwrap_or_else(|| "".to_string()) - ); - if let Some(sender) = script_path_to_worker.get(&key) { - workers.push((module.id.clone(), sender.clone(), None)); - } else { - if let Some(dedi_w) = spawn_dedicated_worker( - SpawnWorker::Script { path: path.to_string(), hash: hash.clone() }, - w_id, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - Some(module.id.clone()), - ) - .await - { - script_path_to_worker.insert(key, dedi_w.1.clone()); - workers.push(dedi_w); + let value = module.get_value(); + if let Ok(value) = value { + match &value { + FlowModuleValue::Script { path, hash, .. } => { + let key = format!( + "{}:{}", + path, + hash.clone() + .map(|x| x.to_string()) + .unwrap_or_else(|| "".to_string()) + ); + if let Some(sender) = script_path_to_worker.get(&key) { + workers.push((module.id.clone(), sender.clone(), None)); + } else { + if let Some(dedi_w) = spawn_dedicated_worker( + SpawnWorker::Script { path: path.to_string(), hash: hash.clone() }, + w_id, + killpill_tx.clone(), + killpill_rx, + db, + worker_dir, + base_internal_url, + worker_name, + job_completed_tx, + Some(module.id.clone()), + ) + .await + { + script_path_to_worker.insert(key, dedi_w.1.clone()); + workers.push(dedi_w); + } } } - } - FlowModuleValue::ForloopFlow { modules, .. } => { - let w = spawn_dedicated_workers_for_flow( - &modules, - path, - w_id, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - ) - .await; - workers.extend(w); - } - FlowModuleValue::WhileloopFlow { modules, .. } => { - let w = spawn_dedicated_workers_for_flow( - &modules, - path, - w_id, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - ) - .await; - workers.extend(w); - } - FlowModuleValue::BranchOne { branches, default } => { - for modules in branches - .iter() - .map(|x| &x.modules) - .chain(std::iter::once(default)) - { + FlowModuleValue::ForloopFlow { modules, .. } => { let w = spawn_dedicated_workers_for_flow( &modules, path, @@ -1931,11 +1896,9 @@ async fn spawn_dedicated_workers_for_flow( .await; workers.extend(w); } - } - FlowModuleValue::BranchAll { branches, .. } => { - for branch in branches { + FlowModuleValue::WhileloopFlow { modules, .. } => { let w = spawn_dedicated_workers_for_flow( - &branch.modules, + &modules, path, w_id, killpill_tx.clone(), @@ -1949,32 +1912,74 @@ async fn spawn_dedicated_workers_for_flow( .await; workers.extend(w); } - } - FlowModuleValue::RawScript { content, lock, path: spath, language, .. } => { - if let Some(dedi_w) = spawn_dedicated_worker( - SpawnWorker::RawScript { - path: spath.clone().unwrap_or(path.to_string()), - content: content.to_string(), - lock: lock.clone(), - lang: language.clone(), - }, - w_id, - killpill_tx.clone(), - killpill_rx, - db, - worker_dir, - base_internal_url, - worker_name, - job_completed_tx, - Some(module.id.clone()), - ) - .await - { - workers.push(dedi_w); + FlowModuleValue::BranchOne { branches, default } => { + for modules in branches + .iter() + .map(|x| &x.modules) + .chain(std::iter::once(default)) + { + let w = spawn_dedicated_workers_for_flow( + &modules, + path, + w_id, + killpill_tx.clone(), + killpill_rx, + db, + worker_dir, + base_internal_url, + worker_name, + job_completed_tx, + ) + .await; + workers.extend(w); + } } + FlowModuleValue::BranchAll { branches, .. } => { + for branch in branches { + let w = spawn_dedicated_workers_for_flow( + &branch.modules, + path, + w_id, + killpill_tx.clone(), + killpill_rx, + db, + worker_dir, + base_internal_url, + worker_name, + job_completed_tx, + ) + .await; + workers.extend(w); + } + } + FlowModuleValue::RawScript { content, lock, path: spath, language, .. } => { + if let Some(dedi_w) = spawn_dedicated_worker( + SpawnWorker::RawScript { + path: spath.clone().unwrap_or(path.to_string()), + content: content.to_string(), + lock: lock.clone(), + lang: language.clone(), + }, + w_id, + killpill_tx.clone(), + killpill_rx, + db, + worker_dir, + base_internal_url, + worker_name, + job_completed_tx, + Some(module.id.clone()), + ) + .await + { + workers.push(dedi_w); + } + } + FlowModuleValue::Flow { .. } => (), + FlowModuleValue::Identity => (), } - FlowModuleValue::Flow { .. } => (), - FlowModuleValue::Identity => (), + } else { + tracing::error!("failed to get value for module: {:?}", module); } } workers @@ -3668,12 +3673,12 @@ async fn trigger_python_dependents_to_recompute_dependencies< PushIsolationLevel::IsolatedRoot(db.clone(), rsmq.clone()); let r = get_latest_deployed_hash_for_path(db, w_id, s.as_str()).await; if let Ok(r) = r { - let mut args: HashMap = HashMap::new(); + let mut args: HashMap> = HashMap::new(); if let Some(ref dm) = deployment_message { - args.insert("deployment_message".to_string(), json!(dm)); + args.insert("deployment_message".to_string(), to_raw_value(&dm)); } if let Some(ref p_path) = parent_path { - args.insert("common_dependency_path".to_string(), json!(p_path)); + args.insert("common_dependency_path".to_string(), to_raw_value(&p_path)); } let (job_uuid, new_tx) = windmill_queue::push( @@ -3686,7 +3691,7 @@ async fn trigger_python_dependents_to_recompute_dependencies< language: r.6, dedicated_worker: r.7, }, - args, + windmill_queue::PushArgs { args, extra: HashMap::new() }, &created_by, email, permissioned_as.to_string(), @@ -3856,9 +3861,9 @@ async fn lock_modules( custom_concurrency_key, concurrent_limit, concurrency_time_window_s, - } = e.value.clone() + } = e.get_value()? else { - match e.value { + match e.get_value()? { FlowModuleValue::ForloopFlow { iterator, modules, @@ -3886,6 +3891,7 @@ async fn lock_modules( parallel, parallelism, } + .into() } FlowModuleValue::BranchAll { branches, parallel } => { let mut nbranches = vec![]; @@ -3906,7 +3912,7 @@ async fn lock_modules( .await?; nbranches.push(b) } - e.value = FlowModuleValue::BranchAll { branches: nbranches, parallel } + e.value = FlowModuleValue::BranchAll { branches: nbranches, parallel }.into() } FlowModuleValue::BranchOne { branches, default } => { let mut nbranches = vec![]; @@ -3941,7 +3947,7 @@ async fn lock_modules( token, ) .await?; - e.value = FlowModuleValue::BranchOne { branches: nbranches, default }; + e.value = FlowModuleValue::BranchOne { branches: nbranches, default }.into(); } _ => (), }; @@ -3971,7 +3977,7 @@ async fn lock_modules( .await; match new_lock { Ok(new_lock) => { - e.value = FlowModuleValue::RawScript { + e.value = windmill_common::worker::to_raw_value(&FlowModuleValue::RawScript { lock: Some(new_lock), path, input_transforms, @@ -3981,7 +3987,7 @@ async fn lock_modules( custom_concurrency_key, concurrent_limit, concurrency_time_window_s, - }; + }); new_flow_modules.push(e); continue; } @@ -3993,7 +3999,7 @@ async fn lock_modules( error = ?error, "Failed to generate flow lock for raw script" ); - e.value = FlowModuleValue::RawScript { + e.value = windmill_common::worker::to_raw_value(&FlowModuleValue::RawScript { lock: None, path, input_transforms, @@ -4003,7 +4009,7 @@ async fn lock_modules( custom_concurrency_key, concurrent_limit, concurrency_time_window_s, - }; + }); new_flow_modules.push(e); continue; } diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index f1ddf2807f..17f033b5a9 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -243,7 +243,8 @@ pub async fn update_flow_status_after_job_completion_internal< .await? .ok_or_else(|| Error::InternalErr(format!("requiring flow to be in the queue")))?; let module = get_module(&flow_job, module_index); - if module.is_some_and(|x| matches!(x.value, FlowModuleValue::Flow { .. })) { + + if module.is_some_and(|x| x.is_flow()) { (false, false, false) } else { (true, se, false) @@ -1255,7 +1256,7 @@ pub async fn handle_flow( #[derive(Serialize, Debug)] pub struct Iter { index: i32, - value: serde_json::Value, + value: Box, } #[derive(Serialize)] @@ -1518,7 +1519,7 @@ async fn push_next_flow_job if suspend.user_groups_required.is_some() { match suspend.user_groups_required.unwrap() { InputTransform::Static { value } => { - user_groups_required = serde_json::from_value::>(value) + user_groups_required = serde_json::from_str::>(value.get()) .expect("Unable to deserialize group names"); } InputTransform::Javascript { expr } => { @@ -1740,7 +1741,7 @@ async fn push_next_flow_job ) } }; - match json_value { + match json_value.and_then(|x| serde_json::from_str::(x.get())) { Ok(serde_json::Value::Number(n)) => { if !n.is_u64() { return Err(Error::ExecutionErr(format!( @@ -1750,8 +1751,11 @@ async fn push_next_flow_job n.as_u64().map(|x| from_now(Duration::from_secs(x))) } - _ => Err(Error::ExecutionErr(format!( - "Expected a number value, found: {json_value:?}" + Ok(x @ _) => Err(Error::ExecutionErr(format!( + "Expected an integer, found: {x:?}" + )))?, + Err(e) => Err(Error::ExecutionErr(format!( + "Expected a number value, had error instead: {e:?}", )))?, } } else { @@ -1893,10 +1897,12 @@ async fn push_next_flow_job Ok(HashMap::new()) } } else { - match &module.value { - FlowModuleValue::Script { input_transforms, .. } - | FlowModuleValue::RawScript { input_transforms, .. } - | FlowModuleValue::Flow { input_transforms, .. } => { + match &module.get_value() { + Ok( + FlowModuleValue::Script { input_transforms, .. } + | FlowModuleValue::RawScript { input_transforms, .. } + | FlowModuleValue::Flow { input_transforms, .. }, + ) => { let ctx = get_transform_context(&flow_job, &previous_id, &status).await?; transform_context = Some(ctx); let by_id = transform_context.as_ref().unwrap(); @@ -1912,7 +1918,7 @@ async fn push_next_flow_job ) .await } - FlowModuleValue::Identity => serde_json::from_str( + Ok(FlowModuleValue::Identity) => serde_json::from_str( &serde_json::to_string(&PreviousResult { previous_result: Some(&arc_last_job_result), }) @@ -1920,7 +1926,12 @@ async fn push_next_flow_job ) .map_err(|e| error::Error::InternalErr(format!("identity: {e}"))), - _ => Ok(flow_job_args), + Ok(_) => Ok(flow_job_args), + Err(e) => { + return Err(error::Error::InternalErr(format!( + "module was not convertible to acceptable value {e:?}" + ))) + } } }; tracing::debug!(id = %flow_job.id, root_id = %job_root, "flow job args computed"); @@ -2085,10 +2096,14 @@ async fn push_next_flow_job Ok(v) => (Some(v), None), Err(e) => (None, Some(e)), }; - let root_job = if matches!( - module.value, - FlowModuleValue::Flow { .. } | FlowModuleValue::ForloopFlow { parallel: true, .. } - ) { + + let value_with_parallel = module.get_value_with_parallel()?; + + let root_job = if { + value_with_parallel.type_ == "flow" + || (value_with_parallel.type_ == "forloopflow" + && value_with_parallel.parallel.is_some_and(|x| x)) + } { None } else { flow_job.root_job.or_else(|| Some(flow_job.id)) @@ -2100,7 +2115,10 @@ async fn push_next_flow_job tx2, &flow_job.workspace_id, payload_tag.payload, - ok.unwrap_or_else(|| serde_json::from_str("{}").unwrap()), + windmill_queue::PushArgs { + args: ok.unwrap_or_else(|| serde_json::from_str("{}").unwrap()), + extra: HashMap::new(), + }, &flow_job.created_by, &flow_job.email, flow_job.permissioned_as.to_owned(), @@ -2126,17 +2144,19 @@ async fn push_next_flow_job tracing::debug!(id = %flow_job.id, root_id = %job_root, "pushed next flow job: {uuid}"); - if let FlowModuleValue::ForloopFlow { parallelism: Some(p), .. } = &module.value { - if i as u16 >= *p { - sqlx::query!( - "UPDATE queue + if value_with_parallel.type_ == "forloopflow" { + if let Some(p) = value_with_parallel.parallelism { + if i as u16 >= p { + sqlx::query!( + "UPDATE queue SET suspend = $1, suspend_until = now() + interval '14 day', running = true WHERE id = $2", - (i as u16 - p + 1) as i32, - uuid, - ) - .execute(&mut inner_tx) - .await?; + (i as u16 - p + 1) as i32, + uuid, + ) + .execute(&mut inner_tx) + .await?; + } } } @@ -2370,14 +2390,14 @@ async fn push_next_flow_job #[derive(Debug)] struct ForloopNextIteration { index: usize, - itered: Vec, + itered: Vec>, flow_jobs: Vec, new_args: Iter, while_loop: bool, } enum ForLoopStatus { - ParallelIteration { itered: Vec }, + ParallelIteration { itered: Vec> }, NextIteration(ForloopNextIteration), EmptyIterator, } @@ -2482,7 +2502,7 @@ async fn compute_next_flow_transform( let delete_after_use = module.delete_after_use.unwrap_or(false); tracing::debug!(id = %flow_job.id, "computing next flow transform for {:?}", &module.value); - match &module.value { + match &module.get_value()? { FlowModuleValue::Identity => trivial_next_job(JobPayload::Identity), FlowModuleValue::Flow { path, .. } => { let payload = flow_to_payload(path, &delete_after_use); @@ -2548,7 +2568,10 @@ async fn compute_next_flow_transform( index: next_loop_idx, itered: vec![], flow_jobs: flow_jobs.clone(), - new_args: Iter { index: next_loop_idx as i32, value: json!(next_loop_idx) }, + new_args: Iter { + index: next_loop_idx as i32, + value: windmill_common::worker::to_raw_value(&next_loop_idx), + }, while_loop: true, }, modules, @@ -2599,65 +2622,66 @@ async fn compute_next_flow_transform( .await } ForLoopStatus::ParallelIteration { itered, .. } => { - let inner_path = Some(format!("{}/loop-parrallel", flow_job.script_path(),)); - let continue_payload = if is_simple { - let payload = payload_from_simple_module( - &modules[0].value, - db, - flow_job, - module, - inner_path, - ) - .await?; - ContinuePayload::ForloopJobs { n: itered.len(), payload: payload } + if modules.is_empty() { + return Ok(NextFlowTransform::EmptyInnerFlows); } else { - let payload = { - JobPayloadWithTag { - payload: JobPayload::RawFlow { - value: FlowValue { - modules: (*modules).clone(), - failure_module: flow.failure_module.clone(), - same_worker: flow.same_worker, - concurrent_limit: None, - concurrency_time_window_s: None, - skip_expr: None, - cache_ttl: None, - priority: None, - early_return: None, - concurrency_key: None, + let inner_path = + Some(format!("{}/loop-parrallel", flow_job.script_path(),)); + let value = &modules[0].get_value()?; + let continue_payload = if is_simple { + let payload = + payload_from_simple_module(value, db, flow_job, module, inner_path) + .await?; + ContinuePayload::ForloopJobs { n: itered.len(), payload: payload } + } else { + let payload = { + JobPayloadWithTag { + payload: JobPayload::RawFlow { + value: FlowValue { + modules: (*modules).clone(), + failure_module: flow.failure_module.clone(), + same_worker: flow.same_worker, + concurrent_limit: None, + concurrency_time_window_s: None, + skip_expr: None, + cache_ttl: None, + priority: None, + early_return: None, + concurrency_key: None, + }, + path: Some(format!("{}/forloop", flow_job.script_path())), + restarted_from: None, }, - path: Some(format!("{}/forloop", flow_job.script_path())), - restarted_from: None, - }, - tag: None, - delete_after_use: delete_after_use, - timeout: None, - } - }; - ContinuePayload::ForloopJobs { n: itered.len(), payload } - }; - Ok(NextFlowTransform::Continue( - continue_payload, - NextStatus::AllFlowJobs { - branchall: None, - iterator: Some(windmill_common::flow_status::Iterator { - index: 0, - itered, - }), - simple_input_transforms: if is_simple { - match &modules[0].value { - FlowModuleValue::Script { input_transforms, .. } - | FlowModuleValue::RawScript { input_transforms, .. } - | FlowModuleValue::Flow { input_transforms, .. } => { - Some(input_transforms.clone()) - } - _ => None, + tag: None, + delete_after_use: delete_after_use, + timeout: None, } - } else { - None + }; + ContinuePayload::ForloopJobs { n: itered.len(), payload } + }; + Ok(NextFlowTransform::Continue( + continue_payload, + NextStatus::AllFlowJobs { + branchall: None, + iterator: Some(windmill_common::flow_status::Iterator { + index: 0, + itered, + }), + simple_input_transforms: if is_simple { + match value { + FlowModuleValue::Script { input_transforms, .. } + | FlowModuleValue::RawScript { input_transforms, .. } + | FlowModuleValue::Flow { input_transforms, .. } => { + Some(input_transforms.clone()) + } + _ => None, + } + } else { + None + }, }, - }, - )) + )) + } } } } @@ -2882,14 +2906,14 @@ async fn next_loop_iteration( add_virtual_items_if_necessary(&mut modules); let inner_path = Some(format!("{}/loop-{}", flow_job.script_path(), ns.index)); if is_simple { - let payload = - payload_from_simple_module(&modules[0].value, db, flow_job, module, inner_path).await?; + let value = &modules[0].get_value()?; + let payload = payload_from_simple_module(value, db, flow_job, module, inner_path).await?; Ok(NextFlowTransform::Continue( ContinuePayload::SingleJob(payload), NextStatus::NextLoopIteration { next: ns, simple_input_transforms: if is_simple { - match &modules[0].value { + match value { FlowModuleValue::Script { input_transforms, .. } | FlowModuleValue::RawScript { input_transforms, .. } | FlowModuleValue::Flow { input_transforms, .. } => { @@ -2932,13 +2956,12 @@ async fn next_loop_iteration( fn is_simple_modules(modules: &Vec, flow: &FlowValue) -> bool { let is_simple = modules.len() == 1 - && modules[0].value.is_simple() + && modules[0].is_simple() && modules[0].sleep.is_none() && modules[0].suspend.is_none() && modules[0].cache_ttl.is_none() - && (modules[0].mock.is_none() - && modules[0].mock.as_ref().is_some_and(|m| !m.enabled) - && flow.failure_module.is_none()); + && (modules[0].mock.is_none() || modules[0].mock.as_ref().is_some_and(|m| !m.enabled)) + && flow.failure_module.is_none(); is_simple } @@ -2987,7 +3010,7 @@ async fn next_forloop_status( .await? } }; - let itered = serde_json::from_str::>(itered_raw.get()).map_err( + let itered = serde_json::from_str::>>(itered_raw.get()).map_err( |not_array| { Error::ExecutionErr(format!( "Expected an array value in the iterator expression, found: {not_array}" @@ -3045,7 +3068,7 @@ async fn next_forloop_status( .await? } }; - serde_json::from_str::>(itered_raw.get()).map_err( + serde_json::from_str::>>(itered_raw.get()).map_err( |not_array| { Error::ExecutionErr(format!("Expected an array value, found: {not_array}")) },