diff --git a/backend/.sqlx/query-7cab8475996f0abd7ca7888b460bd3faf3bef6e34200c258e3704981817c241e.json b/backend/.sqlx/query-0188214a2d01b11e441b6bec56f62c97a821d50f0be73df88f735978ae2ea0ae.json similarity index 83% rename from backend/.sqlx/query-7cab8475996f0abd7ca7888b460bd3faf3bef6e34200c258e3704981817c241e.json rename to backend/.sqlx/query-0188214a2d01b11e441b6bec56f62c97a821d50f0be73df88f735978ae2ea0ae.json index 4311abf5ff..4e17bcaeab 100644 --- a/backend/.sqlx/query-7cab8475996f0abd7ca7888b460bd3faf3bef6e34200c258e3704981817c241e.json +++ b/backend/.sqlx/query-0188214a2d01b11e441b6bec56f62c97a821d50f0be73df88f735978ae2ea0ae.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n path \n FROM \n flow_version \n WHERE \n id = $1 AND \n workspace_id = $2\n ", + "query": "\n SELECT \n path \n FROM \n flow_version \n WHERE \n id = $1 AND \n workspace_id = $2\n ", "describe": { "columns": [ { @@ -19,5 +19,5 @@ false ] }, - "hash": "7cab8475996f0abd7ca7888b460bd3faf3bef6e34200c258e3704981817c241e" + "hash": "0188214a2d01b11e441b6bec56f62c97a821d50f0be73df88f735978ae2ea0ae" } diff --git a/backend/.sqlx/query-0382065b3dfd78b384e26f81317af91de289f52462e74343770a8b0d47d0577d.json b/backend/.sqlx/query-0382065b3dfd78b384e26f81317af91de289f52462e74343770a8b0d47d0577d.json deleted file mode 100644 index 7e2371e9d8..0000000000 --- a/backend/.sqlx/query-0382065b3dfd78b384e26f81317af91de289f52462e74343770a8b0d47d0577d.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO kafka_trigger (\n workspace_id,\n path,\n kafka_resource_path,\n group_id,\n topics,\n script_path,\n is_flow,\n enabled,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), $11, $12, $13\n )\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "VarcharArray", - "Varchar", - "Bool", - "Bool", - "Varchar", - "Varchar", - "Varchar", - "Jsonb", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "0382065b3dfd78b384e26f81317af91de289f52462e74343770a8b0d47d0577d" -} diff --git a/backend/.sqlx/query-06af027f6ed10200de2006a2fc48771a8f42c28c87b78220eec1bddeae1f648f.json b/backend/.sqlx/query-06af027f6ed10200de2006a2fc48771a8f42c28c87b78220eec1bddeae1f648f.json deleted file mode 100644 index 8566af0709..0000000000 --- a/backend/.sqlx/query-06af027f6ed10200de2006a2fc48771a8f42c28c87b78220eec1bddeae1f648f.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO postgres_trigger (\n workspace_id,\n path,\n postgres_resource_path,\n replication_slot_name,\n publication_name,\n script_path,\n is_flow,\n enabled,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), $11, $12, $13\n )\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Bool", - "Bool", - "Varchar", - "Varchar", - "Varchar", - "Jsonb", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "06af027f6ed10200de2006a2fc48771a8f42c28c87b78220eec1bddeae1f648f" -} diff --git a/backend/.sqlx/query-089d7bc7acdbb97cf477159e111bc7e9ee85289ff5c52af43166928337c257e7.json b/backend/.sqlx/query-089d7bc7acdbb97cf477159e111bc7e9ee85289ff5c52af43166928337c257e7.json new file mode 100644 index 0000000000..a032a87239 --- /dev/null +++ b/backend/.sqlx/query-089d7bc7acdbb97cf477159e111bc7e9ee85289ff5c52af43166928337c257e7.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id FROM v2_job\n WHERE workspace_id = $1\n AND (kind = 'unassigned_script'::JOB_KIND OR kind = 'unassigned_flow'::JOB_KIND OR kind = 'unassigned_singlestepflow'::JOB_KIND)\n AND trigger_kind = $2\n AND trigger = $3", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "job_trigger_kind", + "kind": { + "Enum": [ + "webhook", + "http", + "websocket", + "kafka", + "email", + "nats", + "schedule", + "app", + "ui", + "postgres", + "sqs", + "gcp", + "mqtt", + "nextcloud" + ] + } + } + }, + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "089d7bc7acdbb97cf477159e111bc7e9ee85289ff5c52af43166928337c257e7" +} diff --git a/backend/.sqlx/query-0c0f3909b80c35210fc64c685905308621f9135c2c45a2fa0531ea750387da1f.json b/backend/.sqlx/query-0c0f3909b80c35210fc64c685905308621f9135c2c45a2fa0531ea750387da1f.json deleted file mode 100644 index cec59be959..0000000000 --- a/backend/.sqlx/query-0c0f3909b80c35210fc64c685905308621f9135c2c45a2fa0531ea750387da1f.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT \n CASE \n WHEN flow_version.id IS NOT NULL THEN\n (flow_version.value -> 'flow_env' -> $3) #> $4\n ELSE\n (root_job.raw_flow -> 'flow_env' -> $3) #> $4\n END AS \"flow_env: sqlx::types::Json>\"\n FROM \n v2_job current_job\n JOIN \n v2_job root_job ON root_job.id = COALESCE(current_job.root_job, current_job.flow_innermost_root_job, current_job.parent_job, current_job.id)\n AND root_job.workspace_id = current_job.workspace_id\n LEFT JOIN\n flow_version ON flow_version.id = root_job.runnable_id\n AND flow_version.path = root_job.runnable_path\n AND flow_version.workspace_id = root_job.workspace_id\n WHERE \n current_job.id = $1 AND \n current_job.workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "flow_env: sqlx::types::Json>", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Text", - "TextArray" - ] - }, - "nullable": [ - null - ] - }, - "hash": "0c0f3909b80c35210fc64c685905308621f9135c2c45a2fa0531ea750387da1f" -} diff --git a/backend/.sqlx/query-0c89ef278782f5a72b0b07ab3ba0edc487f03edd61936fcf77dee93fb22839ea.json b/backend/.sqlx/query-0c89ef278782f5a72b0b07ab3ba0edc487f03edd61936fcf77dee93fb22839ea.json new file mode 100644 index 0000000000..e74e12f501 --- /dev/null +++ b/backend/.sqlx/query-0c89ef278782f5a72b0b07ab3ba0edc487f03edd61936fcf77dee93fb22839ea.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT jsonb_build_object(\n 'kind', jb.kind,\n 'script_path', jb.runnable_path,\n 'latest_schema', COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow')\n ),\n 'schemas', ARRAY(\n SELECT jsonb_build_object(\n 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),\n 'job_ids', ARRAY_AGG(DISTINCT j.id),\n 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1]\n ) FROM v2_job j\n LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'\n WHERE j.id = ANY(ARRAY_AGG(jb.id))\n GROUP BY COALESCE(s.hash, f.id)\n )\n ) FROM v2_job jb\n WHERE (jb.kind = 'flow' OR jb.kind = 'script')\n AND jb.workspace_id = $1 AND jb.id = ANY($2)\n GROUP BY jb.kind, jb.runnable_path", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "jsonb_build_object", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "UuidArray" + ] + }, + "nullable": [ + null + ] + }, + "hash": "0c89ef278782f5a72b0b07ab3ba0edc487f03edd61936fcf77dee93fb22839ea" +} diff --git a/backend/.sqlx/query-37285436c16684449b33810d97d0a2611dab30faff2891fc3a7f00ee8c120950.json b/backend/.sqlx/query-0d7c3ebcb37452ffd46916d2c291a6c4f8b3ba7c1b1c671171bb0194dc48e5a1.json similarity index 71% rename from backend/.sqlx/query-37285436c16684449b33810d97d0a2611dab30faff2891fc3a7f00ee8c120950.json rename to backend/.sqlx/query-0d7c3ebcb37452ffd46916d2c291a6c4f8b3ba7c1b1c671171bb0194dc48e5a1.json index c26f747c98..73d5ac0bc7 100644 --- a/backend/.sqlx/query-37285436c16684449b33810d97d0a2611dab30faff2891fc3a7f00ee8c120950.json +++ b/backend/.sqlx/query-0d7c3ebcb37452ffd46916d2c291a6c4f8b3ba7c1b1c671171bb0194dc48e5a1.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT j.created_by AS \"created_by!\", CONCAT(coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index\n FROM v2_job j\n LEFT JOIN job_logs ON job_logs.job_id = j.id\n WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", + "query": "SELECT j.created_by AS \"created_by!\", CONCAT(coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index\n FROM v2_job j\n LEFT JOIN job_logs ON job_logs.job_id = j.id\n WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", "describe": { "columns": [ { @@ -38,5 +38,5 @@ true ] }, - "hash": "37285436c16684449b33810d97d0a2611dab30faff2891fc3a7f00ee8c120950" + "hash": "0d7c3ebcb37452ffd46916d2c291a6c4f8b3ba7c1b1c671171bb0194dc48e5a1" } diff --git a/backend/.sqlx/query-0d86a31d7d53e52d24df76fa745d968cda48e036139cdaecf4e87d948f8c365e.json b/backend/.sqlx/query-0d86a31d7d53e52d24df76fa745d968cda48e036139cdaecf4e87d948f8c365e.json deleted file mode 100644 index 67c10a27dc..0000000000 --- a/backend/.sqlx/query-0d86a31d7d53e52d24df76fa745d968cda48e036139cdaecf4e87d948f8c365e.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE v2_job_status f SET flow_status = JSONB_SET(flow_status, ARRAY['user_states'], JSONB_SET(COALESCE(flow_status->'user_states', '{}'::jsonb), ARRAY[$1], $2))\n FROM v2_job j\n WHERE f.id = $3 AND f.id = j.id AND j.workspace_id = $4 AND kind IN ('flow', 'flowpreview', 'flownode') RETURNING 1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text", - "Jsonb", - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "0d86a31d7d53e52d24df76fa745d968cda48e036139cdaecf4e87d948f8c365e" -} diff --git a/backend/.sqlx/query-695a02db2ee43d18fdf139b52eb546cffa44845cd634f188d6619cf39462ca93.json b/backend/.sqlx/query-0e9c3bc8afd819635ff60ed9fb548bbd25e7cf76bdbe06107d82430601c402b7.json similarity index 56% rename from backend/.sqlx/query-695a02db2ee43d18fdf139b52eb546cffa44845cd634f188d6619cf39462ca93.json rename to backend/.sqlx/query-0e9c3bc8afd819635ff60ed9fb548bbd25e7cf76bdbe06107d82430601c402b7.json index 546f3e8026..bebefa8e71 100644 --- a/backend/.sqlx/query-695a02db2ee43d18fdf139b52eb546cffa44845cd634f188d6619cf39462ca93.json +++ b/backend/.sqlx/query-0e9c3bc8afd819635ff60ed9fb548bbd25e7cf76bdbe06107d82430601c402b7.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n result #> $3 AS \"result: sqlx::types::Json>\",\n result_columns,\n created_by AS \"created_by!\"\n FROM v2_job_completed c\n JOIN v2_job USING (id)\n WHERE c.id = $1 AND c.workspace_id = $2 AND ($4::text[] IS NULL OR tag = ANY($4))", + "query": "SELECT\n result #> $3 AS \"result: sqlx::types::Json>\",\n result_columns,\n created_by AS \"created_by!\"\n FROM v2_job_completed c\n JOIN v2_job USING (id)\n WHERE c.id = $1 AND c.workspace_id = $2 AND ($4::text[] IS NULL OR tag = ANY($4))", "describe": { "columns": [ { @@ -33,5 +33,5 @@ false ] }, - "hash": "695a02db2ee43d18fdf139b52eb546cffa44845cd634f188d6619cf39462ca93" + "hash": "0e9c3bc8afd819635ff60ed9fb548bbd25e7cf76bdbe06107d82430601c402b7" } diff --git a/backend/.sqlx/query-140f4ad799fca6c25975a0aca7c9051f0760e22eccdd291c83ed86599ce571cb.json b/backend/.sqlx/query-140f4ad799fca6c25975a0aca7c9051f0760e22eccdd291c83ed86599ce571cb.json deleted file mode 100644 index 754eb02825..0000000000 --- a/backend/.sqlx/query-140f4ad799fca6c25975a0aca7c9051f0760e22eccdd291c83ed86599ce571cb.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job_status (id, workflow_as_code_status)\n VALUES ($1, JSONB_SET('{}'::JSONB, array[$2], $3))\n ON CONFLICT (id) DO UPDATE SET\n workflow_as_code_status = JSONB_SET(\n COALESCE(v2_job_status.workflow_as_code_status, '{}'::JSONB),\n array[$2],\n $3\n )", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "140f4ad799fca6c25975a0aca7c9051f0760e22eccdd291c83ed86599ce571cb" -} diff --git a/backend/.sqlx/query-8440081df15fa7874dded86b8af572d971d5892b7dd4c8e824b953113bd6c4a9.json b/backend/.sqlx/query-16bc77fd2debbc66187883e48c599aa9d3b280ae792ab2af9bb492c0f6f7c029.json similarity index 57% rename from backend/.sqlx/query-8440081df15fa7874dded86b8af572d971d5892b7dd4c8e824b953113bd6c4a9.json rename to backend/.sqlx/query-16bc77fd2debbc66187883e48c599aa9d3b280ae792ab2af9bb492c0f6f7c029.json index 4a7ebbc0f7..105e936a18 100644 --- a/backend/.sqlx/query-8440081df15fa7874dded86b8af572d971d5892b7dd4c8e824b953113bd6c4a9.json +++ b/backend/.sqlx/query-16bc77fd2debbc66187883e48c599aa9d3b280ae792ab2af9bb492c0f6f7c029.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT parent_job\n FROM v2_job\n WHERE id = $1 AND workspace_id = $2\n ", + "query": "\n SELECT parent_job\n FROM v2_job\n WHERE id = $1 AND workspace_id = $2\n ", "describe": { "columns": [ { @@ -19,5 +19,5 @@ true ] }, - "hash": "8440081df15fa7874dded86b8af572d971d5892b7dd4c8e824b953113bd6c4a9" + "hash": "16bc77fd2debbc66187883e48c599aa9d3b280ae792ab2af9bb492c0f6f7c029" } diff --git a/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json b/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json index 6a94914de9..5308492c44 100644 --- a/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json +++ b/backend/.sqlx/query-1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597.json @@ -212,6 +212,7 @@ true, true, true, + true, true ] }, diff --git a/backend/.sqlx/query-19b59c478744d029c6006b01f04243ad2e0aef485a780daea5d76b0be2bb2ea2.json b/backend/.sqlx/query-19b59c478744d029c6006b01f04243ad2e0aef485a780daea5d76b0be2bb2ea2.json new file mode 100644 index 0000000000..43c68f8c5a --- /dev/null +++ b/backend/.sqlx/query-19b59c478744d029c6006b01f04243ad2e0aef485a780daea5d76b0be2bb2ea2.json @@ -0,0 +1,59 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n id,\n args as \"args: _\",\n created_at\n FROM v2_job\n WHERE workspace_id = $1\n AND (\n kind = 'unassigned_script'::JOB_KIND OR\n kind = 'unassigned_flow'::JOB_KIND OR\n kind = 'unassigned_singlestepflow'::JOB_KIND\n )\n AND trigger_kind = $2\n AND trigger = $3\n AND id = ANY($4)\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 2, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "job_trigger_kind", + "kind": { + "Enum": [ + "webhook", + "http", + "websocket", + "kafka", + "email", + "nats", + "schedule", + "app", + "ui", + "postgres", + "sqs", + "gcp", + "mqtt", + "nextcloud" + ] + } + } + }, + "Text", + "UuidArray" + ] + }, + "nullable": [ + false, + true, + false + ] + }, + "hash": "19b59c478744d029c6006b01f04243ad2e0aef485a780daea5d76b0be2bb2ea2" +} diff --git a/backend/.sqlx/query-a704283ff62ac1cd6db489ca3f84d6994d75c6b9e5f61daf54d298a2fdcfd9af.json b/backend/.sqlx/query-1a85e45df7fec414e3e167bad3472f455571e0dd3006fb717b36dcab36689cca.json similarity index 58% rename from backend/.sqlx/query-a704283ff62ac1cd6db489ca3f84d6994d75c6b9e5f61daf54d298a2fdcfd9af.json rename to backend/.sqlx/query-1a85e45df7fec414e3e167bad3472f455571e0dd3006fb717b36dcab36689cca.json index b59a7f294a..a50cd946c5 100644 --- a/backend/.sqlx/query-a704283ff62ac1cd6db489ca3f84d6994d75c6b9e5f61daf54d298a2fdcfd9af.json +++ b/backend/.sqlx/query-1a85e45df7fec414e3e167bad3472f455571e0dd3006fb717b36dcab36689cca.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE email_trigger \n SET \n script_path = $1,\n path = $2,\n is_flow = $3,\n edited_by = $4,\n email = $5,\n edited_at = now(),\n error_handler_path = $6,\n error_handler_args = $7,\n retry = $8,\n enabled = $9\n WHERE \n workspace_id = $10 AND path = $11\n ", + "query": "\n UPDATE email_trigger \n SET \n script_path = $1,\n path = $2,\n is_flow = $3,\n edited_by = $4,\n email = $5,\n edited_at = now(),\n error_handler_path = $6,\n error_handler_args = $7,\n retry = $8,\n mode = $9\n WHERE \n workspace_id = $10 AND path = $11\n ", "describe": { "columns": [], "parameters": { @@ -13,12 +13,23 @@ "Varchar", "Jsonb", "Jsonb", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, "Text", "Text" ] }, "nullable": [] }, - "hash": "a704283ff62ac1cd6db489ca3f84d6994d75c6b9e5f61daf54d298a2fdcfd9af" + "hash": "1a85e45df7fec414e3e167bad3472f455571e0dd3006fb717b36dcab36689cca" } diff --git a/backend/.sqlx/query-f28b71a304721643dd90fa468f61f351d6df8282415e54f0ea4f905417657ba8.json b/backend/.sqlx/query-1bf8dc01326ebf6b8faa04e418b781e37bb9cedd1a89bf71a969b6db8cace48e.json similarity index 51% rename from backend/.sqlx/query-f28b71a304721643dd90fa468f61f351d6df8282415e54f0ea4f905417657ba8.json rename to backend/.sqlx/query-1bf8dc01326ebf6b8faa04e418b781e37bb9cedd1a89bf71a969b6db8cace48e.json index b0095248ef..e047ddf70c 100644 --- a/backend/.sqlx/query-f28b71a304721643dd90fa468f61f351d6df8282415e54f0ea4f905417657ba8.json +++ b/backend/.sqlx/query-1bf8dc01326ebf6b8faa04e418b781e37bb9cedd1a89bf71a969b6db8cace48e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO nats_trigger (\n workspace_id,\n path,\n nats_resource_path,\n subjects,\n stream_name,\n consumer_name,\n use_jetstream,\n script_path,\n is_flow,\n enabled,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now(), $13, $14, $15\n )\n ", + "query": "\n INSERT INTO nats_trigger (\n workspace_id,\n path,\n nats_resource_path,\n subjects,\n stream_name,\n consumer_name,\n use_jetstream,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now(), $13, $14, $15\n )\n ", "describe": { "columns": [], "parameters": { @@ -14,7 +14,18 @@ "Bool", "Varchar", "Bool", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, "Varchar", "Varchar", "Varchar", @@ -24,5 +35,5 @@ }, "nullable": [] }, - "hash": "f28b71a304721643dd90fa468f61f351d6df8282415e54f0ea4f905417657ba8" + "hash": "1bf8dc01326ebf6b8faa04e418b781e37bb9cedd1a89bf71a969b6db8cace48e" } diff --git a/backend/.sqlx/query-1c88ba17e8bd5caae8f40c9f6da77e6e91b6285154e3a71268f4dbb994a69b55.json b/backend/.sqlx/query-1c88ba17e8bd5caae8f40c9f6da77e6e91b6285154e3a71268f4dbb994a69b55.json new file mode 100644 index 0000000000..04d0317421 --- /dev/null +++ b/backend/.sqlx/query-1c88ba17e8bd5caae8f40c9f6da77e6e91b6285154e3a71268f4dbb994a69b55.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n path \n FROM \n flow_version \n WHERE \n id = $1 AND \n workspace_id = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Int8", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "1c88ba17e8bd5caae8f40c9f6da77e6e91b6285154e3a71268f4dbb994a69b55" +} diff --git a/backend/.sqlx/query-1de3e078c108a8a0136fccdf9187cc3500260bac39c7f1dfa05a93628569465b.json b/backend/.sqlx/query-1de3e078c108a8a0136fccdf9187cc3500260bac39c7f1dfa05a93628569465b.json new file mode 100644 index 0000000000..18b3bc3a0e --- /dev/null +++ b/backend/.sqlx/query-1de3e078c108a8a0136fccdf9187cc3500260bac39c7f1dfa05a93628569465b.json @@ -0,0 +1,37 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO kafka_trigger (\n workspace_id,\n path,\n kafka_resource_path,\n group_id,\n topics,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), $11, $12, $13\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "VarcharArray", + "Varchar", + "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, + "Varchar", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "1de3e078c108a8a0136fccdf9187cc3500260bac39c7f1dfa05a93628569465b" +} diff --git a/backend/.sqlx/query-17ca259e1c78e1317fdd19436e15bef428fc4f0d52776d7a5fca64f17225ef30.json b/backend/.sqlx/query-204998a6136091abe6bb8a503b25dc0d34fc38ee129d736b28219b84e6474229.json similarity index 55% rename from backend/.sqlx/query-17ca259e1c78e1317fdd19436e15bef428fc4f0d52776d7a5fca64f17225ef30.json rename to backend/.sqlx/query-204998a6136091abe6bb8a503b25dc0d34fc38ee129d736b28219b84e6474229.json index dd40aabbc1..fb8302c8e6 100644 --- a/backend/.sqlx/query-17ca259e1c78e1317fdd19436e15bef428fc4f0d52776d7a5fca64f17225ef30.json +++ b/backend/.sqlx/query-204998a6136091abe6bb8a503b25dc0d34fc38ee129d736b28219b84e6474229.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE \n gcp_trigger \n SET \n gcp_resource_path = $1,\n subscription_id = $2,\n topic_id = $3,\n delivery_type = $4,\n delivery_config = $5,\n is_flow = $6, \n edited_by = $7, \n email = $8,\n script_path = $9,\n path = $10,\n enabled = $11,\n edited_at = now(), \n error = NULL,\n server_id = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16,\n auto_acknowledge_msg = $17,\n ack_deadline = $18\n WHERE \n workspace_id = $12 AND \n path = $13\n ", + "query": "\n UPDATE \n gcp_trigger \n SET \n gcp_resource_path = $1,\n subscription_id = $2,\n topic_id = $3,\n delivery_type = $4,\n delivery_config = $5,\n is_flow = $6, \n edited_by = $7, \n email = $8,\n script_path = $9,\n path = $10,\n mode = $11,\n edited_at = now(), \n error = NULL,\n server_id = NULL,\n error_handler_path = $14,\n error_handler_args = $15,\n retry = $16,\n auto_acknowledge_msg = $17,\n ack_deadline = $18\n WHERE \n workspace_id = $12 AND \n path = $13\n ", "describe": { "columns": [], "parameters": { @@ -25,7 +25,18 @@ "Varchar", "Varchar", "Varchar", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, "Text", "Text", "Varchar", @@ -37,5 +48,5 @@ }, "nullable": [] }, - "hash": "17ca259e1c78e1317fdd19436e15bef428fc4f0d52776d7a5fca64f17225ef30" + "hash": "204998a6136091abe6bb8a503b25dc0d34fc38ee129d736b28219b84e6474229" } diff --git a/backend/.sqlx/query-25cba74bec5959e6752265cd7b6f84846f74d468f0073f02f81122895e86c364.json b/backend/.sqlx/query-25cba74bec5959e6752265cd7b6f84846f74d468f0073f02f81122895e86c364.json deleted file mode 100644 index 7c07d169b8..0000000000 --- a/backend/.sqlx/query-25cba74bec5959e6752265cd7b6f84846f74d468f0073f02f81122895e86c364.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT COALESCE(s.flow_status, s.workflow_as_code_status)->'user_states'->$1\n FROM v2_job_queue q LEFT JOIN v2_job_status s USING (id)\n WHERE q.id = $2 AND q.workspace_id = $3\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "25cba74bec5959e6752265cd7b6f84846f74d468f0073f02f81122895e86c364" -} diff --git a/backend/.sqlx/query-26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4.json b/backend/.sqlx/query-26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4.json deleted file mode 100644 index b14dab2503..0000000000 --- a/backend/.sqlx/query-26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT jsonb_build_object(\n 'kind', jb.kind,\n 'script_path', jb.runnable_path,\n 'latest_schema', COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow')\n ),\n 'schemas', ARRAY(\n SELECT jsonb_build_object(\n 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'),\n 'job_ids', ARRAY_AGG(DISTINCT j.id),\n 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1]\n ) FROM v2_job j\n LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script'\n LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow'\n WHERE j.id = ANY(ARRAY_AGG(jb.id))\n GROUP BY COALESCE(s.hash, f.id)\n )\n ) FROM v2_job jb\n WHERE (jb.kind = 'flow' OR jb.kind = 'script')\n AND jb.workspace_id = $1 AND jb.id = ANY($2)\n GROUP BY jb.kind, jb.runnable_path", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "jsonb_build_object", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Text", - "UuidArray" - ] - }, - "nullable": [ - null - ] - }, - "hash": "26761fbd7953416eb391de47b1694e0f4ab2bb96a6d838f1b1fdce4b58a8f5d4" -} diff --git a/backend/.sqlx/query-28a6766d38539146b6cac9a3dbf0b8592a93b178c7db91f7e4a3c04f7017c539.json b/backend/.sqlx/query-28a6766d38539146b6cac9a3dbf0b8592a93b178c7db91f7e4a3c04f7017c539.json new file mode 100644 index 0000000000..0802b27273 --- /dev/null +++ b/backend/.sqlx/query-28a6766d38539146b6cac9a3dbf0b8592a93b178c7db91f7e4a3c04f7017c539.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT COALESCE(s.flow_status, s.workflow_as_code_status)->'user_states'->$1\n FROM v2_job_queue q LEFT JOIN v2_job_status s USING (id)\n WHERE q.id = $2 AND q.workspace_id = $3\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Uuid", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "28a6766d38539146b6cac9a3dbf0b8592a93b178c7db91f7e4a3c04f7017c539" +} diff --git a/backend/.sqlx/query-2bf89418f30c1a0bf1cfd0d6bbfa2944181187deeadd07a3db9686869f60e0b9.json b/backend/.sqlx/query-2bf89418f30c1a0bf1cfd0d6bbfa2944181187deeadd07a3db9686869f60e0b9.json new file mode 100644 index 0000000000..e248907da5 --- /dev/null +++ b/backend/.sqlx/query-2bf89418f30c1a0bf1cfd0d6bbfa2944181187deeadd07a3db9686869f60e0b9.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO v2_job_status (id, workflow_as_code_status)\n VALUES ($1, JSONB_SET('{}'::JSONB, array[$2], $3))\n ON CONFLICT (id) DO UPDATE SET\n workflow_as_code_status = JSONB_SET(\n COALESCE(v2_job_status.workflow_as_code_status, '{}'::JSONB),\n array[$2],\n $3\n )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "2bf89418f30c1a0bf1cfd0d6bbfa2944181187deeadd07a3db9686869f60e0b9" +} diff --git a/backend/.sqlx/query-2c0c9312b8b326a3759566059d01c79efee28920a1a5afe2df92043526c1de82.json b/backend/.sqlx/query-2c0c9312b8b326a3759566059d01c79efee28920a1a5afe2df92043526c1de82.json deleted file mode 100644 index b68a435417..0000000000 --- a/backend/.sqlx/query-2c0c9312b8b326a3759566059d01c79efee28920a1a5afe2df92043526c1de82.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO resume_job\n (id, resume_id, job, flow, value, approver, approved)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid", - "Int4", - "Uuid", - "Uuid", - "Jsonb", - "Varchar", - "Bool" - ] - }, - "nullable": [] - }, - "hash": "2c0c9312b8b326a3759566059d01c79efee28920a1a5afe2df92043526c1de82" -} diff --git a/backend/.sqlx/query-2fc0e7e10ba02232dd429cf6fed4de2599ebfd93713f1821cc613f7c69ee7b72.json b/backend/.sqlx/query-2fc0e7e10ba02232dd429cf6fed4de2599ebfd93713f1821cc613f7c69ee7b72.json new file mode 100644 index 0000000000..049f021896 --- /dev/null +++ b/backend/.sqlx/query-2fc0e7e10ba02232dd429cf6fed4de2599ebfd93713f1821cc613f7c69ee7b72.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM v2_job_runtime WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "2fc0e7e10ba02232dd429cf6fed4de2599ebfd93713f1821cc613f7c69ee7b72" +} diff --git a/backend/.sqlx/query-b8bcf1fef244395dad802174dfc9bff45ff6f9cd1298eae5de40218cbab2eb96.json b/backend/.sqlx/query-2fd0d3224382b000028d98b0af4c431d3cadd54cca65d83c1ab7f2d2972e2282.json similarity index 64% rename from backend/.sqlx/query-b8bcf1fef244395dad802174dfc9bff45ff6f9cd1298eae5de40218cbab2eb96.json rename to backend/.sqlx/query-2fd0d3224382b000028d98b0af4c431d3cadd54cca65d83c1ab7f2d2972e2282.json index 5a0f76fc07..3f53a7e6c4 100644 --- a/backend/.sqlx/query-b8bcf1fef244395dad802174dfc9bff45ff6f9cd1298eae5de40218cbab2eb96.json +++ b/backend/.sqlx/query-2fd0d3224382b000028d98b0af4c431d3cadd54cca65d83c1ab7f2d2972e2282.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE\n http_trigger\n SET\n route_path = $1,\n route_path_key = $2,\n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7,\n path = $8,\n is_flow = $9,\n enabled = $10,\n http_method = $11,\n static_asset_config = $12,\n edited_by = $13,\n email = $14,\n request_type = $15,\n authentication_method = $16,\n summary = $17,\n description = $18,\n edited_at = now(),\n is_static_website = $19,\n error_handler_path = $20,\n error_handler_args = $21,\n retry = $22\n WHERE\n workspace_id = $23 AND\n path = $24\n ", + "query": "\n UPDATE\n http_trigger\n SET\n route_path = $1,\n route_path_key = $2,\n workspaced_route = $3,\n wrap_body = $4,\n raw_string = $5,\n authentication_resource_path = $6,\n script_path = $7,\n path = $8,\n is_flow = $9,\n mode = $10,\n http_method = $11,\n static_asset_config = $12,\n edited_by = $13,\n email = $14,\n request_type = $15,\n authentication_method = $16,\n summary = $17,\n description = $18,\n edited_at = now(),\n is_static_website = $19,\n error_handler_path = $20,\n error_handler_args = $21,\n retry = $22\n WHERE\n workspace_id = $23 AND\n path = $24\n ", "describe": { "columns": [], "parameters": { @@ -14,7 +14,18 @@ "Varchar", "Varchar", "Bool", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, { "Custom": { "name": "http_method", @@ -71,5 +82,5 @@ }, "nullable": [] }, - "hash": "b8bcf1fef244395dad802174dfc9bff45ff6f9cd1298eae5de40218cbab2eb96" + "hash": "2fd0d3224382b000028d98b0af4c431d3cadd54cca65d83c1ab7f2d2972e2282" } diff --git a/backend/.sqlx/query-34f4c05b844a83627a5c61edbfe070d0a52e20deaf550185206ae854cce274be.json b/backend/.sqlx/query-34f4c05b844a83627a5c61edbfe070d0a52e20deaf550185206ae854cce274be.json index b5d94b1915..8b01b0af3d 100644 --- a/backend/.sqlx/query-34f4c05b844a83627a5c61edbfe070d0a52e20deaf550185206ae854cce274be.json +++ b/backend/.sqlx/query-34f4c05b844a83627a5c61edbfe070d0a52e20deaf550185206ae854cce274be.json @@ -99,7 +99,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } diff --git a/backend/.sqlx/query-df6972fb16a2364f10379fe37e125ae228697602e3769c479582470dc0b9488b.json b/backend/.sqlx/query-3aad6340ea1f8dc8411742ef5de5a77d5de903845da9e686aef804bc09db1687.json similarity index 55% rename from backend/.sqlx/query-df6972fb16a2364f10379fe37e125ae228697602e3769c479582470dc0b9488b.json rename to backend/.sqlx/query-3aad6340ea1f8dc8411742ef5de5a77d5de903845da9e686aef804bc09db1687.json index 1e5c26b2e7..a93a137b5f 100644 --- a/backend/.sqlx/query-df6972fb16a2364f10379fe37e125ae228697602e3769c479582470dc0b9488b.json +++ b/backend/.sqlx/query-3aad6340ea1f8dc8411742ef5de5a77d5de903845da9e686aef804bc09db1687.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n script_path, \n is_flow, \n workspace_id, \n edited_by, \n email, \n path, \n error_handler_path as \"error_handler_path: _\", \n error_handler_args as \"error_handler_args: _\", \n retry as \"retry: _\" \n FROM email_trigger \n WHERE workspace_id = $1 \n AND local_part = $2 \n AND (workspaced_local_part = TRUE OR $3 IS TRUE)\n AND enabled IS TRUE\n ", + "query": "\n SELECT \n script_path, \n is_flow, \n workspace_id, \n mode as \"mode: _\",\n edited_by, \n email, \n path, \n error_handler_path as \"error_handler_path: _\", \n error_handler_args as \"error_handler_args: _\", \n retry as \"retry: _\" \n FROM email_trigger \n WHERE workspace_id = $1 \n AND local_part = $2 \n AND (workspaced_local_part = TRUE OR $3 IS TRUE)\n AND (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ", "describe": { "columns": [ { @@ -20,31 +20,47 @@ }, { "ordinal": 3, + "name": "mode: _", + "type_info": { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + } + }, + { + "ordinal": 4, "name": "edited_by", "type_info": "Varchar" }, { - "ordinal": 4, + "ordinal": 5, "name": "email", "type_info": "Varchar" }, { - "ordinal": 5, + "ordinal": 6, "name": "path", "type_info": "Varchar" }, { - "ordinal": 6, + "ordinal": 7, "name": "error_handler_path: _", "type_info": "Varchar" }, { - "ordinal": 7, + "ordinal": 8, "name": "error_handler_args: _", "type_info": "Jsonb" }, { - "ordinal": 8, + "ordinal": 9, "name": "retry: _", "type_info": "Jsonb" } @@ -63,10 +79,11 @@ false, false, false, + false, true, true, true ] }, - "hash": "df6972fb16a2364f10379fe37e125ae228697602e3769c479582470dc0b9488b" + "hash": "3aad6340ea1f8dc8411742ef5de5a77d5de903845da9e686aef804bc09db1687" } diff --git a/backend/.sqlx/query-bbc96ae911d4ca0330582340e65c10e008e507610f095ff3936865101c9ba346.json b/backend/.sqlx/query-3cd37daa80bc3697d331c19e01a49916fc03fdf9eceff73fa153020b4a48f4a2.json similarity index 83% rename from backend/.sqlx/query-bbc96ae911d4ca0330582340e65c10e008e507610f095ff3936865101c9ba346.json rename to backend/.sqlx/query-3cd37daa80bc3697d331c19e01a49916fc03fdf9eceff73fa153020b4a48f4a2.json index 5b9959ce13..2159a97360 100644 --- a/backend/.sqlx/query-bbc96ae911d4ca0330582340e65c10e008e507610f095ff3936865101c9ba346.json +++ b/backend/.sqlx/query-3cd37daa80bc3697d331c19e01a49916fc03fdf9eceff73fa153020b4a48f4a2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n path,\n script_path,\n is_flow,\n route_path,\n authentication_resource_path,\n workspace_id,\n request_type AS \"request_type: _\",\n authentication_method AS \"authentication_method: _\",\n edited_by,\n email,\n static_asset_config AS \"static_asset_config: _\",\n wrap_body,\n raw_string,\n workspaced_route,\n is_static_website,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM\n http_trigger\n WHERE\n http_method = $1 AND\n enabled is TRUE\n ", + "query": "\n SELECT\n path,\n script_path,\n is_flow,\n route_path,\n authentication_resource_path,\n workspace_id,\n request_type AS \"request_type: _\",\n authentication_method AS \"authentication_method: _\",\n edited_by,\n email,\n static_asset_config AS \"static_asset_config: _\",\n wrap_body,\n raw_string,\n workspaced_route,\n is_static_website,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\",\n mode as \"mode: _\"\n FROM\n http_trigger\n WHERE\n http_method = $1 AND\n (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ", "describe": { "columns": [ { @@ -117,6 +117,22 @@ "ordinal": 17, "name": "retry: _", "type_info": "Jsonb" + }, + { + "ordinal": 18, + "name": "mode: _", + "type_info": { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + } } ], "parameters": { @@ -155,8 +171,9 @@ false, true, true, - true + true, + false ] }, - "hash": "bbc96ae911d4ca0330582340e65c10e008e507610f095ff3936865101c9ba346" + "hash": "3cd37daa80bc3697d331c19e01a49916fc03fdf9eceff73fa153020b4a48f4a2" } diff --git a/backend/.sqlx/query-3d46bfb231a219afd2e2662bc06cb0167131a86a4e49d000b84a73a8b47235f2.json b/backend/.sqlx/query-3d46bfb231a219afd2e2662bc06cb0167131a86a4e49d000b84a73a8b47235f2.json new file mode 100644 index 0000000000..01efefe6be --- /dev/null +++ b/backend/.sqlx/query-3d46bfb231a219afd2e2662bc06cb0167131a86a4e49d000b84a73a8b47235f2.json @@ -0,0 +1,49 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH result_stream AS (\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n job_id, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $1 AND idx >= $3\n GROUP BY job_id\n )\n SELECT\n COALESCE(jc.result, jc.result) as \"result: sqlx::types::Json>\",\n jq.running as \"running: Option\",\n rs.stream AS \"result_stream: Option\",\n rs.offset AS stream_offset,\n CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job\n FROM (\n SELECT $1::uuid as job_id, $2::text as workspace_id\n ) base\n LEFT JOIN v2_job_completed jc ON jc.id = base.job_id AND jc.workspace_id = base.workspace_id\n LEFT JOIN v2_job_queue jq ON jq.id = base.job_id AND jq.workspace_id = base.workspace_id\n LEFT JOIN v2_job_status js ON js.id = base.job_id\n LEFT JOIN result_stream rs ON rs.job_id = base.job_id\n WHERE base.job_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "result: sqlx::types::Json>", + "type_info": "Jsonb" + }, + { + "ordinal": 1, + "name": "running: Option", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "result_stream: Option", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "stream_offset", + "type_info": "Int4" + }, + { + "ordinal": 4, + "name": "stream_job", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Int4", + "Bool" + ] + }, + "nullable": [ + null, + false, + null, + null, + null + ] + }, + "hash": "3d46bfb231a219afd2e2662bc06cb0167131a86a4e49d000b84a73a8b47235f2" +} diff --git a/backend/.sqlx/query-6c568509908c1833d9e6f58f739b8a186fc91f94d4fcc40eeb9914084e4ef60c.json b/backend/.sqlx/query-3d763dbb411e28ce026cc9ab525b20b409cfe17bf1cdef22aaffc719cf6c53e3.json similarity index 58% rename from backend/.sqlx/query-6c568509908c1833d9e6f58f739b8a186fc91f94d4fcc40eeb9914084e4ef60c.json rename to backend/.sqlx/query-3d763dbb411e28ce026cc9ab525b20b409cfe17bf1cdef22aaffc719cf6c53e3.json index b6e4f96c3c..72806fdb1e 100644 --- a/backend/.sqlx/query-6c568509908c1833d9e6f58f739b8a186fc91f94d4fcc40eeb9914084e4ef60c.json +++ b/backend/.sqlx/query-3d763dbb411e28ce026cc9ab525b20b409cfe17bf1cdef22aaffc719cf6c53e3.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO email_trigger (\n workspace_id,\n path,\n script_path,\n is_flow,\n local_part,\n workspaced_local_part,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry,\n enabled\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, now(), $9, $10, $11, $12\n )\n ", + "query": "\n INSERT INTO email_trigger (\n workspace_id,\n path,\n script_path,\n is_flow,\n local_part,\n workspaced_local_part,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry,\n mode\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, now(), $9, $10, $11, $12\n )\n ", "describe": { "columns": [], "parameters": { @@ -16,10 +16,21 @@ "Varchar", "Jsonb", "Jsonb", - "Bool" + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + } ] }, "nullable": [] }, - "hash": "6c568509908c1833d9e6f58f739b8a186fc91f94d4fcc40eeb9914084e4ef60c" + "hash": "3d763dbb411e28ce026cc9ab525b20b409cfe17bf1cdef22aaffc719cf6c53e3" } diff --git a/backend/.sqlx/query-e7c61bbdcf882f6e1e9b11df03e7a2ee318c72b365c0fe0b9fbec886a461f5e4.json b/backend/.sqlx/query-447457d93fa956d2f7364ff15038cc590473da374b5120921086a1ee557ac9ba.json similarity index 52% rename from backend/.sqlx/query-e7c61bbdcf882f6e1e9b11df03e7a2ee318c72b365c0fe0b9fbec886a461f5e4.json rename to backend/.sqlx/query-447457d93fa956d2f7364ff15038cc590473da374b5120921086a1ee557ac9ba.json index 6d41284328..33e0d6b0ff 100644 --- a/backend/.sqlx/query-e7c61bbdcf882f6e1e9b11df03e7a2ee318c72b365c0fe0b9fbec886a461f5e4.json +++ b/backend/.sqlx/query-447457d93fa956d2f7364ff15038cc590473da374b5120921086a1ee557ac9ba.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO gcp_trigger (\n gcp_resource_path,\n subscription_id,\n topic_id,\n delivery_type,\n delivery_config,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by,\n error_handler_path,\n error_handler_args,\n retry,\n auto_acknowledge_msg,\n ack_deadline\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4,\n $5,\n $6, \n $7, \n $8, \n $9,\n $10,\n $11,\n $12,\n $13,\n $14,\n $15,\n $16,\n $17\n )", + "query": "\n INSERT INTO gcp_trigger (\n gcp_resource_path,\n subscription_id,\n topic_id,\n delivery_type,\n delivery_config,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n mode, \n edited_by,\n error_handler_path,\n error_handler_args,\n retry,\n auto_acknowledge_msg,\n ack_deadline\n ) \n VALUES (\n $1, \n $2, \n $3, \n $4,\n $5,\n $6, \n $7, \n $8, \n $9,\n $10,\n $11,\n $12,\n $13,\n $14,\n $15,\n $16,\n $17\n )", "describe": { "columns": [], "parameters": { @@ -25,7 +25,18 @@ "Varchar", "Bool", "Varchar", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, "Varchar", "Varchar", "Jsonb", @@ -36,5 +47,5 @@ }, "nullable": [] }, - "hash": "e7c61bbdcf882f6e1e9b11df03e7a2ee318c72b365c0fe0b9fbec886a461f5e4" + "hash": "447457d93fa956d2f7364ff15038cc590473da374b5120921086a1ee557ac9ba" } diff --git a/backend/.sqlx/query-4545e1e0953cde730272353e78044744910a17af23f8201d853f0a4f8404fc73.json b/backend/.sqlx/query-4545e1e0953cde730272353e78044744910a17af23f8201d853f0a4f8404fc73.json deleted file mode 100644 index c1fd3462f7..0000000000 --- a/backend/.sqlx/query-4545e1e0953cde730272353e78044744910a17af23f8201d853f0a4f8404fc73.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT j.tag as \"tag!\", COUNT(*) as \"count!\"\n FROM v2_job_completed c JOIN v2_job j USING (id)\n WHERE c.started_at > NOW() - make_interval(secs => $1) AND ($2::text IS NULL OR j.workspace_id = $2)\n GROUP BY j.tag\n ORDER BY \"count!\" DESC\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "tag!", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "count!", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Float8", - "Text" - ] - }, - "nullable": [ - false, - null - ] - }, - "hash": "4545e1e0953cde730272353e78044744910a17af23f8201d853f0a4f8404fc73" -} diff --git a/backend/.sqlx/query-6b9ea0059ad1037a77f67b70d5de13776870612e8faa8d8fa577f8e7b7309f76.json b/backend/.sqlx/query-485dc289a61a06595acae28d4968f3b6b2aeb6a8aee863dc999d6c8d58397814.json similarity index 60% rename from backend/.sqlx/query-6b9ea0059ad1037a77f67b70d5de13776870612e8faa8d8fa577f8e7b7309f76.json rename to backend/.sqlx/query-485dc289a61a06595acae28d4968f3b6b2aeb6a8aee863dc999d6c8d58397814.json index b2e0c68ba5..041d969faf 100644 --- a/backend/.sqlx/query-6b9ea0059ad1037a77f67b70d5de13776870612e8faa8d8fa577f8e7b7309f76.json +++ b/backend/.sqlx/query-485dc289a61a06595acae28d4968f3b6b2aeb6a8aee863dc999d6c8d58397814.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT j.id AS \"id!\", COALESCE(s.flow_status, s.workflow_as_code_status) as flow_status, q.suspend AS \"suspend!\", j.runnable_path as script_path\n FROM v2_job_queue q JOIN v2_job j USING (id) LEFT JOIN v2_job_status s USING (id)\n WHERE j.id = $1\n ", + "query": "\n SELECT j.id AS \"id!\", COALESCE(s.flow_status, s.workflow_as_code_status) as flow_status, q.suspend AS \"suspend!\", j.runnable_path as script_path\n FROM v2_job_queue q JOIN v2_job j USING (id) LEFT JOIN v2_job_status s USING (id)\n WHERE j.id = $1\n ", "describe": { "columns": [ { @@ -36,5 +36,5 @@ true ] }, - "hash": "6b9ea0059ad1037a77f67b70d5de13776870612e8faa8d8fa577f8e7b7309f76" + "hash": "485dc289a61a06595acae28d4968f3b6b2aeb6a8aee863dc999d6c8d58397814" } diff --git a/backend/.sqlx/query-48a41e1ad0ad8fc51624af4de34964c72fe4b3cf2497644d514c5c00d6d71bf3.json b/backend/.sqlx/query-48a41e1ad0ad8fc51624af4de34964c72fe4b3cf2497644d514c5c00d6d71bf3.json index 61c93e6573..358758aff7 100644 --- a/backend/.sqlx/query-48a41e1ad0ad8fc51624af4de34964c72fe4b3cf2497644d514c5c00d6d71bf3.json +++ b/backend/.sqlx/query-48a41e1ad0ad8fc51624af4de34964c72fe4b3cf2497644d514c5c00d6d71bf3.json @@ -39,7 +39,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } diff --git a/backend/.sqlx/query-5190528997a879981a87420ddf3d28c978c8a5876f5c1ac1613391e86ffb550f.json b/backend/.sqlx/query-5190528997a879981a87420ddf3d28c978c8a5876f5c1ac1613391e86ffb550f.json new file mode 100644 index 0000000000..d165e78ad2 --- /dev/null +++ b/backend/.sqlx/query-5190528997a879981a87420ddf3d28c978c8a5876f5c1ac1613391e86ffb550f.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM v2_job WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "5190528997a879981a87420ddf3d28c978c8a5876f5c1ac1613391e86ffb550f" +} diff --git a/backend/.sqlx/query-51ef2ee9dc252f7accc41b89d13a2aa1e4c11d4860894e9e661b6ee3bccd8522.json b/backend/.sqlx/query-51ef2ee9dc252f7accc41b89d13a2aa1e4c11d4860894e9e661b6ee3bccd8522.json deleted file mode 100644 index 7cd9c5ce22..0000000000 --- a/backend/.sqlx/query-51ef2ee9dc252f7accc41b89d13a2aa1e4c11d4860894e9e661b6ee3bccd8522.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE v2_job_completed c SET\n result = NULL,\n deleted = TRUE\n FROM v2_job j\n WHERE c.id = $1\n AND j.id = c.id\n AND c.workspace_id = $2\n AND ($3::TEXT[] IS NULL OR tag = ANY($3))\n RETURNING c.id\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "TextArray" - ] - }, - "nullable": [ - false - ] - }, - "hash": "51ef2ee9dc252f7accc41b89d13a2aa1e4c11d4860894e9e661b6ee3bccd8522" -} diff --git a/backend/.sqlx/query-81ba1e003d3f6e8e7c74a4dd09b980888d71121082390f3fdb91269cd0c5d0b2.json b/backend/.sqlx/query-530b2a88c1465e89670bb3a42f31e44df72184613cd563cc38f846bb992f7ce2.json similarity index 51% rename from backend/.sqlx/query-81ba1e003d3f6e8e7c74a4dd09b980888d71121082390f3fdb91269cd0c5d0b2.json rename to backend/.sqlx/query-530b2a88c1465e89670bb3a42f31e44df72184613cd563cc38f846bb992f7ce2.json index 83b4cc8e28..efc4f096f5 100644 --- a/backend/.sqlx/query-81ba1e003d3f6e8e7c74a4dd09b980888d71121082390f3fdb91269cd0c5d0b2.json +++ b/backend/.sqlx/query-530b2a88c1465e89670bb3a42f31e44df72184613cd563cc38f846bb992f7ce2.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT coalesce(flow_version_lite.value, flow_version.value) as \"value!: sqlx::types::Json>\" FROM flow\n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n LEFT JOIN flow_version_lite\n ON flow_version_lite.id = flow_version.id\n WHERE flow.path = $1 AND flow.workspace_id = $2 LIMIT 1", + "query": "SELECT coalesce(flow_version_lite.value, flow_version.value) as \"value!: sqlx::types::Json>\" FROM flow\n LEFT JOIN flow_version\n ON flow_version.id = flow.versions[array_upper(flow.versions, 1)]\n LEFT JOIN flow_version_lite\n ON flow_version_lite.id = flow_version.id\n WHERE flow.path = $1 AND flow.workspace_id = $2 LIMIT 1", "describe": { "columns": [ { @@ -19,5 +19,5 @@ null ] }, - "hash": "81ba1e003d3f6e8e7c74a4dd09b980888d71121082390f3fdb91269cd0c5d0b2" + "hash": "530b2a88c1465e89670bb3a42f31e44df72184613cd563cc38f846bb992f7ce2" } diff --git a/backend/.sqlx/query-53b43ebf9659a8310540603791593cdfa218ceb7187e00cee2cf2ea00e9ef8dc.json b/backend/.sqlx/query-53b43ebf9659a8310540603791593cdfa218ceb7187e00cee2cf2ea00e9ef8dc.json new file mode 100644 index 0000000000..943c04ebb2 --- /dev/null +++ b/backend/.sqlx/query-53b43ebf9659a8310540603791593cdfa218ceb7187e00cee2cf2ea00e9ef8dc.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_completed c SET\n result = NULL,\n deleted = TRUE\n FROM v2_job j\n WHERE c.id = $1\n AND j.id = c.id\n AND c.workspace_id = $2\n AND ($3::TEXT[] IS NULL OR tag = ANY($3))\n RETURNING c.id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "TextArray" + ] + }, + "nullable": [ + false + ] + }, + "hash": "53b43ebf9659a8310540603791593cdfa218ceb7187e00cee2cf2ea00e9ef8dc" +} diff --git a/backend/.sqlx/query-542b670194b802f09af8ecc4f339524b914fd1bbd620a6d529a0a96766591bc8.json b/backend/.sqlx/query-542b670194b802f09af8ecc4f339524b914fd1bbd620a6d529a0a96766591bc8.json new file mode 100644 index 0000000000..4bc7dfb466 --- /dev/null +++ b/backend/.sqlx/query-542b670194b802f09af8ecc4f339524b914fd1bbd620a6d529a0a96766591bc8.json @@ -0,0 +1,101 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH result_stream AS (\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n job_id, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $3 AND idx >= $8\n GROUP BY job_id\n )\n SELECT\n c.id IS NOT NULL AS completed,\n CASE\n WHEN q.id IS NOT NULL THEN (CASE WHEN NOT $5 AND q.running THEN true ELSE null END)\n ELSE false\n END AS running,\n CASE WHEN $7::BOOLEAN THEN NULL ELSE SUBSTR(logs, GREATEST($1 - log_offset, 0)) END AS logs,\n rs.stream AS new_result_stream,\n COALESCE(r.memory_peak, c.memory_peak) AS mem_peak,\n COALESCE(c.flow_status, f.flow_status) AS \"flow_status: sqlx::types::Json>\",\n (COALESCE(c.flow_status, f.flow_status)->>'stream_job')::uuid AS stream_job,\n COALESCE(c.workflow_as_code_status, f.workflow_as_code_status) AS \"workflow_as_code_status: sqlx::types::Json>\",\n CASE WHEN $7::BOOLEAN THEN NULL ELSE job_logs.log_offset + CHAR_LENGTH(job_logs.logs) + 1 END AS log_offset,\n rs.offset AS stream_offset,\n created_by AS \"created_by!\",\n CASE WHEN $4::BOOLEAN THEN (\n SELECT scalar_int FROM job_stats WHERE job_id = $3 AND metric_id = 'progress_perc'\n ) END AS progress,\n rs.stream AS \"result_stream: Option\"\n FROM v2_job j\n LEFT JOIN v2_job_queue q USING (id)\n LEFT JOIN v2_job_runtime r USING (id)\n LEFT JOIN v2_job_status f USING (id)\n LEFT JOIN v2_job_completed c USING (id)\n LEFT JOIN result_stream rs ON rs.job_id = $3\n LEFT JOIN job_logs ON job_logs.job_id = $3\n WHERE j.workspace_id = $2 AND j.id = $3\n AND ($6::text[] IS NULL OR j.tag = ANY($6))", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "completed", + "type_info": "Bool" + }, + { + "ordinal": 1, + "name": "running", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "logs", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "new_result_stream", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "mem_peak", + "type_info": "Int4" + }, + { + "ordinal": 5, + "name": "flow_status: sqlx::types::Json>", + "type_info": "Jsonb" + }, + { + "ordinal": 6, + "name": "stream_job", + "type_info": "Uuid" + }, + { + "ordinal": 7, + "name": "workflow_as_code_status: sqlx::types::Json>", + "type_info": "Jsonb" + }, + { + "ordinal": 8, + "name": "log_offset", + "type_info": "Int4" + }, + { + "ordinal": 9, + "name": "stream_offset", + "type_info": "Int4" + }, + { + "ordinal": 10, + "name": "created_by!", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "progress", + "type_info": "Int4" + }, + { + "ordinal": 12, + "name": "result_stream: Option", + "type_info": "Text" + } + ], + "parameters": { + "Left": [ + "Int4", + "Text", + "Uuid", + "Bool", + "Bool", + "TextArray", + "Bool", + "Int4" + ] + }, + "nullable": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + false, + null, + null + ] + }, + "hash": "542b670194b802f09af8ecc4f339524b914fd1bbd620a6d529a0a96766591bc8" +} diff --git a/backend/.sqlx/query-5906c48b3b60d3d770ebc172173f02dfbb892db94f464868dda38a0c6fe98bf6.json b/backend/.sqlx/query-5906c48b3b60d3d770ebc172173f02dfbb892db94f464868dda38a0c6fe98bf6.json deleted file mode 100644 index 5388b95d76..0000000000 --- a/backend/.sqlx/query-5906c48b3b60d3d770ebc172173f02dfbb892db94f464868dda38a0c6fe98bf6.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE workspace_settings\n SET\n deploy_to = $1,\n ai_config = source_ws.ai_config,\n large_file_storage = source_ws.large_file_storage,\n git_app_installations = source_ws.git_app_installations\n FROM workspace_settings source_ws\n WHERE source_ws.workspace_id = $1\n AND workspace_settings.workspace_id = $2\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [] - }, - "hash": "5906c48b3b60d3d770ebc172173f02dfbb892db94f464868dda38a0c6fe98bf6" -} diff --git a/backend/.sqlx/query-59a6afc7c2f4d2c00b9389f3d2b6b675074e92a174ba775802a9bcaddd30cd8a.json b/backend/.sqlx/query-59a6afc7c2f4d2c00b9389f3d2b6b675074e92a174ba775802a9bcaddd30cd8a.json new file mode 100644 index 0000000000..e728a1bfbc --- /dev/null +++ b/backend/.sqlx/query-59a6afc7c2f4d2c00b9389f3d2b6b675074e92a174ba775802a9bcaddd30cd8a.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM debounce_key WHERE job_id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "59a6afc7c2f4d2c00b9389f3d2b6b675074e92a174ba775802a9bcaddd30cd8a" +} diff --git a/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json b/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json index 36ddb8ab9f..713ccb9dd3 100644 --- a/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json +++ b/backend/.sqlx/query-5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55.json @@ -15,7 +15,7 @@ ] }, "nullable": [ - true + null ] }, "hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55" diff --git a/backend/.sqlx/query-5fdfc9427f455a4c1bc8f6ca41ddfd426bc0c2ac126792c926f3cf1182ded981.json b/backend/.sqlx/query-5fdfc9427f455a4c1bc8f6ca41ddfd426bc0c2ac126792c926f3cf1182ded981.json deleted file mode 100644 index 41f7673e92..0000000000 --- a/backend/.sqlx/query-5fdfc9427f455a4c1bc8f6ca41ddfd426bc0c2ac126792c926f3cf1182ded981.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT value->>'ducklake_user_pg_pwd' FROM global_settings WHERE name = 'ducklake_settings';", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Text" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null - ] - }, - "hash": "5fdfc9427f455a4c1bc8f6ca41ddfd426bc0c2ac126792c926f3cf1182ded981" -} diff --git a/backend/.sqlx/query-68b21b67e0d7977cf34a4e360a0fdeb62c81b1ef19d4283debeb252f398a7543.json b/backend/.sqlx/query-68b21b67e0d7977cf34a4e360a0fdeb62c81b1ef19d4283debeb252f398a7543.json new file mode 100644 index 0000000000..46c4b2834b --- /dev/null +++ b/backend/.sqlx/query-68b21b67e0d7977cf34a4e360a0fdeb62c81b1ef19d4283debeb252f398a7543.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COUNT(*) as count\n FROM v2_job_queue\n WHERE scheduled_for < to_timestamp($1::bigint / 1000.0)\n AND running = false\n AND suspend_until IS NULL", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + null + ] + }, + "hash": "68b21b67e0d7977cf34a4e360a0fdeb62c81b1ef19d4283debeb252f398a7543" +} diff --git a/backend/.sqlx/query-69f370c2e3596c1a8256b0903332a36e5b8bd03099617db11eed44f291377880.json b/backend/.sqlx/query-69f370c2e3596c1a8256b0903332a36e5b8bd03099617db11eed44f291377880.json new file mode 100644 index 0000000000..c76b929e98 --- /dev/null +++ b/backend/.sqlx/query-69f370c2e3596c1a8256b0903332a36e5b8bd03099617db11eed44f291377880.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT j.tag as \"tag!\", COUNT(*) as \"count!\"\n FROM v2_job_completed c JOIN v2_job j USING (id)\n WHERE c.started_at > NOW() - make_interval(secs => $1) AND ($2::text IS NULL OR j.workspace_id = $2)\n GROUP BY j.tag\n ORDER BY \"count!\" DESC\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "tag!", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "count!", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Float8", + "Text" + ] + }, + "nullable": [ + false, + null + ] + }, + "hash": "69f370c2e3596c1a8256b0903332a36e5b8bd03099617db11eed44f291377880" +} diff --git a/backend/.sqlx/query-a810e7912b65e6d4b361c9d34aa4c34d17c8444699f7bf29cc6907cdac6106f3.json b/backend/.sqlx/query-6adafb42a54ed389bfbf8cf0065aece2d59e4f8b1d433273220d929671a6c94d.json similarity index 67% rename from backend/.sqlx/query-a810e7912b65e6d4b361c9d34aa4c34d17c8444699f7bf29cc6907cdac6106f3.json rename to backend/.sqlx/query-6adafb42a54ed389bfbf8cf0065aece2d59e4f8b1d433273220d929671a6c94d.json index 52c3039fa5..564ed3e08b 100644 --- a/backend/.sqlx/query-a810e7912b65e6d4b361c9d34aa4c34d17c8444699f7bf29cc6907cdac6106f3.json +++ b/backend/.sqlx/query-6adafb42a54ed389bfbf8cf0065aece2d59e4f8b1d433273220d929671a6c94d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job_status (id, flow_status)\n SELECT unnest($1::uuid[]), $2", + "query": "INSERT INTO v2_job_status (id, flow_status)\n SELECT unnest($1::uuid[]), $2", "describe": { "columns": [], "parameters": { @@ -11,5 +11,5 @@ }, "nullable": [] }, - "hash": "a810e7912b65e6d4b361c9d34aa4c34d17c8444699f7bf29cc6907cdac6106f3" + "hash": "6adafb42a54ed389bfbf8cf0065aece2d59e4f8b1d433273220d929671a6c94d" } diff --git a/backend/.sqlx/query-6c03fc7e623afcdb11b55390ac79d9dd236e694c0af5af0ebd90df940d893258.json b/backend/.sqlx/query-6c03fc7e623afcdb11b55390ac79d9dd236e694c0af5af0ebd90df940d893258.json index bfebea0664..85af8d8bab 100644 --- a/backend/.sqlx/query-6c03fc7e623afcdb11b55390ac79d9dd236e694c0af5af0ebd90df940d893258.json +++ b/backend/.sqlx/query-6c03fc7e623afcdb11b55390ac79d9dd236e694c0af5af0ebd90df940d893258.json @@ -99,7 +99,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } diff --git a/backend/.sqlx/query-6c97ab28ab47b75fb3ff39ea70fa3627f08b61bbd33aecb9ea816f8f78a04ec5.json b/backend/.sqlx/query-6c97ab28ab47b75fb3ff39ea70fa3627f08b61bbd33aecb9ea816f8f78a04ec5.json index 272201898c..c45bc18ab4 100644 --- a/backend/.sqlx/query-6c97ab28ab47b75fb3ff39ea70fa3627f08b61bbd33aecb9ea816f8f78a04ec5.json +++ b/backend/.sqlx/query-6c97ab28ab47b75fb3ff39ea70fa3627f08b61bbd33aecb9ea816f8f78a04ec5.json @@ -69,7 +69,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -236,7 +239,8 @@ "postgres", "sqs", "gcp", - "mqtt" + "mqtt", + "nextcloud" ] } } diff --git a/backend/.sqlx/query-71518449b8dec97be26f30e35e7ac4b1c89a941b505fc454cd84cbc0d05a9239.json b/backend/.sqlx/query-71518449b8dec97be26f30e35e7ac4b1c89a941b505fc454cd84cbc0d05a9239.json new file mode 100644 index 0000000000..3b9ebf5f49 --- /dev/null +++ b/backend/.sqlx/query-71518449b8dec97be26f30e35e7ac4b1c89a941b505fc454cd84cbc0d05a9239.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM v2_job_queue WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "71518449b8dec97be26f30e35e7ac4b1c89a941b505fc454cd84cbc0d05a9239" +} diff --git a/backend/.sqlx/query-73c1c88bdf26ea0559b83314fed7a67d850e4e4dd60f4424ffb0b6f472acc8d5.json b/backend/.sqlx/query-73c1c88bdf26ea0559b83314fed7a67d850e4e4dd60f4424ffb0b6f472acc8d5.json deleted file mode 100644 index cf36aed51a..0000000000 --- a/backend/.sqlx/query-73c1c88bdf26ea0559b83314fed7a67d850e4e4dd60f4424ffb0b6f472acc8d5.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT value->'instance_catalog_db_status' FROM global_settings WHERE name = 'ducklake_settings'", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - null - ] - }, - "hash": "73c1c88bdf26ea0559b83314fed7a67d850e4e4dd60f4424ffb0b6f472acc8d5" -} diff --git a/backend/.sqlx/query-74a2871aba7e35527dcefb2538b8cf41c35618f7c15ea047c5082f8feb7a8464.json b/backend/.sqlx/query-74a2871aba7e35527dcefb2538b8cf41c35618f7c15ea047c5082f8feb7a8464.json index 9aa5d8ccbd..dcb00037fb 100644 --- a/backend/.sqlx/query-74a2871aba7e35527dcefb2538b8cf41c35618f7c15ea047c5082f8feb7a8464.json +++ b/backend/.sqlx/query-74a2871aba7e35527dcefb2538b8cf41c35618f7c15ea047c5082f8feb7a8464.json @@ -15,7 +15,8 @@ "s3object", "resource", "variable", - "ducklake" + "ducklake", + "datatable" ] } } diff --git a/backend/.sqlx/query-750f09238caf114d01f752a145fd4289f04f3961d5035d42f3fd0bd2f87eed1a.json b/backend/.sqlx/query-750f09238caf114d01f752a145fd4289f04f3961d5035d42f3fd0bd2f87eed1a.json new file mode 100644 index 0000000000..c72f20178c --- /dev/null +++ b/backend/.sqlx/query-750f09238caf114d01f752a145fd4289f04f3961d5035d42f3fd0bd2f87eed1a.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE v2_job_status f SET flow_status = JSONB_SET(flow_status, ARRAY['user_states'], JSONB_SET(COALESCE(flow_status->'user_states', '{}'::jsonb), ARRAY[$1], $2))\n FROM v2_job j\n WHERE f.id = $3 AND f.id = j.id AND j.workspace_id = $4 AND kind IN ('flow', 'flowpreview', 'flownode') RETURNING 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "?column?", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text", + "Jsonb", + "Uuid", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "750f09238caf114d01f752a145fd4289f04f3961d5035d42f3fd0bd2f87eed1a" +} diff --git a/backend/.sqlx/query-757ef6215d3d385cb3a69e26ee4ca846dd5e7fe7ceb1aa8b3fcd26a2bd30eb2c.json b/backend/.sqlx/query-757ef6215d3d385cb3a69e26ee4ca846dd5e7fe7ceb1aa8b3fcd26a2bd30eb2c.json new file mode 100644 index 0000000000..cd795e6fec --- /dev/null +++ b/backend/.sqlx/query-757ef6215d3d385cb3a69e26ee4ca846dd5e7fe7ceb1aa8b3fcd26a2bd30eb2c.json @@ -0,0 +1,58 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n id,\n args as \"args: _\",\n created_at\n FROM v2_job\n WHERE workspace_id = $1\n AND (\n kind = 'unassigned_script'::JOB_KIND OR\n kind = 'unassigned_flow'::JOB_KIND OR\n kind = 'unassigned_singlestepflow'::JOB_KIND\n )\n AND trigger_kind = $2\n AND trigger = $3\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 2, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "job_trigger_kind", + "kind": { + "Enum": [ + "webhook", + "http", + "websocket", + "kafka", + "email", + "nats", + "schedule", + "app", + "ui", + "postgres", + "sqs", + "gcp", + "mqtt", + "nextcloud" + ] + } + } + }, + "Text" + ] + }, + "nullable": [ + false, + true, + false + ] + }, + "hash": "757ef6215d3d385cb3a69e26ee4ca846dd5e7fe7ceb1aa8b3fcd26a2bd30eb2c" +} diff --git a/backend/.sqlx/query-7b524ee24bb78e494a93c5ea205259bd989a9d769a6a300d37ab116960438882.json b/backend/.sqlx/query-7b524ee24bb78e494a93c5ea205259bd989a9d769a6a300d37ab116960438882.json deleted file mode 100644 index 59f972f50d..0000000000 --- a/backend/.sqlx/query-7b524ee24bb78e494a93c5ea205259bd989a9d769a6a300d37ab116960438882.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH result_stream AS (\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n job_id, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $3 AND idx >= $8\n GROUP BY job_id\n )\n SELECT\n c.id IS NOT NULL AS completed,\n CASE\n WHEN q.id IS NOT NULL THEN (CASE WHEN NOT $5 AND q.running THEN true ELSE null END)\n ELSE false\n END AS running,\n CASE WHEN $7::BOOLEAN THEN NULL ELSE SUBSTR(logs, GREATEST($1 - log_offset, 0)) END AS logs,\n rs.stream AS new_result_stream,\n COALESCE(r.memory_peak, c.memory_peak) AS mem_peak,\n COALESCE(c.flow_status, f.flow_status) AS \"flow_status: sqlx::types::Json>\",\n (COALESCE(c.flow_status, f.flow_status)->>'stream_job')::uuid AS stream_job,\n COALESCE(c.workflow_as_code_status, f.workflow_as_code_status) AS \"workflow_as_code_status: sqlx::types::Json>\",\n CASE WHEN $7::BOOLEAN THEN NULL ELSE job_logs.log_offset + CHAR_LENGTH(job_logs.logs) + 1 END AS log_offset,\n rs.offset AS stream_offset,\n created_by AS \"created_by!\",\n CASE WHEN $4::BOOLEAN THEN (\n SELECT scalar_int FROM job_stats WHERE job_id = $3 AND metric_id = 'progress_perc'\n ) END AS progress,\n rs.stream AS \"result_stream: Option\"\n FROM v2_job j\n LEFT JOIN v2_job_queue q USING (id)\n LEFT JOIN v2_job_runtime r USING (id)\n LEFT JOIN v2_job_status f USING (id)\n LEFT JOIN v2_job_completed c USING (id)\n LEFT JOIN result_stream rs ON rs.job_id = $3\n LEFT JOIN job_logs ON job_logs.job_id = $3\n WHERE j.workspace_id = $2 AND j.id = $3\n AND ($6::text[] IS NULL OR j.tag = ANY($6))", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "completed", - "type_info": "Bool" - }, - { - "ordinal": 1, - "name": "running", - "type_info": "Bool" - }, - { - "ordinal": 2, - "name": "logs", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "new_result_stream", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "mem_peak", - "type_info": "Int4" - }, - { - "ordinal": 5, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 6, - "name": "stream_job", - "type_info": "Uuid" - }, - { - "ordinal": 7, - "name": "workflow_as_code_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 8, - "name": "log_offset", - "type_info": "Int4" - }, - { - "ordinal": 9, - "name": "stream_offset", - "type_info": "Int4" - }, - { - "ordinal": 10, - "name": "created_by!", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "progress", - "type_info": "Int4" - }, - { - "ordinal": 12, - "name": "result_stream: Option", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Int4", - "Text", - "Uuid", - "Bool", - "Bool", - "TextArray", - "Bool", - "Int4" - ] - }, - "nullable": [ - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - false, - null, - null - ] - }, - "hash": "7b524ee24bb78e494a93c5ea205259bd989a9d769a6a300d37ab116960438882" -} diff --git a/backend/.sqlx/query-7b5ad10af2a9b34fa86429499ea24c0c09c6e7e9ebfa3af90035570133f7c579.json b/backend/.sqlx/query-7b5ad10af2a9b34fa86429499ea24c0c09c6e7e9ebfa3af90035570133f7c579.json index 5ce7b053fc..d2dcde86e5 100644 --- a/backend/.sqlx/query-7b5ad10af2a9b34fa86429499ea24c0c09c6e7e9ebfa3af90035570133f7c579.json +++ b/backend/.sqlx/query-7b5ad10af2a9b34fa86429499ea24c0c09c6e7e9ebfa3af90035570133f7c579.json @@ -69,7 +69,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -151,7 +154,8 @@ "postgres", "sqs", "gcp", - "mqtt" + "mqtt", + "nextcloud" ] } } diff --git a/backend/.sqlx/query-488dd591096b2b47787afdc3a1d73917ed13269f2ee20b86df79fca2c8efe672.json b/backend/.sqlx/query-7bb8ff3426ec73672fb572cd1ce36f495a84e0ad7d60fd39eddcccbc129d43c8.json similarity index 69% rename from backend/.sqlx/query-488dd591096b2b47787afdc3a1d73917ed13269f2ee20b86df79fca2c8efe672.json rename to backend/.sqlx/query-7bb8ff3426ec73672fb572cd1ce36f495a84e0ad7d60fd39eddcccbc129d43c8.json index 3cfb853bc2..66baa9f216 100644 --- a/backend/.sqlx/query-488dd591096b2b47787afdc3a1d73917ed13269f2ee20b86df79fca2c8efe672.json +++ b/backend/.sqlx/query-7bb8ff3426ec73672fb572cd1ce36f495a84e0ad7d60fd39eddcccbc129d43c8.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $16)\n )\n INSERT INTO v2_job\n (id, workspace_id, raw_code, raw_lock, raw_flow, tag, runnable_id, runnable_path, kind,\n script_lang, created_by, permissioned_as, permissioned_as_email, concurrent_limit,\n concurrency_time_window_s, timeout, args)\n (SELECT uuid, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15,\n ('{ \"uuid\": \"' || uuid || '\" }')::jsonb FROM uuid_table)\n RETURNING id AS \"id!\"", + "query": "WITH uuid_table as (\n select gen_random_uuid() as uuid from generate_series(1, $16)\n )\n INSERT INTO v2_job\n (id, workspace_id, raw_code, raw_lock, raw_flow, tag, runnable_id, runnable_path, kind,\n script_lang, created_by, permissioned_as, permissioned_as_email, concurrent_limit,\n concurrency_time_window_s, timeout, args)\n (SELECT uuid, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15,\n ('{ \"uuid\": \"' || uuid || '\" }')::jsonb FROM uuid_table)\n RETURNING id AS \"id!\"", "describe": { "columns": [ { @@ -41,7 +41,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -91,5 +94,5 @@ false ] }, - "hash": "488dd591096b2b47787afdc3a1d73917ed13269f2ee20b86df79fca2c8efe672" + "hash": "7bb8ff3426ec73672fb572cd1ce36f495a84e0ad7d60fd39eddcccbc129d43c8" } diff --git a/backend/.sqlx/query-805d633de90fee335f1726284eda0dbc200d45960fb8dea867492c8c7dd096d5.json b/backend/.sqlx/query-805d633de90fee335f1726284eda0dbc200d45960fb8dea867492c8c7dd096d5.json index c18d338929..1ab5ea959b 100644 --- a/backend/.sqlx/query-805d633de90fee335f1726284eda0dbc200d45960fb8dea867492c8c7dd096d5.json +++ b/backend/.sqlx/query-805d633de90fee335f1726284eda0dbc200d45960fb8dea867492c8c7dd096d5.json @@ -39,7 +39,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } diff --git a/backend/.sqlx/query-fe78796c5ea2384bdb6191b66943d746de874ca6600178f5db9797eda44f1733.json b/backend/.sqlx/query-80c8da5b571f8da7cde6da8557898e25fdfdf19ea7a85b5e55922883764df15f.json similarity index 61% rename from backend/.sqlx/query-fe78796c5ea2384bdb6191b66943d746de874ca6600178f5db9797eda44f1733.json rename to backend/.sqlx/query-80c8da5b571f8da7cde6da8557898e25fdfdf19ea7a85b5e55922883764df15f.json index 6881d0aef0..1879fa69ec 100644 --- a/backend/.sqlx/query-fe78796c5ea2384bdb6191b66943d746de874ca6600178f5db9797eda44f1733.json +++ b/backend/.sqlx/query-80c8da5b571f8da7cde6da8557898e25fdfdf19ea7a85b5e55922883764df15f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO mqtt_trigger (\n mqtt_resource_path,\n subscribe_topics,\n client_version,\n client_id,\n v3_config,\n v5_config,\n workspace_id,\n path, \n script_path, \n is_flow, \n email, \n enabled, \n edited_by,\n error_handler_path,\n error_handler_args,\n retry\n ) \n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16\n )", + "query": "\n INSERT INTO mqtt_trigger (\n mqtt_resource_path,\n subscribe_topics,\n client_version,\n client_id,\n v3_config,\n v5_config,\n workspace_id,\n path, \n script_path, \n is_flow, \n email, \n mode, \n edited_by,\n error_handler_path,\n error_handler_args,\n retry\n ) \n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16\n )", "describe": { "columns": [], "parameters": { @@ -26,7 +26,18 @@ "Varchar", "Bool", "Varchar", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, "Varchar", "Varchar", "Jsonb", @@ -35,5 +46,5 @@ }, "nullable": [] }, - "hash": "fe78796c5ea2384bdb6191b66943d746de874ca6600178f5db9797eda44f1733" + "hash": "80c8da5b571f8da7cde6da8557898e25fdfdf19ea7a85b5e55922883764df15f" } diff --git a/backend/.sqlx/query-76690ea29969da28840c90016ec78e5ea7f326df9f19e9d2f0445e662c921fea.json b/backend/.sqlx/query-83785ee2f7dcc7f2252b0e8bcc8322dfd7689d615a34b63e29c9c6699b7e5514.json similarity index 59% rename from backend/.sqlx/query-76690ea29969da28840c90016ec78e5ea7f326df9f19e9d2f0445e662c921fea.json rename to backend/.sqlx/query-83785ee2f7dcc7f2252b0e8bcc8322dfd7689d615a34b63e29c9c6699b7e5514.json index 67ad1da697..1c1288e21c 100644 --- a/backend/.sqlx/query-76690ea29969da28840c90016ec78e5ea7f326df9f19e9d2f0445e662c921fea.json +++ b/backend/.sqlx/query-83785ee2f7dcc7f2252b0e8bcc8322dfd7689d615a34b63e29c9c6699b7e5514.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT q.id, f.flow_status, q.suspend, j.runnable_path AS script_path\n FROM v2_job_queue q\n JOIN v2_job j USING (id)\n JOIN v2_job_status f USING (id)\n WHERE id = ( SELECT parent_job FROM v2_job WHERE id = $1 )\n FOR UPDATE\n ", + "query": "\n SELECT q.id, f.flow_status, q.suspend, j.runnable_path AS script_path\n FROM v2_job_queue q\n JOIN v2_job j USING (id)\n JOIN v2_job_status f USING (id)\n WHERE id = ( SELECT parent_job FROM v2_job WHERE id = $1 )\n FOR UPDATE\n ", "describe": { "columns": [ { @@ -36,5 +36,5 @@ true ] }, - "hash": "76690ea29969da28840c90016ec78e5ea7f326df9f19e9d2f0445e662c921fea" + "hash": "83785ee2f7dcc7f2252b0e8bcc8322dfd7689d615a34b63e29c9c6699b7e5514" } diff --git a/backend/.sqlx/query-29785f22ee7092e8cebeae3757caf0563e3f98fff1f100616c33e8dc95fbff99.json b/backend/.sqlx/query-848c5e2c26f77341d3f3bbb49c5d90b8bca91ce425bfbe39b1c1c6520ad38419.json similarity index 67% rename from backend/.sqlx/query-29785f22ee7092e8cebeae3757caf0563e3f98fff1f100616c33e8dc95fbff99.json rename to backend/.sqlx/query-848c5e2c26f77341d3f3bbb49c5d90b8bca91ce425bfbe39b1c1c6520ad38419.json index 11f612ccda..56e96301dc 100644 --- a/backend/.sqlx/query-29785f22ee7092e8cebeae3757caf0563e3f98fff1f100616c33e8dc95fbff99.json +++ b/backend/.sqlx/query-848c5e2c26f77341d3f3bbb49c5d90b8bca91ce425bfbe39b1c1c6520ad38419.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT j.created_by AS \"created_by!\", j.args as \"args: sqlx::types::Json>\"\n FROM v2_job j\n WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", + "query": "SELECT j.created_by AS \"created_by!\", j.args as \"args: sqlx::types::Json>\"\n FROM v2_job j\n WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", "describe": { "columns": [ { @@ -26,5 +26,5 @@ true ] }, - "hash": "29785f22ee7092e8cebeae3757caf0563e3f98fff1f100616c33e8dc95fbff99" + "hash": "848c5e2c26f77341d3f3bbb49c5d90b8bca91ce425bfbe39b1c1c6520ad38419" } diff --git a/backend/.sqlx/query-85deb351e212f49be71ec80e4fae0b31a1a89446f65354cecc484b10e2ddc9da.json b/backend/.sqlx/query-85deb351e212f49be71ec80e4fae0b31a1a89446f65354cecc484b10e2ddc9da.json new file mode 100644 index 0000000000..e3e77aa6fd --- /dev/null +++ b/backend/.sqlx/query-85deb351e212f49be71ec80e4fae0b31a1a89446f65354cecc484b10e2ddc9da.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM concurrency_key WHERE job_id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "85deb351e212f49be71ec80e4fae0b31a1a89446f65354cecc484b10e2ddc9da" +} diff --git a/backend/.sqlx/query-ada1a14b4f25e41742df153b07e8bdf5b5feac9c6fa9de38bd24104bac8539d2.json b/backend/.sqlx/query-888f7e82c25b677172a276d042ae7066113f2a522068f04c8cee0895a49b787f.json similarity index 61% rename from backend/.sqlx/query-ada1a14b4f25e41742df153b07e8bdf5b5feac9c6fa9de38bd24104bac8539d2.json rename to backend/.sqlx/query-888f7e82c25b677172a276d042ae7066113f2a522068f04c8cee0895a49b787f.json index 1a33383b67..e8917e2b37 100644 --- a/backend/.sqlx/query-ada1a14b4f25e41742df153b07e8bdf5b5feac9c6fa9de38bd24104bac8539d2.json +++ b/backend/.sqlx/query-888f7e82c25b677172a276d042ae7066113f2a522068f04c8cee0895a49b787f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE\n http_trigger\n SET\n wrap_body = $1,\n raw_string = $2,\n authentication_resource_path = $3,\n script_path = $4,\n path = $5,\n is_flow = $6,\n enabled = $7,\n http_method = $8,\n static_asset_config = $9,\n edited_by = $10,\n email = $11,\n request_type = $12,\n authentication_method = $13,\n summary = $14,\n description = $15,\n edited_at = now(),\n is_static_website = $16,\n error_handler_path = $17,\n error_handler_args = $18,\n retry = $19\n WHERE\n workspace_id = $20 AND\n path = $21\n ", + "query": "\n UPDATE\n http_trigger\n SET\n wrap_body = $1,\n raw_string = $2,\n authentication_resource_path = $3,\n script_path = $4,\n path = $5,\n is_flow = $6,\n mode = $7,\n http_method = $8,\n static_asset_config = $9,\n edited_by = $10,\n email = $11,\n request_type = $12,\n authentication_method = $13,\n summary = $14,\n description = $15,\n edited_at = now(),\n is_static_website = $16,\n error_handler_path = $17,\n error_handler_args = $18,\n retry = $19\n WHERE\n workspace_id = $20 AND\n path = $21\n ", "describe": { "columns": [], "parameters": { @@ -11,7 +11,18 @@ "Varchar", "Varchar", "Bool", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, { "Custom": { "name": "http_method", @@ -68,5 +79,5 @@ }, "nullable": [] }, - "hash": "ada1a14b4f25e41742df153b07e8bdf5b5feac9c6fa9de38bd24104bac8539d2" + "hash": "888f7e82c25b677172a276d042ae7066113f2a522068f04c8cee0895a49b787f" } diff --git a/backend/.sqlx/query-46b44de4ea6eb15258c1cb9bbd21cab7fc350b46e8899d59759945eae02a6d40.json b/backend/.sqlx/query-8e99bd48ae67aed1b081c5f84df897541cd93dcda63c28395cd9edee374156f8.json similarity index 60% rename from backend/.sqlx/query-46b44de4ea6eb15258c1cb9bbd21cab7fc350b46e8899d59759945eae02a6d40.json rename to backend/.sqlx/query-8e99bd48ae67aed1b081c5f84df897541cd93dcda63c28395cd9edee374156f8.json index 72f8a2f814..f43bf31a14 100644 --- a/backend/.sqlx/query-46b44de4ea6eb15258c1cb9bbd21cab7fc350b46e8899d59759945eae02a6d40.json +++ b/backend/.sqlx/query-8e99bd48ae67aed1b081c5f84df897541cd93dcda63c28395cd9edee374156f8.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT EXISTS (SELECT 1 FROM resume_job WHERE id = $1)\n ", + "query": "\n SELECT EXISTS (SELECT 1 FROM resume_job WHERE id = $1)\n ", "describe": { "columns": [ { @@ -18,5 +18,5 @@ null ] }, - "hash": "46b44de4ea6eb15258c1cb9bbd21cab7fc350b46e8899d59759945eae02a6d40" + "hash": "8e99bd48ae67aed1b081c5f84df897541cd93dcda63c28395cd9edee374156f8" } diff --git a/backend/.sqlx/query-9272643a0efd45b7a2f9ed6957801d57b74f4f41f6df80324ae964447b90fe8f.json b/backend/.sqlx/query-9272643a0efd45b7a2f9ed6957801d57b74f4f41f6df80324ae964447b90fe8f.json new file mode 100644 index 0000000000..2ef169abe1 --- /dev/null +++ b/backend/.sqlx/query-9272643a0efd45b7a2f9ed6957801d57b74f4f41f6df80324ae964447b90fe8f.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n CASE \n WHEN flow_version.id IS NOT NULL THEN\n (flow_version.value -> 'flow_env' -> $3) #> $4\n ELSE\n (root_job.raw_flow -> 'flow_env' -> $3) #> $4\n END AS \"flow_env: sqlx::types::Json>\"\n FROM \n v2_job current_job\n JOIN \n v2_job root_job ON root_job.id = COALESCE(current_job.root_job, current_job.flow_innermost_root_job, current_job.parent_job, current_job.id)\n AND root_job.workspace_id = current_job.workspace_id\n LEFT JOIN\n flow_version ON flow_version.id = root_job.runnable_id\n AND flow_version.path = root_job.runnable_path\n AND flow_version.workspace_id = root_job.workspace_id\n WHERE \n current_job.id = $1 AND \n current_job.workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "flow_env: sqlx::types::Json>", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text", + "Text", + "TextArray" + ] + }, + "nullable": [ + null + ] + }, + "hash": "9272643a0efd45b7a2f9ed6957801d57b74f4f41f6df80324ae964447b90fe8f" +} diff --git a/backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json b/backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json index 32358798b9..f39f1be51b 100644 --- a/backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json +++ b/backend/.sqlx/query-92c7c961198e506426bf3f97a8ddbb34af450041c675b30b708fed3ef9e01d2d.json @@ -29,7 +29,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } diff --git a/backend/.sqlx/query-2729c73b53605908e2fa26b4e4bbbd75d6d5bb77e6a92bf187d978a059d7af4a.json b/backend/.sqlx/query-998ca12977082959544b6964333b090b54b1e7e039452283644ca53548704f39.json similarity index 56% rename from backend/.sqlx/query-2729c73b53605908e2fa26b4e4bbbd75d6d5bb77e6a92bf187d978a059d7af4a.json rename to backend/.sqlx/query-998ca12977082959544b6964333b090b54b1e7e039452283644ca53548704f39.json index afa445d679..ee081ea073 100644 --- a/backend/.sqlx/query-2729c73b53605908e2fa26b4e4bbbd75d6d5bb77e6a92bf187d978a059d7af4a.json +++ b/backend/.sqlx/query-998ca12977082959544b6964333b090b54b1e7e039452283644ca53548704f39.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT COUNT(*) FROM v2_job_queue q WHERE q.canceled_by IS NULL AND (q.scheduled_for <= now()\n OR (q.suspend_until IS NOT NULL\n AND (q.suspend <= 0 OR q.suspend_until <= now())))", + "query": "SELECT COUNT(*) FROM v2_job_queue q WHERE q.canceled_by IS NULL AND (q.scheduled_for <= now()\n OR (q.suspend_until IS NOT NULL\n AND (q.suspend <= 0 OR q.suspend_until <= now())))", "describe": { "columns": [ { @@ -16,5 +16,5 @@ null ] }, - "hash": "2729c73b53605908e2fa26b4e4bbbd75d6d5bb77e6a92bf187d978a059d7af4a" + "hash": "998ca12977082959544b6964333b090b54b1e7e039452283644ca53548704f39" } diff --git a/backend/.sqlx/query-9c0bbd44902d8eee393236f7c2372b273d14a093bb29ec12dda8bbfaecd49a35.json b/backend/.sqlx/query-9c0bbd44902d8eee393236f7c2372b273d14a093bb29ec12dda8bbfaecd49a35.json deleted file mode 100644 index 5740a1bda5..0000000000 --- a/backend/.sqlx/query-9c0bbd44902d8eee393236f7c2372b273d14a093bb29ec12dda8bbfaecd49a35.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH result_stream AS (\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n job_id, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $2 AND idx >= $3\n GROUP BY job_id\n )\n SELECT \n jc.result as \"result: sqlx::types::Json>\",\n v2_job.tag,\n v2_job_queue.running as \"running: Option\",\n rs.stream AS \"result_stream: Option\",\n rs.offset AS stream_offset,\n CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job\n FROM v2_job\n LEFT JOIN v2_job_queue USING (id)\n LEFT JOIN v2_job_completed jc USING (id)\n LEFT JOIN v2_job_status js USING (id)\n LEFT JOIN result_stream rs ON rs.job_id = $2\n WHERE v2_job.id = $2 AND v2_job.workspace_id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "result: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "tag", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "running: Option", - "type_info": "Bool" - }, - { - "ordinal": 3, - "name": "result_stream: Option", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "stream_offset", - "type_info": "Int4" - }, - { - "ordinal": 5, - "name": "stream_job", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text", - "Uuid", - "Int4", - "Bool" - ] - }, - "nullable": [ - true, - false, - false, - null, - null, - null - ] - }, - "hash": "9c0bbd44902d8eee393236f7c2372b273d14a093bb29ec12dda8bbfaecd49a35" -} diff --git a/backend/.sqlx/query-9ebf262393fc4a29e8f09b304dd99e786fe78dd21721e8f54dc943dd571a7e08.json b/backend/.sqlx/query-9ebf262393fc4a29e8f09b304dd99e786fe78dd21721e8f54dc943dd571a7e08.json deleted file mode 100644 index 53c7562d03..0000000000 --- a/backend/.sqlx/query-9ebf262393fc4a29e8f09b304dd99e786fe78dd21721e8f54dc943dd571a7e08.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO websocket_trigger (\n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n enabled,\n filters,\n initial_messages,\n url_runnable_args,\n edited_by,\n can_return_message,\n can_return_error_result,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, now(), $14, $15, $16\n )\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Varchar", - "Varchar", - "Varchar", - "Varchar", - "Bool", - "Bool", - "JsonbArray", - "JsonbArray", - "Jsonb", - "Varchar", - "Bool", - "Bool", - "Varchar", - "Varchar", - "Jsonb", - "Jsonb" - ] - }, - "nullable": [] - }, - "hash": "9ebf262393fc4a29e8f09b304dd99e786fe78dd21721e8f54dc943dd571a7e08" -} diff --git a/backend/.sqlx/query-9ecb404e46a4eac55f977f05a3afbafe5dc3cdecc17a3d5a7476b160c1b6e7e1.json b/backend/.sqlx/query-9ecb404e46a4eac55f977f05a3afbafe5dc3cdecc17a3d5a7476b160c1b6e7e1.json new file mode 100644 index 0000000000..79c8f0b45d --- /dev/null +++ b/backend/.sqlx/query-9ecb404e46a4eac55f977f05a3afbafe5dc3cdecc17a3d5a7476b160c1b6e7e1.json @@ -0,0 +1,47 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id FROM v2_job\n WHERE workspace_id = $1\n AND (kind = 'unassigned_script'::JOB_KIND OR kind = 'unassigned_flow'::JOB_KIND OR kind = 'unassigned_singlestepflow'::JOB_KIND)\n AND trigger_kind = $2\n AND trigger = $3\n AND id = ANY($4)", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text", + { + "Custom": { + "name": "job_trigger_kind", + "kind": { + "Enum": [ + "webhook", + "http", + "websocket", + "kafka", + "email", + "nats", + "schedule", + "app", + "ui", + "postgres", + "sqs", + "gcp", + "mqtt", + "nextcloud" + ] + } + } + }, + "Text", + "UuidArray" + ] + }, + "nullable": [ + false + ] + }, + "hash": "9ecb404e46a4eac55f977f05a3afbafe5dc3cdecc17a3d5a7476b160c1b6e7e1" +} diff --git a/backend/.sqlx/query-a1ab1f23f49496f745d89d6c33a91c6b693afc4477a634aa84f81db91f9e03a4.json b/backend/.sqlx/query-a1ab1f23f49496f745d89d6c33a91c6b693afc4477a634aa84f81db91f9e03a4.json new file mode 100644 index 0000000000..0c8209f0d0 --- /dev/null +++ b/backend/.sqlx/query-a1ab1f23f49496f745d89d6c33a91c6b693afc4477a634aa84f81db91f9e03a4.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow')\n ) FROM v2_job jb\n WHERE jb.id = $1 AND jb.workspace_id = $2\n GROUP BY jb.kind, jb.runnable_path", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "coalesce", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + }, + "nullable": [ + null + ] + }, + "hash": "a1ab1f23f49496f745d89d6c33a91c6b693afc4477a634aa84f81db91f9e03a4" +} diff --git a/backend/.sqlx/query-2f27fd4d7aed67c987d9f0cca0a2426f3fbd22c1f0759be943625ad5c921f91b.json b/backend/.sqlx/query-a2af6124a04fda8b6b3cf0075250733b33e76a4f823c7d0e12a79c66ed2b098a.json similarity index 50% rename from backend/.sqlx/query-2f27fd4d7aed67c987d9f0cca0a2426f3fbd22c1f0759be943625ad5c921f91b.json rename to backend/.sqlx/query-a2af6124a04fda8b6b3cf0075250733b33e76a4f823c7d0e12a79c66ed2b098a.json index eef5b530e7..5efc6805cd 100644 --- a/backend/.sqlx/query-2f27fd4d7aed67c987d9f0cca0a2426f3fbd22c1f0759be943625ad5c921f91b.json +++ b/backend/.sqlx/query-a2af6124a04fda8b6b3cf0075250733b33e76a4f823c7d0e12a79c66ed2b098a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n result AS \"result: sqlx::types::Json>\",\n result_columns,\n status = 'success' AS \"success!\",\n created_by AS \"created_by!\"\n FROM v2_job_completed c\n JOIN v2_job j USING (id)\n WHERE c.id = $1 AND c.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", + "query": "SELECT\n result AS \"result: sqlx::types::Json>\",\n result_columns,\n created_by AS \"created_by!\"\n FROM v2_job_completed c\n JOIN v2_job USING (id)\n WHERE c.id = $1 AND c.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", "describe": { "columns": [ { @@ -15,11 +15,6 @@ }, { "ordinal": 2, - "name": "success!", - "type_info": "Bool" - }, - { - "ordinal": 3, "name": "created_by!", "type_info": "Varchar" } @@ -34,9 +29,8 @@ "nullable": [ true, true, - null, false ] }, - "hash": "2f27fd4d7aed67c987d9f0cca0a2426f3fbd22c1f0759be943625ad5c921f91b" + "hash": "a2af6124a04fda8b6b3cf0075250733b33e76a4f823c7d0e12a79c66ed2b098a" } diff --git a/backend/.sqlx/query-a4b6371d33206010b2f3ffd2b09e33244fe8ab9a803248fc23f334034d24aad4.json b/backend/.sqlx/query-a4b6371d33206010b2f3ffd2b09e33244fe8ab9a803248fc23f334034d24aad4.json index 3a843d3ab3..da6d213748 100644 --- a/backend/.sqlx/query-a4b6371d33206010b2f3ffd2b09e33244fe8ab9a803248fc23f334034d24aad4.json +++ b/backend/.sqlx/query-a4b6371d33206010b2f3ffd2b09e33244fe8ab9a803248fc23f334034d24aad4.json @@ -74,7 +74,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -181,7 +184,8 @@ "postgres", "sqs", "gcp", - "mqtt" + "mqtt", + "nextcloud" ] } } diff --git a/backend/.sqlx/query-7b5dc50b685afc7de0b99bd68e2217d660529c15b41860a4513f63d465bb9abb.json b/backend/.sqlx/query-a5313b8d1d29c13a5b23cd8b6f9bd4c3d96c53dd0d8702facfefaaf35b7d6b26.json similarity index 63% rename from backend/.sqlx/query-7b5dc50b685afc7de0b99bd68e2217d660529c15b41860a4513f63d465bb9abb.json rename to backend/.sqlx/query-a5313b8d1d29c13a5b23cd8b6f9bd4c3d96c53dd0d8702facfefaaf35b7d6b26.json index 7bd7c1a192..2bd076eb88 100644 --- a/backend/.sqlx/query-7b5dc50b685afc7de0b99bd68e2217d660529c15b41860a4513f63d465bb9abb.json +++ b/backend/.sqlx/query-a5313b8d1d29c13a5b23cd8b6f9bd4c3d96c53dd0d8702facfefaaf35b7d6b26.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO concurrency_counter(concurrency_id, job_uuids)\n VALUES ($1, '{}'::jsonb)", + "query": "INSERT INTO concurrency_counter(concurrency_id, job_uuids)\n VALUES ($1, '{}'::jsonb)", "describe": { "columns": [], "parameters": { @@ -10,5 +10,5 @@ }, "nullable": [] }, - "hash": "7b5dc50b685afc7de0b99bd68e2217d660529c15b41860a4513f63d465bb9abb" + "hash": "a5313b8d1d29c13a5b23cd8b6f9bd4c3d96c53dd0d8702facfefaaf35b7d6b26" } diff --git a/backend/.sqlx/query-a76eec5797ca8f97e63ed5542bf03873e7dfd1cf9fe984c769afb5a8bdb48d49.json b/backend/.sqlx/query-a76eec5797ca8f97e63ed5542bf03873e7dfd1cf9fe984c769afb5a8bdb48d49.json deleted file mode 100644 index ddf7d06e68..0000000000 --- a/backend/.sqlx/query-a76eec5797ca8f97e63ed5542bf03873e7dfd1cf9fe984c769afb5a8bdb48d49.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n WITH result_stream AS (\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n job_id, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $1 AND idx >= $3\n GROUP BY job_id\n )\n SELECT\n COALESCE(jc.result, jc.result) as \"result: sqlx::types::Json>\",\n jq.running as \"running: Option\",\n rs.stream AS \"result_stream: Option\",\n rs.offset AS stream_offset,\n CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job\n FROM (\n SELECT $1::uuid as job_id, $2::text as workspace_id\n ) base\n LEFT JOIN v2_job_completed jc ON jc.id = base.job_id AND jc.workspace_id = base.workspace_id\n LEFT JOIN v2_job_queue jq ON jq.id = base.job_id AND jq.workspace_id = base.workspace_id\n LEFT JOIN v2_job_status js ON js.id = base.job_id\n LEFT JOIN result_stream rs ON rs.job_id = base.job_id\n WHERE base.job_id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "result: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 1, - "name": "running: Option", - "type_info": "Bool" - }, - { - "ordinal": 2, - "name": "result_stream: Option", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "stream_offset", - "type_info": "Int4" - }, - { - "ordinal": 4, - "name": "stream_job", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text", - "Int4", - "Bool" - ] - }, - "nullable": [ - null, - false, - null, - null, - null - ] - }, - "hash": "a76eec5797ca8f97e63ed5542bf03873e7dfd1cf9fe984c769afb5a8bdb48d49" -} diff --git a/backend/.sqlx/query-a84e67035584bbdb02482026b9cc0808086c50f78947d43bb88628a481f41a1d.json b/backend/.sqlx/query-a84e67035584bbdb02482026b9cc0808086c50f78947d43bb88628a481f41a1d.json index 8b9b0246b2..19d6878850 100644 --- a/backend/.sqlx/query-a84e67035584bbdb02482026b9cc0808086c50f78947d43bb88628a481f41a1d.json +++ b/backend/.sqlx/query-a84e67035584bbdb02482026b9cc0808086c50f78947d43bb88628a481f41a1d.json @@ -64,7 +64,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -151,7 +154,8 @@ "postgres", "sqs", "gcp", - "mqtt" + "mqtt", + "nextcloud" ] } } diff --git a/backend/.sqlx/query-aec42faf7154602c2e87cd7e2d3dfed3c49a85d89be00243039f62197ed03aeb.json b/backend/.sqlx/query-aec42faf7154602c2e87cd7e2d3dfed3c49a85d89be00243039f62197ed03aeb.json new file mode 100644 index 0000000000..46309f6854 --- /dev/null +++ b/backend/.sqlx/query-aec42faf7154602c2e87cd7e2d3dfed3c49a85d89be00243039f62197ed03aeb.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM debounce_stale_data WHERE job_id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "aec42faf7154602c2e87cd7e2d3dfed3c49a85d89be00243039f62197ed03aeb" +} diff --git a/backend/.sqlx/query-b179a3f876ca659bed892d464bf51a733cc86a3204fcd9edccda63fddc97dced.json b/backend/.sqlx/query-b179a3f876ca659bed892d464bf51a733cc86a3204fcd9edccda63fddc97dced.json index ee004c664c..b1610d61d6 100644 --- a/backend/.sqlx/query-b179a3f876ca659bed892d464bf51a733cc86a3204fcd9edccda63fddc97dced.json +++ b/backend/.sqlx/query-b179a3f876ca659bed892d464bf51a733cc86a3204fcd9edccda63fddc97dced.json @@ -40,7 +40,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -118,7 +121,8 @@ "postgres", "sqs", "gcp", - "mqtt" + "mqtt", + "nextcloud" ] } } diff --git a/backend/.sqlx/query-b1a11db5617e8282f5d8256f193be955155b7d0344dfa908451faf2a0ba9269b.json b/backend/.sqlx/query-b1a11db5617e8282f5d8256f193be955155b7d0344dfa908451faf2a0ba9269b.json index 73ad47bc5c..94acdfb2d8 100644 --- a/backend/.sqlx/query-b1a11db5617e8282f5d8256f193be955155b7d0344dfa908451faf2a0ba9269b.json +++ b/backend/.sqlx/query-b1a11db5617e8282f5d8256f193be955155b7d0344dfa908451faf2a0ba9269b.json @@ -38,7 +38,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -101,7 +104,8 @@ "postgres", "sqs", "gcp", - "mqtt" + "mqtt", + "nextcloud" ] } } diff --git a/backend/.sqlx/query-b1a1781cbda48313cac7422034fb05e1db8eae05cd3e87969b2c8786e02b1a0b.json b/backend/.sqlx/query-b1a1781cbda48313cac7422034fb05e1db8eae05cd3e87969b2c8786e02b1a0b.json new file mode 100644 index 0000000000..e470b53af4 --- /dev/null +++ b/backend/.sqlx/query-b1a1781cbda48313cac7422034fb05e1db8eae05cd3e87969b2c8786e02b1a0b.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job_queue SET scheduled_for = now() WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "b1a1781cbda48313cac7422034fb05e1db8eae05cd3e87969b2c8786e02b1a0b" +} diff --git a/backend/.sqlx/query-b2f6327828acc4ba1872c20a138ac5fb13680b0e574d6c7c16e3bc420dd90727.json b/backend/.sqlx/query-b2f6327828acc4ba1872c20a138ac5fb13680b0e574d6c7c16e3bc420dd90727.json deleted file mode 100644 index 9695c3e1c3..0000000000 --- a/backend/.sqlx/query-b2f6327828acc4ba1872c20a138ac5fb13680b0e574d6c7c16e3bc420dd90727.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COUNT(*) as count\n FROM v2_job_queue\n WHERE scheduled_for < to_timestamp($1::bigint / 1000.0)\n AND running = false\n AND suspend_until IS NULL", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "count", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int8" - ] - }, - "nullable": [ - null - ] - }, - "hash": "b2f6327828acc4ba1872c20a138ac5fb13680b0e574d6c7c16e3bc420dd90727" -} diff --git a/backend/.sqlx/query-aeaae7641539d51f4535b04e9a7adb7545bd37d0d706edb54b8f5507403105ee.json b/backend/.sqlx/query-b6a431847363eda74ffbacdaccedb0aa19608014983b41522befd9e2e00033d6.json similarity index 63% rename from backend/.sqlx/query-aeaae7641539d51f4535b04e9a7adb7545bd37d0d706edb54b8f5507403105ee.json rename to backend/.sqlx/query-b6a431847363eda74ffbacdaccedb0aa19608014983b41522befd9e2e00033d6.json index 4500ce4788..e47548ef79 100644 --- a/backend/.sqlx/query-aeaae7641539d51f4535b04e9a7adb7545bd37d0d706edb54b8f5507403105ee.json +++ b/backend/.sqlx/query-b6a431847363eda74ffbacdaccedb0aa19608014983b41522befd9e2e00033d6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT resume_id, approver\n FROM resume_job\n WHERE job = $1\n ", + "query": "\n SELECT resume_id, approver\n FROM resume_job\n WHERE job = $1\n ", "describe": { "columns": [ { @@ -24,5 +24,5 @@ true ] }, - "hash": "aeaae7641539d51f4535b04e9a7adb7545bd37d0d706edb54b8f5507403105ee" + "hash": "b6a431847363eda74ffbacdaccedb0aa19608014983b41522befd9e2e00033d6" } diff --git a/backend/.sqlx/query-b82f97f2a858e20b1fb6d9b0b3eab5bab2bafa91689eac5073c1678c338afb27.json b/backend/.sqlx/query-b82f97f2a858e20b1fb6d9b0b3eab5bab2bafa91689eac5073c1678c338afb27.json deleted file mode 100644 index 64b774cbd9..0000000000 --- a/backend/.sqlx/query-b82f97f2a858e20b1fb6d9b0b3eab5bab2bafa91689eac5073c1678c338afb27.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT \n path \n FROM \n flow_version \n WHERE \n id = $1 AND \n workspace_id = $2\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "path", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Int8", - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "b82f97f2a858e20b1fb6d9b0b3eab5bab2bafa91689eac5073c1678c338afb27" -} diff --git a/backend/.sqlx/query-bc0bd9659bb8b58ed5967754b1c403e77ebb044063f0659485f44b1d04bb6b4d.json b/backend/.sqlx/query-bc0bd9659bb8b58ed5967754b1c403e77ebb044063f0659485f44b1d04bb6b4d.json new file mode 100644 index 0000000000..1592041803 --- /dev/null +++ b/backend/.sqlx/query-bc0bd9659bb8b58ed5967754b1c403e77ebb044063f0659485f44b1d04bb6b4d.json @@ -0,0 +1,25 @@ +{ + "db_name": "PostgreSQL", + "query": "WITH uuid_table as (\n select unnest($4::uuid[]) as uuid\n )\n INSERT INTO v2_job_queue\n (id, workspace_id, scheduled_for, tag)\n (SELECT uuid, $1, $2, $3 FROM uuid_table)\n RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Timestamptz", + "Varchar", + "UuidArray" + ] + }, + "nullable": [ + false + ] + }, + "hash": "bc0bd9659bb8b58ed5967754b1c403e77ebb044063f0659485f44b1d04bb6b4d" +} diff --git a/backend/.sqlx/query-d067bf59ed65562f1efbfbc9c264cbcb90e5a8d94d628cc78d1f2271c43e56f1.json b/backend/.sqlx/query-c0eddbf3f0e97334a6c1a4836a7229ddee4af66a4714cf76e05b5d9e17cb1217.json similarity index 58% rename from backend/.sqlx/query-d067bf59ed65562f1efbfbc9c264cbcb90e5a8d94d628cc78d1f2271c43e56f1.json rename to backend/.sqlx/query-c0eddbf3f0e97334a6c1a4836a7229ddee4af66a4714cf76e05b5d9e17cb1217.json index 53490d5b60..ce1ef89810 100644 --- a/backend/.sqlx/query-d067bf59ed65562f1efbfbc9c264cbcb90e5a8d94d628cc78d1f2271c43e56f1.json +++ b/backend/.sqlx/query-c0eddbf3f0e97334a6c1a4836a7229ddee4af66a4714cf76e05b5d9e17cb1217.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO v2_job_status (id, workflow_as_code_status) VALUES ($1, '{}'::JSONB)\n ON CONFLICT (id) DO NOTHING", + "query": "INSERT INTO v2_job_status (id, workflow_as_code_status) VALUES ($1, '{}'::JSONB)\n ON CONFLICT (id) DO NOTHING", "describe": { "columns": [], "parameters": { @@ -10,5 +10,5 @@ }, "nullable": [] }, - "hash": "d067bf59ed65562f1efbfbc9c264cbcb90e5a8d94d628cc78d1f2271c43e56f1" + "hash": "c0eddbf3f0e97334a6c1a4836a7229ddee4af66a4714cf76e05b5d9e17cb1217" } diff --git a/backend/.sqlx/query-c444549636a8520c3ad1164491a495372104786fa968e8b7e9e8a5b8fe775f99.json b/backend/.sqlx/query-c444549636a8520c3ad1164491a495372104786fa968e8b7e9e8a5b8fe775f99.json deleted file mode 100644 index aa77f61309..0000000000 --- a/backend/.sqlx/query-c444549636a8520c3ad1164491a495372104786fa968e8b7e9e8a5b8fe775f99.json +++ /dev/null @@ -1,214 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n workspace_id,\n slack_team_id,\n teams_team_id,\n teams_team_name,\n slack_name,\n slack_command_script,\n teams_command_script,\n slack_email,\n slack_oauth_client_id,\n slack_oauth_client_secret,\n auto_invite_domain,\n auto_invite_operator,\n auto_add,\n customer_id,\n plan,\n webhook,\n deploy_to,\n ai_config,\n error_handler,\n error_handler_extra_args,\n error_handler_muted_on_cancel,\n large_file_storage,\n ducklake,\n git_sync,\n deploy_ui,\n default_app,\n default_scripts,\n mute_critical_alerts,\n color,\n operator_settings,\n git_app_installations,\n auto_add_instance_groups,\n auto_add_instance_groups_roles\n FROM\n workspace_settings\n WHERE\n workspace_id = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "workspace_id", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "slack_team_id", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "teams_team_id", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "teams_team_name", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "slack_name", - "type_info": "Varchar" - }, - { - "ordinal": 5, - "name": "slack_command_script", - "type_info": "Varchar" - }, - { - "ordinal": 6, - "name": "teams_command_script", - "type_info": "Text" - }, - { - "ordinal": 7, - "name": "slack_email", - "type_info": "Varchar" - }, - { - "ordinal": 8, - "name": "slack_oauth_client_id", - "type_info": "Varchar" - }, - { - "ordinal": 9, - "name": "slack_oauth_client_secret", - "type_info": "Varchar" - }, - { - "ordinal": 10, - "name": "auto_invite_domain", - "type_info": "Varchar" - }, - { - "ordinal": 11, - "name": "auto_invite_operator", - "type_info": "Bool" - }, - { - "ordinal": 12, - "name": "auto_add", - "type_info": "Bool" - }, - { - "ordinal": 13, - "name": "customer_id", - "type_info": "Varchar" - }, - { - "ordinal": 14, - "name": "plan", - "type_info": "Varchar" - }, - { - "ordinal": 15, - "name": "webhook", - "type_info": "Text" - }, - { - "ordinal": 16, - "name": "deploy_to", - "type_info": "Varchar" - }, - { - "ordinal": 17, - "name": "ai_config", - "type_info": "Jsonb" - }, - { - "ordinal": 18, - "name": "error_handler", - "type_info": "Varchar" - }, - { - "ordinal": 19, - "name": "error_handler_extra_args", - "type_info": "Json" - }, - { - "ordinal": 20, - "name": "error_handler_muted_on_cancel", - "type_info": "Bool" - }, - { - "ordinal": 21, - "name": "large_file_storage", - "type_info": "Jsonb" - }, - { - "ordinal": 22, - "name": "ducklake", - "type_info": "Jsonb" - }, - { - "ordinal": 23, - "name": "git_sync", - "type_info": "Jsonb" - }, - { - "ordinal": 24, - "name": "deploy_ui", - "type_info": "Jsonb" - }, - { - "ordinal": 25, - "name": "default_app", - "type_info": "Varchar" - }, - { - "ordinal": 26, - "name": "default_scripts", - "type_info": "Jsonb" - }, - { - "ordinal": 27, - "name": "mute_critical_alerts", - "type_info": "Bool" - }, - { - "ordinal": 28, - "name": "color", - "type_info": "Varchar" - }, - { - "ordinal": 29, - "name": "operator_settings", - "type_info": "Jsonb" - }, - { - "ordinal": 30, - "name": "git_app_installations", - "type_info": "Jsonb" - }, - { - "ordinal": 31, - "name": "auto_add_instance_groups", - "type_info": "TextArray" - }, - { - "ordinal": 32, - "name": "auto_add_instance_groups_roles", - "type_info": "Jsonb" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - true, - true, - true, - true, - true, - true, - false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true, - true, - true, - true, - true, - true, - true, - true, - false, - true, - true - ] - }, - "hash": "c444549636a8520c3ad1164491a495372104786fa968e8b7e9e8a5b8fe775f99" -} diff --git a/backend/.sqlx/query-9abbb75c238372cc3dbaebf1dbc50ff78df6c5dcf92b08d611afe23c5b865521.json b/backend/.sqlx/query-cad914554762b6be72b289814f77072600f8f1bc1fa73ef6f63972775aedf044.json similarity index 58% rename from backend/.sqlx/query-9abbb75c238372cc3dbaebf1dbc50ff78df6c5dcf92b08d611afe23c5b865521.json rename to backend/.sqlx/query-cad914554762b6be72b289814f77072600f8f1bc1fa73ef6f63972775aedf044.json index 65dc60db73..1c2146bfce 100644 --- a/backend/.sqlx/query-9abbb75c238372cc3dbaebf1dbc50ff78df6c5dcf92b08d611afe23c5b865521.json +++ b/backend/.sqlx/query-cad914554762b6be72b289814f77072600f8f1bc1fa73ef6f63972775aedf044.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO sqs_trigger (\n workspace_id,\n path,\n queue_url,\n aws_resource_path,\n message_attributes,\n aws_auth_resource_type,\n script_path,\n is_flow,\n enabled,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now(), $12, $13, $14\n )\n ", + "query": "\n INSERT INTO sqs_trigger (\n workspace_id,\n path,\n queue_url,\n aws_resource_path,\n message_attributes,\n aws_auth_resource_type,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, now(), $12, $13, $14\n )\n ", "describe": { "columns": [], "parameters": { @@ -23,7 +23,18 @@ }, "Varchar", "Bool", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, "Varchar", "Varchar", "Varchar", @@ -33,5 +44,5 @@ }, "nullable": [] }, - "hash": "9abbb75c238372cc3dbaebf1dbc50ff78df6c5dcf92b08d611afe23c5b865521" + "hash": "cad914554762b6be72b289814f77072600f8f1bc1fa73ef6f63972775aedf044" } diff --git a/backend/.sqlx/query-cd333b93d70640bfa124d334ab324629fb873a7234b56cc554e43db6585fbc49.json b/backend/.sqlx/query-cd333b93d70640bfa124d334ab324629fb873a7234b56cc554e43db6585fbc49.json new file mode 100644 index 0000000000..df4b05af12 --- /dev/null +++ b/backend/.sqlx/query-cd333b93d70640bfa124d334ab324629fb873a7234b56cc554e43db6585fbc49.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO resume_job\n (id, resume_id, job, flow, value, approver, approved)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Uuid", + "Int4", + "Uuid", + "Uuid", + "Jsonb", + "Varchar", + "Bool" + ] + }, + "nullable": [] + }, + "hash": "cd333b93d70640bfa124d334ab324629fb873a7234b56cc554e43db6585fbc49" +} diff --git a/backend/.sqlx/query-d1f505524793c03a63967d058ed1fd381094070dd7707edac81e1b5e7d1d958b.json b/backend/.sqlx/query-d1f505524793c03a63967d058ed1fd381094070dd7707edac81e1b5e7d1d958b.json deleted file mode 100644 index 979d328083..0000000000 --- a/backend/.sqlx/query-d1f505524793c03a63967d058ed1fd381094070dd7707edac81e1b5e7d1d958b.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT 1 FROM resource WHERE workspace_id = $1 AND path = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "d1f505524793c03a63967d058ed1fd381094070dd7707edac81e1b5e7d1d958b" -} diff --git a/backend/.sqlx/query-d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2.json b/backend/.sqlx/query-d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2.json deleted file mode 100644 index 5ebf571855..0000000000 --- a/backend/.sqlx/query-d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT COALESCE(\n (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC),\n (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow')\n ) FROM v2_job jb\n WHERE jb.id = $1 AND jb.workspace_id = $2\n GROUP BY jb.kind, jb.runnable_path", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "coalesce", - "type_info": "Json" - } - ], - "parameters": { - "Left": [ - "Uuid", - "Text" - ] - }, - "nullable": [ - null - ] - }, - "hash": "d1fdfabd4f3bbe93cf9667e8e76bd8f537e68fabceb094fe535693fbd146eff2" -} diff --git a/backend/.sqlx/query-d2fe10b3e608407147adb704a7cf3da60f22dcce6f05852de2f75c737b57907f.json b/backend/.sqlx/query-d2fe10b3e608407147adb704a7cf3da60f22dcce6f05852de2f75c737b57907f.json deleted file mode 100644 index 8e693056a0..0000000000 --- a/backend/.sqlx/query-d2fe10b3e608407147adb704a7cf3da60f22dcce6f05852de2f75c737b57907f.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT \n schema \n FROM \n flow \n WHERE \n workspace_id = $1 AND \n path = $2\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "schema", - "type_info": "Json" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "d2fe10b3e608407147adb704a7cf3da60f22dcce6f05852de2f75c737b57907f" -} diff --git a/backend/.sqlx/query-fe12006498b9e7aece6b104fc5d9a80eeac12c6ca07a0e8950941ba796b0ba79.json b/backend/.sqlx/query-d33810c502838ad7aeb1f45691a456ec563d346d99256ded52756cdb4a0d7ffd.json similarity index 65% rename from backend/.sqlx/query-fe12006498b9e7aece6b104fc5d9a80eeac12c6ca07a0e8950941ba796b0ba79.json rename to backend/.sqlx/query-d33810c502838ad7aeb1f45691a456ec563d346d99256ded52756cdb4a0d7ffd.json index 150d2d9504..1060c7af74 100644 --- a/backend/.sqlx/query-fe12006498b9e7aece6b104fc5d9a80eeac12c6ca07a0e8950941ba796b0ba79.json +++ b/backend/.sqlx/query-d33810c502838ad7aeb1f45691a456ec563d346d99256ded52756cdb4a0d7ffd.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE email_trigger \n SET \n script_path = $1,\n path = $2,\n is_flow = $3,\n local_part = $4,\n workspaced_local_part = $5,\n edited_by = $6,\n email = $7,\n edited_at = now(),\n error_handler_path = $8,\n error_handler_args = $9,\n retry = $10,\n enabled = $11\n WHERE \n workspace_id = $12 AND path = $13\n ", + "query": "\n UPDATE email_trigger \n SET \n script_path = $1,\n path = $2,\n is_flow = $3,\n local_part = $4,\n workspaced_local_part = $5,\n edited_by = $6,\n email = $7,\n edited_at = now(),\n error_handler_path = $8,\n error_handler_args = $9,\n retry = $10,\n mode = $11\n WHERE \n workspace_id = $12 AND path = $13\n ", "describe": { "columns": [], "parameters": { @@ -15,12 +15,23 @@ "Varchar", "Jsonb", "Jsonb", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, "Text", "Text" ] }, "nullable": [] }, - "hash": "fe12006498b9e7aece6b104fc5d9a80eeac12c6ca07a0e8950941ba796b0ba79" + "hash": "d33810c502838ad7aeb1f45691a456ec563d346d99256ded52756cdb4a0d7ffd" } diff --git a/backend/.sqlx/query-4b056d33215b3a1e9849bb66ce84e96c69d10e3970e38151c97d1fca0cb7388d.json b/backend/.sqlx/query-d75346523ede1a7677cca073ce894b540d637b1067ece6209d9365c92fc59e03.json similarity index 50% rename from backend/.sqlx/query-4b056d33215b3a1e9849bb66ce84e96c69d10e3970e38151c97d1fca0cb7388d.json rename to backend/.sqlx/query-d75346523ede1a7677cca073ce894b540d637b1067ece6209d9365c92fc59e03.json index da7115eb81..504e8f451e 100644 --- a/backend/.sqlx/query-4b056d33215b3a1e9849bb66ce84e96c69d10e3970e38151c97d1fca0cb7388d.json +++ b/backend/.sqlx/query-d75346523ede1a7677cca073ce894b540d637b1067ece6209d9365c92fc59e03.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n WITH result_stream AS (\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n job_id, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $2 AND idx >= $3\n GROUP BY job_id\n )\n SELECT\n COALESCE(jc.result, NULL) as \"result: sqlx::types::Json>\",\n rs.stream AS \"result_stream: Option\",\n rs.offset AS stream_offset,\n COALESCE(js.flow_status, jc.flow_status) as \"flow_status: sqlx::types::Json>\",\n CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job\n FROM (\n SELECT $2::uuid as job_id, $1::text as workspace_id\n ) base\n LEFT JOIN v2_job_completed jc ON jc.id = base.job_id AND jc.workspace_id = base.workspace_id\n LEFT JOIN v2_job_status js ON js.id = base.job_id\n LEFT JOIN result_stream rs ON rs.job_id = base.job_id\n WHERE base.job_id = $2", + "query": "\n WITH result_stream AS (\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n job_id, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $2 AND idx >= $3\n GROUP BY job_id\n )\n SELECT \n jc.result as \"result: sqlx::types::Json>\",\n v2_job.tag,\n v2_job_queue.running as \"running: Option\",\n rs.stream AS \"result_stream: Option\",\n rs.offset AS stream_offset,\n CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job\n FROM v2_job\n LEFT JOIN v2_job_queue USING (id)\n LEFT JOIN v2_job_completed jc USING (id)\n LEFT JOIN v2_job_status js USING (id)\n LEFT JOIN result_stream rs ON rs.job_id = $2\n WHERE v2_job.id = $2 AND v2_job.workspace_id = $1", "describe": { "columns": [ { @@ -10,21 +10,26 @@ }, { "ordinal": 1, + "name": "tag", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "running: Option", + "type_info": "Bool" + }, + { + "ordinal": 3, "name": "result_stream: Option", "type_info": "Text" }, { - "ordinal": 2, + "ordinal": 4, "name": "stream_offset", "type_info": "Int4" }, { - "ordinal": 3, - "name": "flow_status: sqlx::types::Json>", - "type_info": "Jsonb" - }, - { - "ordinal": 4, + "ordinal": 5, "name": "stream_job", "type_info": "Uuid" } @@ -38,12 +43,13 @@ ] }, "nullable": [ - null, - null, + true, + false, + false, null, null, null ] }, - "hash": "4b056d33215b3a1e9849bb66ce84e96c69d10e3970e38151c97d1fca0cb7388d" + "hash": "d75346523ede1a7677cca073ce894b540d637b1067ece6209d9365c92fc59e03" } diff --git a/backend/.sqlx/query-628d303956d55d22e5ac64496b66010d4476d5c222184f09e5a189975a37b941.json b/backend/.sqlx/query-de656102e898ebc90e53af8d36c882cf47e798567bb733e8b528dcedc48ad4bf.json similarity index 56% rename from backend/.sqlx/query-628d303956d55d22e5ac64496b66010d4476d5c222184f09e5a189975a37b941.json rename to backend/.sqlx/query-de656102e898ebc90e53af8d36c882cf47e798567bb733e8b528dcedc48ad4bf.json index 3f5e733cb1..6639e931ab 100644 --- a/backend/.sqlx/query-628d303956d55d22e5ac64496b66010d4476d5c222184f09e5a189975a37b941.json +++ b/backend/.sqlx/query-de656102e898ebc90e53af8d36c882cf47e798567bb733e8b528dcedc48ad4bf.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n script_path, \n is_flow, \n workspace_id, \n edited_by, \n email, \n path, \n error_handler_path as \"error_handler_path: _\", \n error_handler_args as \"error_handler_args: _\", \n retry as \"retry: _\" \n FROM email_trigger \n WHERE local_part = $1 \n AND workspaced_local_part = FALSE\n AND enabled IS TRUE\n ", + "query": "\n SELECT \n script_path, \n is_flow, \n workspace_id, \n mode as \"mode: _\",\n edited_by, \n email, \n path, \n error_handler_path as \"error_handler_path: _\", \n error_handler_args as \"error_handler_args: _\", \n retry as \"retry: _\" \n FROM email_trigger \n WHERE local_part = $1 \n AND workspaced_local_part = FALSE\n AND (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)\n ", "describe": { "columns": [ { @@ -20,31 +20,47 @@ }, { "ordinal": 3, + "name": "mode: _", + "type_info": { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + } + }, + { + "ordinal": 4, "name": "edited_by", "type_info": "Varchar" }, { - "ordinal": 4, + "ordinal": 5, "name": "email", "type_info": "Varchar" }, { - "ordinal": 5, + "ordinal": 6, "name": "path", "type_info": "Varchar" }, { - "ordinal": 6, + "ordinal": 7, "name": "error_handler_path: _", "type_info": "Varchar" }, { - "ordinal": 7, + "ordinal": 8, "name": "error_handler_args: _", "type_info": "Jsonb" }, { - "ordinal": 8, + "ordinal": 9, "name": "retry: _", "type_info": "Jsonb" } @@ -61,10 +77,11 @@ false, false, false, + false, true, true, true ] }, - "hash": "628d303956d55d22e5ac64496b66010d4476d5c222184f09e5a189975a37b941" + "hash": "de656102e898ebc90e53af8d36c882cf47e798567bb733e8b528dcedc48ad4bf" } diff --git a/backend/.sqlx/query-a8ccf0f70cb8286e9dcafda5cbd542af647ca392cf6e307dc9358ed8b2ba640f.json b/backend/.sqlx/query-e0ddd0ca6c3f94a26345d34a6b04c2e240bb90075bba33a91402655c9e764b30.json similarity index 60% rename from backend/.sqlx/query-a8ccf0f70cb8286e9dcafda5cbd542af647ca392cf6e307dc9358ed8b2ba640f.json rename to backend/.sqlx/query-e0ddd0ca6c3f94a26345d34a6b04c2e240bb90075bba33a91402655c9e764b30.json index b4ffae3d05..283d501103 100644 --- a/backend/.sqlx/query-a8ccf0f70cb8286e9dcafda5cbd542af647ca392cf6e307dc9358ed8b2ba640f.json +++ b/backend/.sqlx/query-e0ddd0ca6c3f94a26345d34a6b04c2e240bb90075bba33a91402655c9e764b30.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n result AS \"result: sqlx::types::Json>\",\n result_columns,\n created_by AS \"created_by!\"\n FROM v2_job_completed c\n JOIN v2_job USING (id)\n WHERE c.id = $1 AND c.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", + "query": "SELECT\n result AS \"result: sqlx::types::Json>\",\n result_columns,\n status = 'success' AS \"success!\",\n created_by AS \"created_by!\"\n FROM v2_job_completed c\n JOIN v2_job j USING (id)\n WHERE c.id = $1 AND c.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", "describe": { "columns": [ { @@ -15,6 +15,11 @@ }, { "ordinal": 2, + "name": "success!", + "type_info": "Bool" + }, + { + "ordinal": 3, "name": "created_by!", "type_info": "Varchar" } @@ -29,8 +34,9 @@ "nullable": [ true, true, + null, false ] }, - "hash": "a8ccf0f70cb8286e9dcafda5cbd542af647ca392cf6e307dc9358ed8b2ba640f" + "hash": "e0ddd0ca6c3f94a26345d34a6b04c2e240bb90075bba33a91402655c9e764b30" } diff --git a/backend/.sqlx/query-e1f10f940d7ba6b1652e3d6505e12b16394f9632fd535febb008ebfe9fe0b7c8.json b/backend/.sqlx/query-e1f10f940d7ba6b1652e3d6505e12b16394f9632fd535febb008ebfe9fe0b7c8.json new file mode 100644 index 0000000000..18dbf7953e --- /dev/null +++ b/backend/.sqlx/query-e1f10f940d7ba6b1652e3d6505e12b16394f9632fd535febb008ebfe9fe0b7c8.json @@ -0,0 +1,45 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE v2_job SET kind = $1 WHERE id = $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "job_kind", + "kind": { + "Enum": [ + "script", + "preview", + "flow", + "dependencies", + "flowpreview", + "script_hub", + "identity", + "flowdependencies", + "http", + "graphql", + "postgresql", + "noop", + "appdependencies", + "deploymentcallback", + "singlestepflow", + "flowscript", + "flownode", + "appscript", + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" + ] + } + } + }, + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "e1f10f940d7ba6b1652e3d6505e12b16394f9632fd535febb008ebfe9fe0b7c8" +} diff --git a/backend/.sqlx/query-b3c02fd225a6aa78785d466e7f033b38deb9c7fa17bd3836c9ad8884f27be84a.json b/backend/.sqlx/query-e2905bca184696a80357d8e4126832a902b3088d91fbbb858f6c0aa9de8a5ff7.json similarity index 57% rename from backend/.sqlx/query-b3c02fd225a6aa78785d466e7f033b38deb9c7fa17bd3836c9ad8884f27be84a.json rename to backend/.sqlx/query-e2905bca184696a80357d8e4126832a902b3088d91fbbb858f6c0aa9de8a5ff7.json index 313180bd10..cab697d254 100644 --- a/backend/.sqlx/query-b3c02fd225a6aa78785d466e7f033b38deb9c7fa17bd3836c9ad8884f27be84a.json +++ b/backend/.sqlx/query-e2905bca184696a80357d8e4126832a902b3088d91fbbb858f6c0aa9de8a5ff7.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n j.id,\n j.kind AS \"kind: _\",\n COALESCE(s.path, f.path) AS \"script_path!\",\n COALESCE(s.hash, f.id) AS \"script_hash!: _\",\n COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS \"scheduled_for!: _\",\n args AS input,\n COALESCE(s.schema, f.schema) AS \"schema: _\"\n FROM v2_job j\n LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script'\n LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow'\n LEFT JOIN v2_job_completed jc ON jc.id = j.id\n LEFT JOIN v2_job_queue jq ON jq.id = j.id\n WHERE j.id = ANY($1)\n AND j.workspace_id = $2\n AND COALESCE(s.hash, f.id) IS NOT NULL\n AND COALESCE(s.path, f.path) IS NOT NULL", + "query": "SELECT\n j.id,\n j.kind AS \"kind: _\",\n COALESCE(s.path, f.path) AS \"script_path!\",\n COALESCE(s.hash, f.id) AS \"script_hash!: _\",\n COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS \"scheduled_for!: _\",\n args AS input,\n COALESCE(s.schema, f.schema) AS \"schema: _\"\n FROM v2_job j\n LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script'\n LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow'\n LEFT JOIN v2_job_completed jc ON jc.id = j.id\n LEFT JOIN v2_job_queue jq ON jq.id = j.id\n WHERE j.id = ANY($1)\n AND j.workspace_id = $2\n AND COALESCE(s.hash, f.id) IS NOT NULL\n AND COALESCE(s.path, f.path) IS NOT NULL", "describe": { "columns": [ { @@ -34,7 +34,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -82,5 +85,5 @@ null ] }, - "hash": "b3c02fd225a6aa78785d466e7f033b38deb9c7fa17bd3836c9ad8884f27be84a" + "hash": "e2905bca184696a80357d8e4126832a902b3088d91fbbb858f6c0aa9de8a5ff7" } diff --git a/backend/.sqlx/query-e4d71278fb80126a7a9da73f1889352d4d1e3cb3a8a08f1c9c03055a1cab1235.json b/backend/.sqlx/query-e4d71278fb80126a7a9da73f1889352d4d1e3cb3a8a08f1c9c03055a1cab1235.json index 3319cce7ff..5b07bcd9c9 100644 --- a/backend/.sqlx/query-e4d71278fb80126a7a9da73f1889352d4d1e3cb3a8a08f1c9c03055a1cab1235.json +++ b/backend/.sqlx/query-e4d71278fb80126a7a9da73f1889352d4d1e3cb3a8a08f1c9c03055a1cab1235.json @@ -74,7 +74,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -181,7 +184,8 @@ "postgres", "sqs", "gcp", - "mqtt" + "mqtt", + "nextcloud" ] } } diff --git a/backend/.sqlx/query-dd92bce0ddbfbf06834807aab4d589fc104647cae7abf37ddce3ef7109726261.json b/backend/.sqlx/query-e72e81370a93a6c53a1da01db0df55074d4b5ffcd80cabbe0c5fedcd90e0712b.json similarity index 53% rename from backend/.sqlx/query-dd92bce0ddbfbf06834807aab4d589fc104647cae7abf37ddce3ef7109726261.json rename to backend/.sqlx/query-e72e81370a93a6c53a1da01db0df55074d4b5ffcd80cabbe0c5fedcd90e0712b.json index d30b0bae62..15f7b7eff7 100644 --- a/backend/.sqlx/query-dd92bce0ddbfbf06834807aab4d589fc104647cae7abf37ddce3ef7109726261.json +++ b/backend/.sqlx/query-e72e81370a93a6c53a1da01db0df55074d4b5ffcd80cabbe0c5fedcd90e0712b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $2 AND idx >= $1\n ", + "query": "\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $2 AND idx >= $1\n ", "describe": { "columns": [ { @@ -25,5 +25,5 @@ null ] }, - "hash": "dd92bce0ddbfbf06834807aab4d589fc104647cae7abf37ddce3ef7109726261" + "hash": "e72e81370a93a6c53a1da01db0df55074d4b5ffcd80cabbe0c5fedcd90e0712b" } diff --git a/backend/.sqlx/query-e99c958628d83e3fea054eaa182b1301e0b04c7eb9801d9de13b37bd908d902f.json b/backend/.sqlx/query-e99c958628d83e3fea054eaa182b1301e0b04c7eb9801d9de13b37bd908d902f.json new file mode 100644 index 0000000000..8993a7c789 --- /dev/null +++ b/backend/.sqlx/query-e99c958628d83e3fea054eaa182b1301e0b04c7eb9801d9de13b37bd908d902f.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO websocket_trigger (\n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n mode,\n filters,\n initial_messages,\n url_runnable_args,\n edited_by,\n can_return_message,\n can_return_error_result,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, now(), $14, $15, $16\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, + "JsonbArray", + "JsonbArray", + "Jsonb", + "Varchar", + "Bool", + "Bool", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "e99c958628d83e3fea054eaa182b1301e0b04c7eb9801d9de13b37bd908d902f" +} diff --git a/backend/.sqlx/query-ed5ff52b210e9158706ceffb4700108d4965e911656028be19b5ce530ab01d92.json b/backend/.sqlx/query-ed5ff52b210e9158706ceffb4700108d4965e911656028be19b5ce530ab01d92.json new file mode 100644 index 0000000000..17bcf37c00 --- /dev/null +++ b/backend/.sqlx/query-ed5ff52b210e9158706ceffb4700108d4965e911656028be19b5ce530ab01d92.json @@ -0,0 +1,49 @@ +{ + "db_name": "PostgreSQL", + "query": "\n WITH result_stream AS (\n SELECT \n string_agg(stream, '' order by idx asc) as stream, \n job_id, \n max(idx) + 1 as offset \n FROM job_result_stream_v2\n WHERE job_id = $2 AND idx >= $3\n GROUP BY job_id\n )\n SELECT\n COALESCE(jc.result, NULL) as \"result: sqlx::types::Json>\",\n rs.stream AS \"result_stream: Option\",\n rs.offset AS stream_offset,\n COALESCE(js.flow_status, jc.flow_status) as \"flow_status: sqlx::types::Json>\",\n CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job\n FROM (\n SELECT $2::uuid as job_id, $1::text as workspace_id\n ) base\n LEFT JOIN v2_job_completed jc ON jc.id = base.job_id AND jc.workspace_id = base.workspace_id\n LEFT JOIN v2_job_status js ON js.id = base.job_id\n LEFT JOIN result_stream rs ON rs.job_id = base.job_id\n WHERE base.job_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "result: sqlx::types::Json>", + "type_info": "Jsonb" + }, + { + "ordinal": 1, + "name": "result_stream: Option", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "stream_offset", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "flow_status: sqlx::types::Json>", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "stream_job", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text", + "Uuid", + "Int4", + "Bool" + ] + }, + "nullable": [ + null, + null, + null, + null, + null + ] + }, + "hash": "ed5ff52b210e9158706ceffb4700108d4965e911656028be19b5ce530ab01d92" +} diff --git a/backend/.sqlx/query-cad2dfc50d57c2298cb0ec05ed3b42b9fd3edd05c314c9d96ba33cad37fe35c2.json b/backend/.sqlx/query-ee56be959ce50ba4c8bfde170b5d965c0d35f69f62212549e14d3a2938be65c0.json similarity index 64% rename from backend/.sqlx/query-cad2dfc50d57c2298cb0ec05ed3b42b9fd3edd05c314c9d96ba33cad37fe35c2.json rename to backend/.sqlx/query-ee56be959ce50ba4c8bfde170b5d965c0d35f69f62212549e14d3a2938be65c0.json index 73bab032f1..26586919ee 100644 --- a/backend/.sqlx/query-cad2dfc50d57c2298cb0ec05ed3b42b9fd3edd05c314c9d96ba33cad37fe35c2.json +++ b/backend/.sqlx/query-ee56be959ce50ba4c8bfde170b5d965c0d35f69f62212549e14d3a2938be65c0.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id)\n SELECT unnest($1::uuid[]), $2, $3, $4, $5, $6, $7, $8", + "query": "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id)\n SELECT unnest($1::uuid[]), $2, $3, $4, $5, $6, $7, $8", "describe": { "columns": [], "parameters": { @@ -17,5 +17,5 @@ }, "nullable": [] }, - "hash": "cad2dfc50d57c2298cb0ec05ed3b42b9fd3edd05c314c9d96ba33cad37fe35c2" + "hash": "ee56be959ce50ba4c8bfde170b5d965c0d35f69f62212549e14d3a2938be65c0" } diff --git a/backend/.sqlx/query-94e2e899fc0a5134c29217feda1f0530d03548cc5c884c83d7b85d068e738b8e.json b/backend/.sqlx/query-ee9ee0fbf5dd72d190e18c56622244b984fdb36fbf70197d9f8cb6306c9670db.json similarity index 64% rename from backend/.sqlx/query-94e2e899fc0a5134c29217feda1f0530d03548cc5c884c83d7b85d068e738b8e.json rename to backend/.sqlx/query-ee9ee0fbf5dd72d190e18c56622244b984fdb36fbf70197d9f8cb6306c9670db.json index ca3310858b..47dafd738c 100644 --- a/backend/.sqlx/query-94e2e899fc0a5134c29217feda1f0530d03548cc5c884c83d7b85d068e738b8e.json +++ b/backend/.sqlx/query-ee9ee0fbf5dd72d190e18c56622244b984fdb36fbf70197d9f8cb6306c9670db.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO http_trigger (\n workspace_id,\n path,\n route_path,\n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path,\n summary,\n description,\n is_flow,\n enabled,\n request_type,\n authentication_method,\n http_method,\n static_asset_config,\n edited_by,\n email,\n edited_at,\n is_static_website,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, now(), $20, $21, $22, $23\n )\n ", + "query": "\n INSERT INTO http_trigger (\n workspace_id,\n path,\n route_path,\n route_path_key,\n workspaced_route,\n authentication_resource_path,\n wrap_body,\n raw_string,\n script_path,\n summary,\n description,\n is_flow,\n mode,\n request_type,\n authentication_method,\n http_method,\n static_asset_config,\n edited_by,\n email,\n edited_at,\n is_static_website,\n error_handler_path,\n error_handler_args,\n retry\n )\n VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, now(), $20, $21, $22, $23\n )\n ", "describe": { "columns": [], "parameters": { @@ -17,7 +17,18 @@ "Varchar", "Text", "Bool", - "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, { "Custom": { "name": "request_type", @@ -70,5 +81,5 @@ }, "nullable": [] }, - "hash": "94e2e899fc0a5134c29217feda1f0530d03548cc5c884c83d7b85d068e738b8e" + "hash": "ee9ee0fbf5dd72d190e18c56622244b984fdb36fbf70197d9f8cb6306c9670db" } diff --git a/backend/.sqlx/query-6df2ca52e3e8515c398943da701600a33e64525809fdd67bce63e28d98eababc.json b/backend/.sqlx/query-eff41f41ffa6793baa65131258d8c9b67669db25ad3923bdb43093fd7b409736.json similarity index 56% rename from backend/.sqlx/query-6df2ca52e3e8515c398943da701600a33e64525809fdd67bce63e28d98eababc.json rename to backend/.sqlx/query-eff41f41ffa6793baa65131258d8c9b67669db25ad3923bdb43093fd7b409736.json index af4ea862f6..058ddab114 100644 --- a/backend/.sqlx/query-6df2ca52e3e8515c398943da701600a33e64525809fdd67bce63e28d98eababc.json +++ b/backend/.sqlx/query-eff41f41ffa6793baa65131258d8c9b67669db25ad3923bdb43093fd7b409736.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT created_by AS \"created_by!\", coalesce(job_logs.logs, '') as logs\n FROM v2_job\n LEFT JOIN job_logs ON job_logs.job_id = v2_job.id\n WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 AND ($3::text[] IS NULL OR v2_job.tag = ANY($3))\n ORDER BY job_logs.log_offset DESC\n LIMIT 100", + "query": "SELECT created_by AS \"created_by!\", coalesce(job_logs.logs, '') as logs\n FROM v2_job\n LEFT JOIN job_logs ON job_logs.job_id = v2_job.id\n WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 AND ($3::text[] IS NULL OR v2_job.tag = ANY($3))\n ORDER BY job_logs.log_offset DESC\n LIMIT 100", "describe": { "columns": [ { @@ -26,5 +26,5 @@ null ] }, - "hash": "6df2ca52e3e8515c398943da701600a33e64525809fdd67bce63e28d98eababc" + "hash": "eff41f41ffa6793baa65131258d8c9b67669db25ad3923bdb43093fd7b409736" } diff --git a/backend/.sqlx/query-f027ddbf2877c6ed9be749ae1c5b852061399ada923d8206ac666745664d58e6.json b/backend/.sqlx/query-f027ddbf2877c6ed9be749ae1c5b852061399ada923d8206ac666745664d58e6.json new file mode 100644 index 0000000000..573d69cf41 --- /dev/null +++ b/backend/.sqlx/query-f027ddbf2877c6ed9be749ae1c5b852061399ada923d8206ac666745664d58e6.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n schema \n FROM \n flow \n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "schema", + "type_info": "Json" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + true + ] + }, + "hash": "f027ddbf2877c6ed9be749ae1c5b852061399ada923d8206ac666745664d58e6" +} diff --git a/backend/.sqlx/query-5e7cadffbee74b11e224b60322b102b9899b4a97b8e557692c0085a9b472b8a7.json b/backend/.sqlx/query-f0bfdcd5949cba7cb9c3ee5632ca9e0bb94fc00b72d51aa755efea09dfc582f1.json similarity index 72% rename from backend/.sqlx/query-5e7cadffbee74b11e224b60322b102b9899b4a97b8e557692c0085a9b472b8a7.json rename to backend/.sqlx/query-f0bfdcd5949cba7cb9c3ee5632ca9e0bb94fc00b72d51aa755efea09dfc582f1.json index 414ba5924d..f243a3ce1f 100644 --- a/backend/.sqlx/query-5e7cadffbee74b11e224b60322b102b9899b4a97b8e557692c0085a9b472b8a7.json +++ b/backend/.sqlx/query-f0bfdcd5949cba7cb9c3ee5632ca9e0bb94fc00b72d51aa755efea09dfc582f1.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT j.created_by AS \"created_by\", coalesce(job_logs.logs, '') as logs, COALESCE(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index\n FROM v2_job j\n LEFT JOIN job_logs ON job_logs.job_id = j.id\n WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", + "query": "SELECT j.created_by AS \"created_by\", coalesce(job_logs.logs, '') as logs, COALESCE(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index\n FROM v2_job j\n LEFT JOIN job_logs ON job_logs.job_id = j.id\n WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", "describe": { "columns": [ { @@ -38,5 +38,5 @@ true ] }, - "hash": "5e7cadffbee74b11e224b60322b102b9899b4a97b8e557692c0085a9b472b8a7" + "hash": "f0bfdcd5949cba7cb9c3ee5632ca9e0bb94fc00b72d51aa755efea09dfc582f1" } diff --git a/backend/.sqlx/query-ef8413620c6860c3bf200894c5917c9209817a9cd8eb3bcd05a74f55423054ae.json b/backend/.sqlx/query-f0fd8fccb98032139568afccc666af148d57ff3a2eb8183298ac35585501b2fc.json similarity index 68% rename from backend/.sqlx/query-ef8413620c6860c3bf200894c5917c9209817a9cd8eb3bcd05a74f55423054ae.json rename to backend/.sqlx/query-f0fd8fccb98032139568afccc666af148d57ff3a2eb8183298ac35585501b2fc.json index 58485a7cfb..59e2a9ea15 100644 --- a/backend/.sqlx/query-ef8413620c6860c3bf200894c5917c9209817a9cd8eb3bcd05a74f55423054ae.json +++ b/backend/.sqlx/query-f0fd8fccb98032139568afccc666af148d57ff3a2eb8183298ac35585501b2fc.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT created_by AS \"created_by!\", args as \"args: sqlx::types::Json>\"\n FROM v2_job\n WHERE id = $1 AND workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", + "query": "SELECT created_by AS \"created_by!\", args as \"args: sqlx::types::Json>\"\n FROM v2_job\n WHERE id = $1 AND workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", "describe": { "columns": [ { @@ -26,5 +26,5 @@ true ] }, - "hash": "ef8413620c6860c3bf200894c5917c9209817a9cd8eb3bcd05a74f55423054ae" + "hash": "f0fd8fccb98032139568afccc666af148d57ff3a2eb8183298ac35585501b2fc" } diff --git a/backend/.sqlx/query-39cd46049a6e7d21d310a4973b3b40604cbb8c5ea1d28e4f74651505c317b440.json b/backend/.sqlx/query-f2eebf58001aa792c5fbe9f24ac8290cc15d2865b6774f07a52dd9db7a7209c7.json similarity index 60% rename from backend/.sqlx/query-39cd46049a6e7d21d310a4973b3b40604cbb8c5ea1d28e4f74651505c317b440.json rename to backend/.sqlx/query-f2eebf58001aa792c5fbe9f24ac8290cc15d2865b6774f07a52dd9db7a7209c7.json index 0b80171146..8eb4477540 100644 --- a/backend/.sqlx/query-39cd46049a6e7d21d310a4973b3b40604cbb8c5ea1d28e4f74651505c317b440.json +++ b/backend/.sqlx/query-f2eebf58001aa792c5fbe9f24ac8290cc15d2865b6774f07a52dd9db7a7209c7.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n gcp_resource_path, \n script_path,\n is_flow, \n workspace_id,\n path,\n edited_by,\n email,\n delivery_config AS \"delivery_config: _\",\n retry as \"retry: _\",\n error_handler_path,\n error_handler_args as \"error_handler_args: _\"\n FROM\n gcp_trigger\n WHERE\n workspace_id = $1 AND\n path = $2 AND\n delivery_type = 'push'::DELIVERY_MODE \n ", + "query": "\n SELECT \n gcp_resource_path, \n script_path,\n is_flow,\n mode as \"mode: _\",\n workspace_id,\n path,\n edited_by,\n email,\n delivery_config AS \"delivery_config: _\",\n retry as \"retry: _\",\n error_handler_path,\n error_handler_args as \"error_handler_args: _\"\n FROM\n gcp_trigger\n WHERE\n workspace_id = $1 AND\n path = $2 AND\n delivery_type = 'push'::DELIVERY_MODE \n ", "describe": { "columns": [ { @@ -20,41 +20,57 @@ }, { "ordinal": 3, + "name": "mode: _", + "type_info": { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + } + }, + { + "ordinal": 4, "name": "workspace_id", "type_info": "Varchar" }, { - "ordinal": 4, + "ordinal": 5, "name": "path", "type_info": "Varchar" }, { - "ordinal": 5, + "ordinal": 6, "name": "edited_by", "type_info": "Varchar" }, { - "ordinal": 6, + "ordinal": 7, "name": "email", "type_info": "Varchar" }, { - "ordinal": 7, + "ordinal": 8, "name": "delivery_config: _", "type_info": "Jsonb" }, { - "ordinal": 8, + "ordinal": 9, "name": "retry: _", "type_info": "Jsonb" }, { - "ordinal": 9, + "ordinal": 10, "name": "error_handler_path", "type_info": "Varchar" }, { - "ordinal": 10, + "ordinal": 11, "name": "error_handler_args: _", "type_info": "Jsonb" } @@ -73,11 +89,12 @@ false, false, false, + false, true, true, true, true ] }, - "hash": "39cd46049a6e7d21d310a4973b3b40604cbb8c5ea1d28e4f74651505c317b440" + "hash": "f2eebf58001aa792c5fbe9f24ac8290cc15d2865b6774f07a52dd9db7a7209c7" } diff --git a/backend/.sqlx/query-f6e63a324664423353b56bccd9f3b322a3512ccfa86485e0f01664942ae04d0f.json b/backend/.sqlx/query-f6e63a324664423353b56bccd9f3b322a3512ccfa86485e0f01664942ae04d0f.json deleted file mode 100644 index 4e21dac9a9..0000000000 --- a/backend/.sqlx/query-f6e63a324664423353b56bccd9f3b322a3512ccfa86485e0f01664942ae04d0f.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH uuid_table as (\n select unnest($4::uuid[]) as uuid\n )\n INSERT INTO v2_job_queue\n (id, workspace_id, scheduled_for, tag)\n (SELECT uuid, $1, $2, $3 FROM uuid_table)\n RETURNING id", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Timestamptz", - "Varchar", - "UuidArray" - ] - }, - "nullable": [ - false - ] - }, - "hash": "f6e63a324664423353b56bccd9f3b322a3512ccfa86485e0f01664942ae04d0f" -} diff --git a/backend/.sqlx/query-49f7e7481019d27b44d5c8a167f9d5d3309a31b195b3147fa32f2ca8a6c9c90e.json b/backend/.sqlx/query-f7753f8f7b40aebf66de607203f97681d06ac04f9d547b4c49c032d9e933a29e.json similarity index 65% rename from backend/.sqlx/query-49f7e7481019d27b44d5c8a167f9d5d3309a31b195b3147fa32f2ca8a6c9c90e.json rename to backend/.sqlx/query-f7753f8f7b40aebf66de607203f97681d06ac04f9d547b4c49c032d9e933a29e.json index d57628b1aa..09ac527a72 100644 --- a/backend/.sqlx/query-49f7e7481019d27b44d5c8a167f9d5d3309a31b195b3147fa32f2ca8a6c9c90e.json +++ b/backend/.sqlx/query-f7753f8f7b40aebf66de607203f97681d06ac04f9d547b4c49c032d9e933a29e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT\n j.runnable_path as script_path, j.args AS \"args: sqlx::types::Json>>\",\n j.tag AS \"tag!\", j.priority\n FROM v2_job j\n WHERE j.id = $1 and j.workspace_id = $2", + "query": "SELECT\n j.runnable_path as script_path, j.args AS \"args: sqlx::types::Json>>\",\n j.tag AS \"tag!\", j.priority\n FROM v2_job j\n WHERE j.id = $1 and j.workspace_id = $2", "describe": { "columns": [ { @@ -37,5 +37,5 @@ true ] }, - "hash": "49f7e7481019d27b44d5c8a167f9d5d3309a31b195b3147fa32f2ca8a6c9c90e" + "hash": "f7753f8f7b40aebf66de607203f97681d06ac04f9d547b4c49c032d9e933a29e" } diff --git a/backend/.sqlx/query-79d6b757c9556cfcf0c98f52035b5f1a9036b6005764b79c415373a5d39c3211.json b/backend/.sqlx/query-f7f0c846cb9db866fd5188631f238eb69709b86976079425549d5bab8eefbac7.json similarity index 52% rename from backend/.sqlx/query-79d6b757c9556cfcf0c98f52035b5f1a9036b6005764b79c415373a5d39c3211.json rename to backend/.sqlx/query-f7f0c846cb9db866fd5188631f238eb69709b86976079425549d5bab8eefbac7.json index 7b0d64c691..ca81bf8764 100644 --- a/backend/.sqlx/query-79d6b757c9556cfcf0c98f52035b5f1a9036b6005764b79c415373a5d39c3211.json +++ b/backend/.sqlx/query-f7f0c846cb9db866fd5188631f238eb69709b86976079425549d5bab8eefbac7.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n result AS \"result: sqlx::types::Json>\",\n result_columns,\n status = 'success' AS \"success!\"\n FROM \n v2_job_completed\n WHERE \n id = $1 AND \n workspace_id = $2\n ", + "query": "\n SELECT\n result AS \"result: sqlx::types::Json>\",\n result_columns,\n status = 'success' AS \"success!\"\n FROM \n v2_job_completed\n WHERE \n id = $1 AND \n workspace_id = $2\n ", "describe": { "columns": [ { @@ -31,5 +31,5 @@ null ] }, - "hash": "79d6b757c9556cfcf0c98f52035b5f1a9036b6005764b79c415373a5d39c3211" + "hash": "f7f0c846cb9db866fd5188631f238eb69709b86976079425549d5bab8eefbac7" } diff --git a/backend/.sqlx/query-fa9c2c75b622b23008ef1cdba3cb691ef7d1b0ad9eb2596ccfa7227721a0784f.json b/backend/.sqlx/query-fa9c2c75b622b23008ef1cdba3cb691ef7d1b0ad9eb2596ccfa7227721a0784f.json index be88c36d08..7f581135ed 100644 --- a/backend/.sqlx/query-fa9c2c75b622b23008ef1cdba3cb691ef7d1b0ad9eb2596ccfa7227721a0784f.json +++ b/backend/.sqlx/query-fa9c2c75b622b23008ef1cdba3cb691ef7d1b0ad9eb2596ccfa7227721a0784f.json @@ -38,7 +38,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } @@ -101,7 +104,8 @@ "postgres", "sqs", "gcp", - "mqtt" + "mqtt", + "nextcloud" ] } } diff --git a/backend/.sqlx/query-fb942aa7894b4ae904f0233405f62f201e3f5deed593128017b886342ef6d210.json b/backend/.sqlx/query-fb942aa7894b4ae904f0233405f62f201e3f5deed593128017b886342ef6d210.json new file mode 100644 index 0000000000..942c881b38 --- /dev/null +++ b/backend/.sqlx/query-fb942aa7894b4ae904f0233405f62f201e3f5deed593128017b886342ef6d210.json @@ -0,0 +1,37 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO postgres_trigger (\n workspace_id,\n path,\n postgres_resource_path,\n replication_slot_name,\n publication_name,\n script_path,\n is_flow,\n mode,\n edited_by,\n email,\n edited_at,\n error_handler_path,\n error_handler_args,\n retry\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now(), $11, $12, $13\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "trigger_mode", + "kind": { + "Enum": [ + "enabled", + "disabled", + "suspended" + ] + } + } + }, + "Varchar", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "fb942aa7894b4ae904f0233405f62f201e3f5deed593128017b886342ef6d210" +} diff --git a/backend/.sqlx/query-fbd38c9c4f4ecba2d3e3d79433327fd4280466eec443aff4f1e8c1ab810cca7e.json b/backend/.sqlx/query-fbd38c9c4f4ecba2d3e3d79433327fd4280466eec443aff4f1e8c1ab810cca7e.json index 29cedfbc1c..d8855042a2 100644 --- a/backend/.sqlx/query-fbd38c9c4f4ecba2d3e3d79433327fd4280466eec443aff4f1e8c1ab810cca7e.json +++ b/backend/.sqlx/query-fbd38c9c4f4ecba2d3e3d79433327fd4280466eec443aff4f1e8c1ab810cca7e.json @@ -29,7 +29,10 @@ "flowscript", "flownode", "appscript", - "aiagent" + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] } } diff --git a/backend/.sqlx/query-fd55112d55995ab08d2c275aa6430cdec1cacebdf2f2b3dd6f678b434643eb50.json b/backend/.sqlx/query-fd55112d55995ab08d2c275aa6430cdec1cacebdf2f2b3dd6f678b434643eb50.json deleted file mode 100644 index b8eb1b000f..0000000000 --- a/backend/.sqlx/query-fd55112d55995ab08d2c275aa6430cdec1cacebdf2f2b3dd6f678b434643eb50.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "UPDATE global_settings SET value = jsonb_set(value, '{instance_catalog_db_status}', (COALESCE(value->'instance_catalog_db_status', '{}'::jsonb) || to_jsonb($1::json))) WHERE name = 'ducklake_settings'", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Json" - ] - }, - "nullable": [] - }, - "hash": "fd55112d55995ab08d2c275aa6430cdec1cacebdf2f2b3dd6f678b434643eb50" -} diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 2b19bb4da9..1ff8cc8af9 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -9501f6dc030f4235984286cf00762ab4bb54c846 \ No newline at end of file +c559aa1896dafc98a5000ce1a77bd28a9e087363 diff --git a/backend/migrations/20251127103823_add_unassigned_kind.down.sql b/backend/migrations/20251127103823_add_unassigned_kind.down.sql new file mode 100644 index 0000000000..d2f607c5b8 --- /dev/null +++ b/backend/migrations/20251127103823_add_unassigned_kind.down.sql @@ -0,0 +1 @@ +-- Add down migration script here diff --git a/backend/migrations/20251127103823_add_unassigned_kind.up.sql b/backend/migrations/20251127103823_add_unassigned_kind.up.sql new file mode 100644 index 0000000000..4aece96992 --- /dev/null +++ b/backend/migrations/20251127103823_add_unassigned_kind.up.sql @@ -0,0 +1,4 @@ +-- Add up migration script here +ALTER TYPE JOB_KIND ADD VALUE IF NOT EXISTS 'unassigned_script'; +ALTER TYPE JOB_KIND ADD VALUE IF NOT EXISTS 'unassigned_flow'; +ALTER TYPE JOB_KIND ADD VALUE IF NOT EXISTS 'unassigned_singlestepflow'; diff --git a/backend/migrations/20251127104100_add_suspended_mode_to_triggers.down.sql b/backend/migrations/20251127104100_add_suspended_mode_to_triggers.down.sql new file mode 100644 index 0000000000..07b38ccca8 --- /dev/null +++ b/backend/migrations/20251127104100_add_suspended_mode_to_triggers.down.sql @@ -0,0 +1,93 @@ +-- Add down migration script here +-- Revert mode column back to enabled boolean for each trigger table +ALTER TABLE email_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE email_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE email_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +ALTER TABLE http_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE http_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE http_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +ALTER TABLE websocket_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE websocket_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE websocket_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +ALTER TABLE sqs_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE sqs_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE sqs_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +ALTER TABLE postgres_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE postgres_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE postgres_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +ALTER TABLE nats_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE nats_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE nats_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +ALTER TABLE mqtt_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE mqtt_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE mqtt_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +ALTER TABLE kafka_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE kafka_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE kafka_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +ALTER TABLE gcp_trigger +ADD COLUMN enabled BOOLEAN; + +UPDATE gcp_trigger SET enabled = CASE WHEN mode = 'enabled'::TRIGGER_MODE THEN true ELSE false END; + +ALTER TABLE gcp_trigger +ALTER COLUMN enabled SET NOT NULL, +ALTER COLUMN enabled SET DEFAULT true, +DROP COLUMN mode; + +DROP TYPE IF EXISTS TRIGGER_MODE; \ No newline at end of file diff --git a/backend/migrations/20251127104100_add_suspended_mode_to_triggers.up.sql b/backend/migrations/20251127104100_add_suspended_mode_to_triggers.up.sql new file mode 100644 index 0000000000..f504b37dcf --- /dev/null +++ b/backend/migrations/20251127104100_add_suspended_mode_to_triggers.up.sql @@ -0,0 +1,94 @@ +-- Add up migration script here +CREATE TYPE TRIGGER_MODE AS ENUM ('enabled', 'disabled', 'suspended'); + +-- Rename enabled column to mode and change its type for each trigger table +ALTER TABLE gcp_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE gcp_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE gcp_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + +ALTER TABLE kafka_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE kafka_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE kafka_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + +ALTER TABLE mqtt_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE mqtt_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE mqtt_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + +ALTER TABLE nats_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE nats_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE nats_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + +ALTER TABLE postgres_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE postgres_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE postgres_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + +ALTER TABLE sqs_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE sqs_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE sqs_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + +ALTER TABLE websocket_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE websocket_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE websocket_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + +ALTER TABLE http_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE http_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE http_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + +ALTER TABLE email_trigger +ADD COLUMN mode TRIGGER_MODE; + +UPDATE email_trigger SET mode = CASE WHEN enabled = true THEN 'enabled'::TRIGGER_MODE ELSE 'disabled'::TRIGGER_MODE END; + +ALTER TABLE email_trigger +ALTER COLUMN mode SET NOT NULL, +ALTER COLUMN mode SET DEFAULT 'enabled'::TRIGGER_MODE, +DROP COLUMN enabled; + diff --git a/backend/tests/common/mod.rs b/backend/tests/common/mod.rs index 7150eaaf84..a73068691d 100644 --- a/backend/tests/common/mod.rs +++ b/backend/tests/common/mod.rs @@ -209,6 +209,7 @@ impl RunJob { None, debounce_job_id_o, None, + None, ) .await .expect("push has to succeed"); diff --git a/backend/tests/debouncing.rs b/backend/tests/debouncing.rs index 72fdc8a203..0f7c68b7b8 100644 --- a/backend/tests/debouncing.rs +++ b/backend/tests/debouncing.rs @@ -383,6 +383,7 @@ def main(): None, None, None, + None, ) .await .unwrap(); @@ -543,6 +544,7 @@ def main(): None, debounce_job_id_o, None, + None, ) .await .unwrap(); @@ -721,6 +723,8 @@ def main(): None, debounce_job_id_o, None, + None, + None, ) .await .unwrap(); @@ -1229,6 +1233,8 @@ WHERE None, None, None, + None, + None, ) .await .unwrap(); @@ -1369,6 +1375,7 @@ WHERE None, None, None, + None, ) .await .unwrap(); @@ -1800,6 +1807,7 @@ WHERE None, None, None, + None, ) .await .unwrap(); @@ -1926,6 +1934,7 @@ WHERE // None, // None, // None, + // None, // ) // .await // .unwrap(); diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 44bdee8d11..ded8a90fa2 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -8594,6 +8594,8 @@ paths: - $ref: "#/components/parameters/ScriptExactPath" - $ref: "#/components/parameters/ScriptStartPath" - $ref: "#/components/parameters/SchedulePath" + - $ref: "#/components/parameters/TriggerPath" + - $ref: "#/components/parameters/JobTriggerKind" - $ref: "#/components/parameters/ScriptExactHash" - $ref: "#/components/parameters/StartedBefore" - $ref: "#/components/parameters/StartedAfter" @@ -8866,6 +8868,94 @@ paths: items: type: string + /w/{workspace}/trigger/{trigger_kind}/resume_suspended_trigger_jobs/{trigger_path}: + post: + summary: resume all suspended jobs for a specific trigger + operationId: resumeSuspendedTriggerJobs + tags: + - trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: trigger_kind + description: The kind of trigger + in: path + required: true + schema: + $ref: "#/components/schemas/JobTriggerKind" + - name: trigger_path + description: The path of the trigger (can contain forward slashes) + in: path + required: true + schema: + type: string + style: simple + explode: false + requestBody: + description: Optional list of job IDs to reassign + required: false + content: + application/json: + schema: + type: object + properties: + job_ids: + type: array + items: + type: string + format: uuid + description: Optional list of specific job UUIDs to reassign. If not provided, all suspended jobs for the trigger will be reassigned. + responses: + "200": + description: confirmation message + content: + application/json: + schema: + type: string + + /w/{workspace}/trigger/{trigger_kind}/cancel_suspended_trigger_jobs/{trigger_path}: + post: + summary: cancel all suspended jobs for a specific trigger + operationId: cancelSuspendedTriggerJobs + tags: + - trigger + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: trigger_kind + description: The kind of trigger + in: path + required: true + schema: + $ref: "#/components/schemas/JobTriggerKind" + - name: trigger_path + description: The path of the trigger (can contain forward slashes) + in: path + required: true + schema: + type: string + style: simple + explode: false + requestBody: + description: Optional list of job IDs to cancel + required: false + content: + application/json: + schema: + type: object + properties: + job_ids: + type: array + items: + type: string + format: uuid + description: Optional list of specific job UUIDs to cancel. If not provided, all suspended jobs for the trigger will be canceled. + responses: + "200": + description: confirmation message + content: + application/json: + schema: + type: string + /w/{workspace}/jobs/completed/list: get: summary: list all completed jobs @@ -10564,10 +10654,10 @@ paths: schema: type: boolean - /w/{workspace}/http_triggers/setenabled/{path}: + /w/{workspace}/http_triggers/setmode/{path}: post: summary: enable/disable http trigger - operationId: setHttpTriggerEnabled + operationId: setHttpTriggerMode tags: - http_trigger parameters: @@ -10580,10 +10670,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: http trigger enable/disable @@ -10724,10 +10814,10 @@ paths: schema: type: boolean - /w/{workspace}/websocket_triggers/setenabled/{path}: + /w/{workspace}/websocket_triggers/setmode/{path}: post: summary: set enabled websocket trigger - operationId: setWebsocketTriggerEnabled + operationId: setWebsocketTriggerMode tags: - websocket_trigger parameters: @@ -10741,10 +10831,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: websocket trigger enabled set @@ -10918,10 +11008,10 @@ paths: schema: type: boolean - /w/{workspace}/kafka_triggers/setenabled/{path}: + /w/{workspace}/kafka_triggers/setmode/{path}: post: summary: set enabled kafka trigger - operationId: setKafkaTriggerEnabled + operationId: setKafkaTriggerMode tags: - kafka_trigger parameters: @@ -10935,10 +11025,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: kafka trigger enabled set @@ -11107,10 +11197,10 @@ paths: schema: type: boolean - /w/{workspace}/nats_triggers/setenabled/{path}: + /w/{workspace}/nats_triggers/setmode/{path}: post: summary: set enabled nats trigger - operationId: setNatsTriggerEnabled + operationId: setNatsTriggerMode tags: - nats_trigger parameters: @@ -11124,10 +11214,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: nats trigger enabled set @@ -11296,10 +11386,10 @@ paths: schema: type: boolean - /w/{workspace}/sqs_triggers/setenabled/{path}: + /w/{workspace}/sqs_triggers/setmode/{path}: post: summary: set enabled sqs trigger - operationId: setSqsTriggerEnabled + operationId: setSqsTriggerMode tags: - sqs_trigger parameters: @@ -11313,10 +11403,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: sqs trigger enabled set @@ -11485,10 +11575,10 @@ paths: schema: type: boolean - /w/{workspace}/mqtt_triggers/setenabled/{path}: + /w/{workspace}/mqtt_triggers/setmode/{path}: post: summary: set enabled mqtt trigger - operationId: setMqttTriggerEnabled + operationId: setMqttTriggerMode tags: - mqtt_trigger parameters: @@ -11502,10 +11592,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: mqtt trigger enabled set @@ -11674,10 +11764,10 @@ paths: schema: type: boolean - /w/{workspace}/gcp_triggers/setenabled/{path}: + /w/{workspace}/gcp_triggers/setmode/{path}: post: summary: set enabled gcp trigger - operationId: setGcpTriggerEnabled + operationId: setGcpTriggerMode tags: - gcp_trigger parameters: @@ -11691,10 +11781,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: gcp trigger enabled set @@ -12178,10 +12268,10 @@ paths: schema: type: boolean - /w/{workspace}/postgres_triggers/setenabled/{path}: + /w/{workspace}/postgres_triggers/setmode/{path}: post: summary: set enabled postgres trigger - operationId: setPostgresTriggerEnabled + operationId: setPostgresTriggerMode tags: - postgres_trigger parameters: @@ -12195,10 +12285,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: postgres trigger enabled set @@ -12399,10 +12489,10 @@ paths: application/json: schema: type: boolean - /w/{workspace}/email_triggers/setenabled/{path}: + /w/{workspace}/email_triggers/setmode/{path}: post: summary: enable/disable email trigger - operationId: setEmailTriggerEnabled + operationId: setEmailTriggerMode tags: - email_trigger parameters: @@ -12415,10 +12505,10 @@ paths: schema: type: object properties: - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - - enabled + - mode responses: "200": description: email trigger enable/disable @@ -15560,22 +15650,7 @@ components: description: trigger kind (schedule, http, websocket...) in: query schema: - type: string - enum: - - webhook - - default_email - - email - - schedule - - http - - websocket - - postgres - - kafka - - nats - - mqtt - - sqs - - gcp - - poll - - cli + $ref: "#/components/schemas/JobTriggerKind" OrderDesc: name: order_desc description: order by desc order (default true) @@ -15672,6 +15747,12 @@ components: in: query schema: type: string + TriggerPath: + name: trigger_path + description: mask to filter by trigger path + in: query + schema: + type: string ScriptExactPath: name: script_path_exact description: mask to filter exact matching path @@ -16538,6 +16619,9 @@ components: "flownode", "appscript", "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] schedule_path: type: string @@ -16651,6 +16735,9 @@ components: "flownode", "appscript", "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] schedule_path: type: string @@ -16771,6 +16858,10 @@ components: "flowscript", "flownode", "appscript", + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] trigger: type: string @@ -16915,6 +17006,10 @@ components: "flowscript", "flownode", "appscript", + "aiagent", + "unassigned_script", + "unassigned_flow", + "unassigned_singlestepflow" ] trigger: type: string @@ -18023,6 +18118,31 @@ components: - timezone - args + JobTriggerKind: + description: job trigger kind (schedule, http, websocket...) + type: string + enum: + - webhook + - default_email + - email + - schedule + - http + - websocket + - postgres + - kafka + - nats + - mqtt + - sqs + - gcp + + TriggerMode: + description: job trigger mode + type: string + enum: + - enabled + - disabled + - suspended + TriggerExtraProperty: type: object properties: @@ -18045,8 +18165,8 @@ components: format: date-time is_flow: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" required: - path - script_path @@ -18056,7 +18176,7 @@ components: - edited_by - edited_at - is_flow - - enabled + - mode AuthenticationMethod: type: string @@ -18282,8 +18402,8 @@ components: type: boolean wrap_body: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" raw_string: type: boolean error_handler_path: @@ -18454,8 +18574,8 @@ components: type: boolean url: type: string - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" filters: type: array items: @@ -18660,8 +18780,8 @@ components: type: string is_flow: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" error_handler_path: type: string error_handler_args: @@ -18698,8 +18818,8 @@ components: type: string is_flow: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" error_handler_path: type: string error_handler_args: @@ -18798,8 +18918,8 @@ components: type: string is_flow: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" auto_acknowledge_msg: type: boolean ack_deadline: @@ -18946,8 +19066,8 @@ components: type: string is_flow: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" error_handler_path: type: string error_handler_args: @@ -18981,8 +19101,8 @@ components: type: string is_flow: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" error_handler_path: type: string error_handler_args: @@ -19116,8 +19236,8 @@ components: type: string is_flow: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" postgres_resource_path: type: string publication: @@ -19148,8 +19268,8 @@ components: type: string is_flow: type: boolean - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" postgres_resource_path: type: string publication: @@ -19218,8 +19338,8 @@ components: type: array items: type: string - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" error_handler_path: type: string error_handler_args: @@ -19324,8 +19444,8 @@ components: type: array items: type: string - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" error_handler_path: type: string error_handler_args: @@ -19368,7 +19488,6 @@ components: $ref: "#/components/schemas/ScriptArgs" retry: $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" - required: - path - script_path @@ -19415,8 +19534,9 @@ components: $ref: "#/components/schemas/ScriptArgs" retry: $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" - enabled: - type: boolean + mode: + $ref: "#/components/schemas/TriggerMode" + required: - path - script_path diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 08aca365f2..70f0ea5788 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -1247,6 +1247,7 @@ async fn create_app_internal<'a>( None, None, None, + None, ) .await?; tracing::info!("Pushed app dependency job {}", dependency_job_uuid); @@ -1637,6 +1638,7 @@ async fn update_app_internal<'a>( None, None, None, + None, ) .await?; tracing::info!("Pushed app dependency job {}", dependency_job_uuid); @@ -1965,6 +1967,7 @@ async fn execute_component( end_user_email, None, None, + None, ) .await?; diff --git a/backend/windmill-api/src/concurrency_groups.rs b/backend/windmill-api/src/concurrency_groups.rs index 4a96a16e0b..e9c06de4f1 100644 --- a/backend/windmill-api/src/concurrency_groups.rs +++ b/backend/windmill-api/src/concurrency_groups.rs @@ -203,6 +203,7 @@ async fn get_concurrent_intervals( has_null_parent: None, worker: None, label: None, + trigger_path: None, scheduled_for_before_now: _, is_not_schedule: _, started_before: _, diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index d64292765f..bdc322f43e 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -565,6 +565,7 @@ async fn create_flow( None, None, None, + None, ) .await?; @@ -1110,6 +1111,7 @@ async fn update_flow( None, None, None, + None, ) .await?; diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index f3a0cc7e11..c7ba8cb2c9 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -40,6 +40,7 @@ use windmill_common::jobs::{ JobTriggerKind, RunInlinePreviewScriptFnParams, ENTRYPOINT_OVERRIDE, }; use windmill_common::s3_helpers::{upload_artifact_to_store, BundleFormat}; +use windmill_common::triggers::TriggerMetadata; use windmill_common::utils::{RunnableKind, WarnAfterExt}; use windmill_common::worker::{Connection, CLOUD_HOSTED, TMP_DIR}; use windmill_common::workspace_dependencies::{ @@ -434,39 +435,39 @@ async fn get_flow_env_by_flow_job_id( Query(JsonPath { json_path, .. }): Query, ) -> windmill_common::error::JsonResult> { let flow_env = sqlx::query_scalar!( - r#" - SELECT - CASE - WHEN flow_version.id IS NOT NULL THEN - (flow_version.value -> 'flow_env' -> $3) #> $4 - ELSE - (root_job.raw_flow -> 'flow_env' -> $3) #> $4 - END AS "flow_env: sqlx::types::Json>" - FROM - v2_job current_job - JOIN - v2_job root_job ON root_job.id = COALESCE(current_job.root_job, current_job.flow_innermost_root_job, current_job.parent_job, current_job.id) - AND root_job.workspace_id = current_job.workspace_id - LEFT JOIN - flow_version ON flow_version.id = root_job.runnable_id - AND flow_version.path = root_job.runnable_path - AND flow_version.workspace_id = root_job.workspace_id - WHERE - current_job.id = $1 AND - current_job.workspace_id = $2"#, - flow_job_id, - w_id, - var_name, - json_path - .as_ref() - .map(|x| x.split(".").collect::>()) - .unwrap_or_default() as Vec<&str>, - ) - .fetch_optional(&db) - .await? - .map(|r| r.map(|x| x.0)) - .flatten() - .unwrap_or_else(|| to_raw_value(&serde_json::Value::Null)); + r#" + SELECT + CASE + WHEN flow_version.id IS NOT NULL THEN + (flow_version.value -> 'flow_env' -> $3) #> $4 + ELSE + (root_job.raw_flow -> 'flow_env' -> $3) #> $4 + END AS "flow_env: sqlx::types::Json>" + FROM + v2_job current_job + JOIN + v2_job root_job ON root_job.id = COALESCE(current_job.root_job, current_job.flow_innermost_root_job, current_job.parent_job, current_job.id) + AND root_job.workspace_id = current_job.workspace_id + LEFT JOIN + flow_version ON flow_version.id = root_job.runnable_id + AND flow_version.path = root_job.runnable_path + AND flow_version.workspace_id = root_job.workspace_id + WHERE + current_job.id = $1 AND + current_job.workspace_id = $2"#, + flow_job_id, + w_id, + var_name, + json_path + .as_ref() + .map(|x| x.split(".").collect::>()) + .unwrap_or_default() as Vec<&str>, + ) + .fetch_optional(&db) + .await? + .map(|r| r.map(|x| x.0)) + .flatten() + .unwrap_or_else(|| to_raw_value(&serde_json::Value::Null)); log_job_view( &db, @@ -481,12 +482,12 @@ async fn get_flow_env_by_flow_job_id( async fn compute_root_job_for_flow(db: &DB, w_id: &str, job_id: Uuid) -> error::Result { let root_job = sqlx::query_scalar!( - r#"SELECT COALESCE(root_job, flow_innermost_root_job, parent_job, id) as "root_job!" FROM v2_job WHERE id = $1 AND workspace_id = $2"#, - job_id, - w_id - ) - .fetch_one(db) - .await?; + r#"SELECT COALESCE(root_job, flow_innermost_root_job, parent_job, id) as "root_job!" FROM v2_job WHERE id = $1 AND workspace_id = $2"#, + job_id, + w_id + ) + .fetch_one(db) + .await?; Ok(root_job.to_string()) } @@ -689,10 +690,10 @@ async fn get_queue_position( // Count jobs that are scheduled before this job and are not suspended let count: i64 = sqlx::query_scalar!( "SELECT COUNT(*) as count - FROM v2_job_queue - WHERE scheduled_for < to_timestamp($1::bigint / 1000.0) - AND running = false - AND suspend_until IS NULL", + FROM v2_job_queue + WHERE scheduled_for < to_timestamp($1::bigint / 1000.0) + AND running = false + AND suspend_until IS NULL", scheduled_for, ) .fetch_one(&db) @@ -800,33 +801,33 @@ async fn list_selected_job_groups( let mut tx = user_db.begin(&authed).await?; let results = sqlx::query_scalar!( - r#"SELECT jsonb_build_object( - 'kind', jb.kind, - 'script_path', jb.runnable_path, - 'latest_schema', COALESCE( - (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC), - (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow') - ), - 'schemas', ARRAY( - SELECT jsonb_build_object( - 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'), - 'job_ids', ARRAY_AGG(DISTINCT j.id), - 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1] - ) FROM v2_job j - LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script' - LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow' - WHERE j.id = ANY(ARRAY_AGG(jb.id)) - GROUP BY COALESCE(s.hash, f.id) - ) - ) FROM v2_job jb - WHERE (jb.kind = 'flow' OR jb.kind = 'script') - AND jb.workspace_id = $1 AND jb.id = ANY($2) - GROUP BY jb.kind, jb.runnable_path"#, - &w_id, - &uuids - ) - .fetch_all(&mut *tx) - .await?; + r#"SELECT jsonb_build_object( + 'kind', jb.kind, + 'script_path', jb.runnable_path, + 'latest_schema', COALESCE( + (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.workspace_id = $1 AND s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC), + (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.workspace_id = $1 AND flow.path = jb.runnable_path AND jb.kind = 'flow') + ), + 'schemas', ARRAY( + SELECT jsonb_build_object( + 'script_hash', LPAD(TO_HEX(COALESCE(s.hash, f.id)), 16, '0'), + 'job_ids', ARRAY_AGG(DISTINCT j.id), + 'schema', (ARRAY_AGG(COALESCE(s.schema, f.schema)))[1] + ) FROM v2_job j + LEFT JOIN script s ON s.hash = j.runnable_id AND j.kind = 'script' + LEFT JOIN flow_version f ON f.id = j.runnable_id AND j.kind = 'flow' + WHERE j.id = ANY(ARRAY_AGG(jb.id)) + GROUP BY COALESCE(s.hash, f.id) + ) + ) FROM v2_job jb + WHERE (jb.kind = 'flow' OR jb.kind = 'script') + AND jb.workspace_id = $1 AND jb.id = ANY($2) + GROUP BY jb.kind, jb.runnable_path"#, + &w_id, + &uuids + ) + .fetch_all(&mut *tx) + .await?; tx.commit().await?; @@ -1199,13 +1200,13 @@ async fn send_workspace_trigger_failure_email_notification( let trigger_info = if trigger_kind.is_some() && trigger_path.is_some() { format!( r#" -
- Trigger path: {} -
+
+ Trigger path: {} +
-
- Trigger Type: {} -
"#, +
+ Trigger Type: {} +
"#, trigger_path.unwrap(), trigger_kind_str ) @@ -1221,51 +1222,51 @@ async fn send_workspace_trigger_failure_email_notification( let content = format!( r#" - - - - - - - -

{}

+ + + + + + + +

{}

-
- Workspace: {} -
-{} -
- Script/Flow Path: {} -
+
+ Workspace: {} +
+ {} +
+ Script/Flow Path: {} +
-
- Job ID: {} -
+
+ Job ID: {} +
-
- Error Details: -
{}
-
+
+ Error Details: +
{}
+
- View Job Details - - -"#, + View Job Details + + + "#, email_title, w_id, trigger_info, runnable_path, &job_id, error_details, job_url ); @@ -1279,11 +1280,11 @@ async fn send_workspace_trigger_failure_email_notification( .await { let err_msg = format!( - "Failed to send workspace email notification for trigger failure (trigger kind: {}, job ID: {}): {}", - trigger_kind_str, - &job_id, - e - ); + "Failed to send workspace email notification for trigger failure (trigger kind: {}, job ID: {}): {}", + trigger_kind_str, + &job_id, + e + ); return Err(Error::internal_err(err_msg)); } let success_msg = format!( @@ -1317,8 +1318,8 @@ async fn send_email_with_instance_smtp( if *CLOUD_HOSTED { tracing::warn!( - "Workspace trigger failure email notification is not available for cloud hosted Windmill", - ); + "Workspace trigger failure email notification is not available for cloud hosted Windmill", + ); return Err(anyhow::anyhow!("Feature not supported in cloud hosted windmill").into()); } @@ -1461,18 +1462,18 @@ async fn get_completed_job_logs_tail( .flatten(); let record = sqlx::query!( - "SELECT created_by AS \"created_by!\", coalesce(job_logs.logs, '') as logs - FROM v2_job - LEFT JOIN job_logs ON job_logs.job_id = v2_job.id - WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 AND ($3::text[] IS NULL OR v2_job.tag = ANY($3)) - ORDER BY job_logs.log_offset DESC - LIMIT 100", - id, - w_id, - tags.as_ref().map(|v| v.as_slice()) - ) - .fetch_optional(&db) - .await?; + "SELECT created_by AS \"created_by!\", coalesce(job_logs.logs, '') as logs + FROM v2_job + LEFT JOIN job_logs ON job_logs.job_id = v2_job.id + WHERE v2_job.id = $1 AND v2_job.workspace_id = $2 AND ($3::text[] IS NULL OR v2_job.tag = ANY($3)) + ORDER BY job_logs.log_offset DESC + LIMIT 100", + id, + w_id, + tags.as_ref().map(|v| v.as_slice()) + ) + .fetch_optional(&db) + .await?; if let Some(record) = record { if opt_authed.is_none() && record.created_by != "anonymous" { @@ -1514,16 +1515,16 @@ async fn get_job_logs( .flatten(); let record = sqlx::query!( - "SELECT j.created_by AS \"created_by\", coalesce(job_logs.logs, '') as logs, COALESCE(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index - FROM v2_job j - LEFT JOIN job_logs ON job_logs.job_id = j.id - WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", - id, - w_id, - tags.as_ref().map(|v| v.as_slice()) - ) - .fetch_optional(&db) - .await?; + "SELECT j.created_by AS \"created_by\", coalesce(job_logs.logs, '') as logs, COALESCE(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index + FROM v2_job j + LEFT JOIN job_logs ON job_logs.job_id = j.id + WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", + id, + w_id, + tags.as_ref().map(|v| v.as_slice()) + ) + .fetch_optional(&db) + .await?; if let Some(record) = record { if opt_authed.is_none() && record.created_by != "anonymous" { @@ -1572,16 +1573,16 @@ async fn get_job_logs( Ok(content_plain(Body::from(logs))) } else { let text = sqlx::query!( - "SELECT j.created_by AS \"created_by!\", CONCAT(coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index - FROM v2_job j - LEFT JOIN job_logs ON job_logs.job_id = j.id - WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", - id, - w_id, - tags.as_ref().map(|v| v.as_slice()) - ) - .fetch_optional(&db) - .await?; + "SELECT j.created_by AS \"created_by!\", CONCAT(coalesce(job_logs.logs, '')) as logs, coalesce(job_logs.log_offset, 0) as log_offset, job_logs.log_file_index + FROM v2_job j + LEFT JOIN job_logs ON job_logs.job_id = j.id + WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", + id, + w_id, + tags.as_ref().map(|v| v.as_slice()) + ) + .fetch_optional(&db) + .await?; let text = not_found_if_none(text, "Job Logs", id.to_string())?; if opt_authed.is_none() && text.created_by != "anonymous" { @@ -1631,15 +1632,15 @@ async fn get_args( .map(|authed| get_scope_tags(authed)) .flatten(); let record = sqlx::query!( - "SELECT j.created_by AS \"created_by!\", j.args as \"args: sqlx::types::Json>\" - FROM v2_job j - WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", - id, - &w_id, - tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, - ) - .fetch_optional(&db) - .await?; + "SELECT j.created_by AS \"created_by!\", j.args as \"args: sqlx::types::Json>\" + FROM v2_job j + WHERE j.id = $1 AND j.workspace_id = $2 AND ($3::text[] IS NULL OR j.tag = ANY($3))", + id, + &w_id, + tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, + ) + .fetch_optional(&db) + .await?; if let Some(record) = record { if opt_authed.is_none() && record.created_by != "anonymous" { @@ -1660,15 +1661,15 @@ async fn get_args( Ok(Json(record.args.map(|x| x.0).unwrap_or_default())) } else { let record = sqlx::query!( - "SELECT created_by AS \"created_by!\", args as \"args: sqlx::types::Json>\" - FROM v2_job - WHERE id = $1 AND workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", - id, - &w_id, - tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, - ) - .fetch_optional(&db) - .await?; + "SELECT created_by AS \"created_by!\", args as \"args: sqlx::types::Json>\" + FROM v2_job + WHERE id = $1 AND workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", + id, + &w_id, + tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, + ) + .fetch_optional(&db) + .await?; let record = not_found_if_none(record, "Job Args", id.to_string())?; if opt_authed.is_none() && record.created_by != "anonymous" { return Err(Error::BadRequest( @@ -1779,6 +1780,7 @@ pub struct RunJobQuery { pub skip_preprocessor: Option, pub poll_delay_ms: Option, pub memory_id: Option, + pub suspended_mode: Option, } impl RunJobQuery { @@ -1845,6 +1847,7 @@ pub struct ListQueueQuery { pub concurrency_key: Option, pub allow_wildcards: Option, pub trigger_kind: Option, + pub trigger_path: Option, } impl From for ListQueueQuery { @@ -1877,6 +1880,7 @@ impl From for ListQueueQuery { concurrency_key: lcq.concurrency_key, allow_wildcards: lcq.allow_wildcards, trigger_kind: lcq.trigger_kind, + trigger_path: lcq.trigger_path, } } } @@ -2004,6 +2008,10 @@ pub fn filter_list_queue_query( sqlb.and_where_eq("trigger_kind", "?".bind(&format!("{}", tk))); } + if let Some(tp) = &lq.trigger_path { + sqlb.and_where_eq("trigger", "?".bind(tp)); + } + sqlb } @@ -2099,7 +2107,7 @@ async fn list_queue_jobs( Ok(Json(jobs)) } -async fn cancel_jobs( +pub async fn cancel_jobs( jobs: Vec, db: &DB, username: &str, @@ -2212,12 +2220,12 @@ async fn cancel_selection( let mut tx = user_db.begin(&authed).await?; let tags = get_scope_tags(&authed).map(|v| v.iter().map(|s| s.to_string()).collect_vec()); let jobs_to_cancel = sqlx::query_scalar!( - "SELECT j.id AS \"id!\" FROM v2_job j LEFT JOIN v2_job_queue q USING (id) WHERE j.id = ANY($1) AND j.trigger_kind IS DISTINCT FROM 'schedule'::job_trigger_kind AND ($2::text[] IS NULL OR j.tag = ANY($2))", - &jobs, - tags.as_ref().map(|v| v.as_slice()) - ) - .fetch_all(&mut *tx) - .await?; + "SELECT j.id AS \"id!\" FROM v2_job j LEFT JOIN v2_job_queue q USING (id) WHERE j.id = ANY($1) AND j.trigger_kind IS DISTINCT FROM 'schedule'::job_trigger_kind AND ($2::text[] IS NULL OR j.tag = ANY($2))", + &jobs, + tags.as_ref().map(|v| v.as_slice()) + ) + .fetch_all(&mut *tx) + .await?; tx.commit().await?; cancel_jobs( @@ -2314,16 +2322,16 @@ async fn count_queue_jobs( .tags .map(|t| t.split(',').map(|s| s.to_string()).collect::>()); Ok(Json( - sqlx::query_as!( - QueueStats, - "SELECT coalesce(COUNT(*) FILTER(WHERE q.suspend = 0 AND q.running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE q.suspend > 0), 0) as \"suspended!\" FROM v2_job_queue q JOIN v2_job j USING (id) WHERE (j.workspace_id = $1 OR $2) AND q.scheduled_for <= now() AND ($3::text[] IS NULL OR j.tag = ANY($3))", - w_id, - w_id == "admins" && cq.all_workspaces.unwrap_or(false), - tags.as_ref().map(|v| v.as_slice()) - ) - .fetch_one(&db) - .await?, - )) + sqlx::query_as!( + QueueStats, + "SELECT coalesce(COUNT(*) FILTER(WHERE q.suspend = 0 AND q.running = false), 0) as \"database_length!\", coalesce(COUNT(*) FILTER(WHERE q.suspend > 0), 0) as \"suspended!\" FROM v2_job_queue q JOIN v2_job j USING (id) WHERE (j.workspace_id = $1 OR $2) AND q.scheduled_for <= now() AND ($3::text[] IS NULL OR j.tag = ANY($3))", + w_id, + w_id == "admins" && cq.all_workspaces.unwrap_or(false), + tags.as_ref().map(|v| v.as_slice()) + ) + .fetch_one(&db) + .await?, + )) } #[derive(Deserialize)] @@ -2384,14 +2392,14 @@ async fn count_completed_jobs( Path(w_id): Path, ) -> error::JsonResult { Ok(Json( - sqlx::query_as!( - QueueStats, - "SELECT coalesce(COUNT(*), 0) as \"database_length!\", null::bigint as suspended FROM v2_job_completed WHERE workspace_id = $1", - w_id - ) - .fetch_one(&db) - .await?, - )) + sqlx::query_as!( + QueueStats, + "SELECT coalesce(COUNT(*), 0) as \"database_length!\", null::bigint as suspended FROM v2_job_completed WHERE workspace_id = $1", + w_id + ) + .fetch_one(&db) + .await?, + )) } async fn list_jobs( @@ -2452,13 +2460,13 @@ async fn list_jobs( if sqlc.is_none() { return Err(error::Error::BadRequest( "cannot specify success, label, created_or_started_before, or starte - d_before with running" + d_before with running" .to_string(), )); } sqlc.unwrap().limit(per_page).offset(offset).query()? }; - // tracing::info!("sql: {}", sql); + // tracing::info!("sql: {}", &sql); let mut tx: Transaction<'_, Postgres> = user_db.begin(&authed).await?; let jobs: Vec = sqlx::query_as(&sql) @@ -2560,8 +2568,8 @@ async fn resume_suspended_job_internal( let exists = sqlx::query_scalar!( r#" - SELECT EXISTS (SELECT 1 FROM resume_job WHERE id = $1) - "#, + SELECT EXISTS (SELECT 1 FROM resume_job WHERE id = $1) + "#, Uuid::from_u128(job_id.as_u128() ^ resume_id as u128), ) .fetch_one(&db) @@ -2708,10 +2716,10 @@ async fn insert_resume_job<'c>( ) -> error::Result<()> { sqlx::query!( r#" - INSERT INTO resume_job - (id, resume_id, job, flow, value, approver, approved) - VALUES ($1, $2, $3, $4, $5, $6, $7) - "#, + INSERT INTO resume_job + (id, resume_id, job, flow, value, approver, approved) + VALUES ($1, $2, $3, $4, $5, $6, $7) + "#, Uuid::from_u128(job_id.as_u128() ^ resume_id as u128), resume_id as i32, job_id, @@ -2737,13 +2745,13 @@ async fn get_suspended_parent_flow_info(job_id: Uuid, db: &DB) -> error::Result< let flow = sqlx::query_as!( FlowInfo, r#" - SELECT q.id, f.flow_status, q.suspend, j.runnable_path AS script_path - FROM v2_job_queue q - JOIN v2_job j USING (id) - JOIN v2_job_status f USING (id) - WHERE id = ( SELECT parent_job FROM v2_job WHERE id = $1 ) - FOR UPDATE - "#, + SELECT q.id, f.flow_status, q.suspend, j.runnable_path AS script_path + FROM v2_job_queue q + JOIN v2_job j USING (id) + JOIN v2_job_status f USING (id) + WHERE id = ( SELECT parent_job FROM v2_job WHERE id = $1 ) + FOR UPDATE + "#, job_id, ) .fetch_optional(db) @@ -2757,17 +2765,17 @@ async fn get_suspended_flow_info<'c>( tx: &mut Transaction<'c, Postgres>, ) -> error::Result<(FlowInfo, Uuid)> { let flow = sqlx::query_as!( - FlowInfo, - r#" - SELECT j.id AS "id!", COALESCE(s.flow_status, s.workflow_as_code_status) as flow_status, q.suspend AS "suspend!", j.runnable_path as script_path - FROM v2_job_queue q JOIN v2_job j USING (id) LEFT JOIN v2_job_status s USING (id) - WHERE j.id = $1 - "#, - job_id, - ) - .fetch_optional(&mut **tx) - .await? - .ok_or_else(|| anyhow::anyhow!("parent flow job not found"))?; + FlowInfo, + r#" + SELECT j.id AS "id!", COALESCE(s.flow_status, s.workflow_as_code_status) as flow_status, q.suspend AS "suspend!", j.runnable_path as script_path + FROM v2_job_queue q JOIN v2_job j USING (id) LEFT JOIN v2_job_status s USING (id) + WHERE j.id = $1 + "#, + job_id, + ) + .fetch_optional(&mut **tx) + .await? + .ok_or_else(|| anyhow::anyhow!("parent flow job not found"))?; let job_id = flow .flow_status .as_ref() @@ -2829,10 +2837,10 @@ pub async fn get_suspended_job_flow( let flow_id = sqlx::query_scalar!( r#" - SELECT parent_job - FROM v2_job - WHERE id = $1 AND workspace_id = $2 - "#, + SELECT parent_job + FROM v2_job + WHERE id = $1 AND workspace_id = $2 + "#, job, w_id ) @@ -2869,10 +2877,10 @@ pub async fn get_suspended_job_flow( let approvers = if approvers_from_status.is_empty() { sqlx::query!( r#" - SELECT resume_id, approver - FROM resume_job - WHERE job = $1 - "#, + SELECT resume_id, approver + FROM resume_job + WHERE job = $1 + "#, job, ) .fetch_all(&db) @@ -2944,9 +2952,9 @@ fn conditionally_require_authed_user( } } let error_msg = format!( - "Only users from one of the following groups are allowed to approve this workflow: {}", - approval_conditions.user_groups_required.join(", ") - ); + "Only users from one of the following groups are allowed to approve this workflow: {}", + approval_conditions.user_groups_required.join(", ") + ); return Err(Error::PermissionDenied(error_msg)); } } @@ -2975,10 +2983,10 @@ pub async fn get_flow_user_state( let mut tx = user_db.begin(&authed).await?; let r = sqlx::query_scalar!( r#" - SELECT COALESCE(s.flow_status, s.workflow_as_code_status)->'user_states'->$1 - FROM v2_job_queue q LEFT JOIN v2_job_status s USING (id) - WHERE q.id = $2 AND q.workspace_id = $3 - "#, + SELECT COALESCE(s.flow_status, s.workflow_as_code_status)->'user_states'->$1 + FROM v2_job_queue q LEFT JOIN v2_job_status s USING (id) + WHERE q.id = $2 AND q.workspace_id = $3 + "#, key, job_id, w_id @@ -2997,19 +3005,19 @@ pub async fn set_flow_user_state( ) -> error::Result { let mut tx = user_db.begin(&authed).await?; let r = sqlx::query_scalar!( - r#" - UPDATE v2_job_status f SET flow_status = JSONB_SET(flow_status, ARRAY['user_states'], JSONB_SET(COALESCE(flow_status->'user_states', '{}'::jsonb), ARRAY[$1], $2)) - FROM v2_job j - WHERE f.id = $3 AND f.id = j.id AND j.workspace_id = $4 AND kind IN ('flow', 'flowpreview', 'flownode') RETURNING 1 - "#, - key, - value, - job_id, - w_id - ) - .fetch_optional(&mut *tx) - .await? - .flatten(); + r#" + UPDATE v2_job_status f SET flow_status = JSONB_SET(flow_status, ARRAY['user_states'], JSONB_SET(COALESCE(flow_status->'user_states', '{}'::jsonb), ARRAY[$1], $2)) + FROM v2_job j + WHERE f.id = $3 AND f.id = j.id AND j.workspace_id = $4 AND kind IN ('flow', 'flowpreview', 'flownode') RETURNING 1 + "#, + key, + value, + job_id, + w_id + ) + .fetch_optional(&mut *tx) + .await? + .flatten(); if r.is_none() { return Err(Error::NotFound("Flow job not found".to_string())); } @@ -3303,11 +3311,11 @@ impl Job { db: &Pool, ) -> Result<(), sqlx::Error> { let r = sqlx::query!( - "SELECT self_wait_time_ms, aggregate_wait_time_ms FROM outstanding_wait_time WHERE job_id = $1", - self.id() - ) - .fetch_optional(db) - .await?; + "SELECT self_wait_time_ms, aggregate_wait_time_ms FROM outstanding_wait_time WHERE job_id = $1", + self.id() + ) + .fetch_optional(db) + .await?; let (self_wait_time, aggregate_wait_time) = r .map(|x| (x.self_wait_time_ms, x.aggregate_wait_time_ms)) @@ -3808,27 +3816,27 @@ fn batch_rerun_jobs_inner( let (tx, rx) = tokio::sync::mpsc::channel(10); tokio::spawn(async move { let mut job_stream = sqlx::query_as!( - BatchReRunQueryReturnType, - r#"SELECT - j.id, - j.kind AS "kind: _", - COALESCE(s.path, f.path) AS "script_path!", - COALESCE(s.hash, f.id) AS "script_hash!: _", - COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS "scheduled_for!: _", - args AS input, - COALESCE(s.schema, f.schema) AS "schema: _" - FROM v2_job j - LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script' - LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow' - LEFT JOIN v2_job_completed jc ON jc.id = j.id - LEFT JOIN v2_job_queue jq ON jq.id = j.id - WHERE j.id = ANY($1) - AND j.workspace_id = $2 - AND COALESCE(s.hash, f.id) IS NOT NULL - AND COALESCE(s.path, f.path) IS NOT NULL"#, - &body.job_ids, - w_id - ).fetch(&db); + BatchReRunQueryReturnType, + r#"SELECT + j.id, + j.kind AS "kind: _", + COALESCE(s.path, f.path) AS "script_path!", + COALESCE(s.hash, f.id) AS "script_hash!: _", + COALESCE(jc.started_at, jq.scheduled_for, make_date(1970, 1, 1)) AS "scheduled_for!: _", + args AS input, + COALESCE(s.schema, f.schema) AS "schema: _" + FROM v2_job j + LEFT JOIN script s ON j.runnable_id = s.hash AND j.kind = 'script' + LEFT JOIN flow_version f ON j.runnable_id = f.id AND j.runnable_path = f.path AND j.kind = 'flow' + LEFT JOIN v2_job_completed jc ON jc.id = j.id + LEFT JOIN v2_job_queue jq ON jq.id = j.id + WHERE j.id = ANY($1) + AND j.workspace_id = $2 + AND COALESCE(s.hash, f.id) IS NOT NULL + AND COALESCE(s.path, f.path) IS NOT NULL"#, + &body.job_ids, + w_id + ).fetch(&db); while let Some(Ok(job)) = job_stream.next().await { let job_result = batch_rerun_handle_job(&job, &authed, &db, &user_db, &w_id, &body).await; @@ -3873,15 +3881,15 @@ async fn batch_rerun_handle_job( let latest_schema; let schema = if use_latest_version { latest_schema = sqlx::query_scalar!( - r#"SELECT COALESCE( - (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC), - (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow') - ) FROM v2_job jb - WHERE jb.id = $1 AND jb.workspace_id = $2 - GROUP BY jb.kind, jb.runnable_path"#, - &job.id, - &w_id - ).fetch_optional(db).await?.flatten(); + r#"SELECT COALESCE( + (SELECT DISTINCT ON (s.path) s.schema FROM script s WHERE s.path = jb.runnable_path AND jb.kind = 'script' ORDER BY s.path, s.created_at DESC), + (SELECT flow_version.schema FROM flow LEFT JOIN flow_version ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] WHERE flow.path = jb.runnable_path AND jb.kind = 'flow') + ) FROM v2_job jb + WHERE jb.id = $1 AND jb.workspace_id = $2 + GROUP BY jb.kind, jb.runnable_path"#, + &job.id, + &w_id + ).fetch_optional(db).await?.flatten(); latest_schema.as_ref() } else { job.schema.as_ref() @@ -3921,9 +3929,10 @@ async fn batch_rerun_handle_job( // Call appropriate function to push job to queue match job.kind { JobKind::Flow => { - let result = run_flow_by_path_inner( + let result = push_flow_job_by_path_into_queue( authed.clone(), db.clone(), + None, user_db.clone(), w_id.clone(), StripPath(job.script_path.clone()), @@ -3932,15 +3941,16 @@ async fn batch_rerun_handle_job( None, ) .await; - if let Ok((uuid, _)) = result { + if let Ok((uuid, _, _)) = result { return Ok(uuid.to_string()); } } JobKind::Script => { let result = if use_latest_version { - run_script_by_path_inner( + push_script_job_by_path_into_queue( authed.clone(), db.clone(), + None, user_db.clone(), w_id.clone(), StripPath(job.script_path.clone()), @@ -3949,6 +3959,7 @@ async fn batch_rerun_handle_job( None, ) .await + .map(|r| r.0) } else { run_job_by_hash_inner( authed.clone(), @@ -3958,10 +3969,12 @@ async fn batch_rerun_handle_job( job.script_hash, RunJobQuery { skip_preprocessor: Some(true), ..Default::default() }, PushArgsOwned { extra: None, args }, + None, ) .await + .map(|r| r.0) }; - if let Ok((uuid, _)) = result { + if let Ok(uuid) = result { return Ok(uuid.to_string()); } } @@ -4079,23 +4092,30 @@ pub async fn run_flow_by_path( ) .await?; - let (uuid, _) = - run_flow_by_path_inner(authed, db, user_db, w_id, flow_path, run_query, args, None).await?; + let (uuid, _, _) = push_flow_job_by_path_into_queue( + authed, db, None, user_db, w_id, flow_path, run_query, args, None, + ) + .await?; Ok((StatusCode::CREATED, uuid.to_string())) } -pub async fn run_flow( +pub async fn run_flow<'c>( authed: &ApiAuthed, db: &DB, + tx_o: Option>, user_db: UserDB, w_id: &str, flow_path: &str, flow_version_info: FlowVersionInfo, run_query: RunJobQuery, args: PushArgsOwned, - trigger_kind: Option, -) -> error::Result<(Uuid, Option)> { + trigger: Option, +) -> error::Result<( + Uuid, + Option, + Option>, +)> { let FlowVersionInfo { version, tag, @@ -4113,22 +4133,30 @@ pub async fn run_flow( check_tag_available_for_workspace(&db, &w_id, &tag, &authed).await?; let scheduled_for = run_query.get_scheduled_for(&db).await?; - let (email, permissioned_as, push_authed, tx) = - if let Some(on_behalf_of_email) = on_behalf_of_email.as_ref() { - ( - on_behalf_of_email, - username_to_permissioned_as(&edited_by), - None, - PushIsolationLevel::IsolatedRoot(db.clone()), - ) - } else { - ( - &authed.email, - username_to_permissioned_as(&authed.username), - Some(authed.clone().into()), - PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into()), - ) - }; + let return_tx = tx_o.is_some(); + + let (email, permissioned_as, push_authed, tx) = if let Some(tx) = tx_o { + ( + &authed.email, + username_to_permissioned_as(&authed.username), + Some(authed.clone().into()), + PushIsolationLevel::Transaction(tx), + ) + } else if let Some(on_behalf_of_email) = on_behalf_of_email.as_ref() { + ( + on_behalf_of_email, + username_to_permissioned_as(&edited_by), + None, + PushIsolationLevel::IsolatedRoot(db.clone()), + ) + } else { + ( + &authed.email, + username_to_permissioned_as(&authed.username), + Some(authed.clone().into()), + PushIsolationLevel::Isolated(user_db.clone(), authed.clone().into()), + ) + }; let (uuid, mut tx) = push( &db, @@ -4164,7 +4192,8 @@ pub async fn run_flow( false, None, None, - trigger_kind, + trigger, + run_query.suspended_mode, ) .await?; @@ -4186,9 +4215,13 @@ pub async fn run_flow( .await?; } - tx.commit().await?; - - Ok((uuid, early_return)) + // If we were given a transaction, return it; otherwise commit it + if return_tx { + Ok((uuid, early_return, Some(tx))) + } else { + tx.commit().await?; + Ok((uuid, early_return, None)) + } } pub async fn run_flow_and_wait_result( @@ -4200,34 +4233,40 @@ pub async fn run_flow_and_wait_result( flow_version_info: FlowVersionInfo, run_query: RunJobQuery, args: PushArgsOwned, - trigger_kind: Option, + trigger: Option, ) -> error::Result { - let (uuid, early_return) = run_flow( + let (uuid, early_return, _) = run_flow( authed, db, + None, user_db, w_id, flow_path, flow_version_info, run_query, args, - trigger_kind, + trigger, ) .await?; run_wait_result(&db, uuid, w_id, early_return, &authed.username).await } -pub async fn run_flow_by_path_inner( +pub async fn push_flow_job_by_path_into_queue<'c>( authed: ApiAuthed, db: DB, + tx_o: Option>, user_db: UserDB, w_id: String, flow_path: StripPath, run_query: RunJobQuery, args: PushArgsOwned, - trigger_kind: Option, -) -> error::Result<(Uuid, Option)> { + trigger: Option, +) -> error::Result<( + Uuid, + Option, + Option>, +)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -4243,13 +4282,14 @@ pub async fn run_flow_by_path_inner( run_flow( &authed, &db, + tx_o, user_db, &w_id, flow_path, flow_version_info, run_query, args, - trigger_kind, + trigger, ) .await } @@ -4287,21 +4327,21 @@ pub async fn run_flow_by_version_inner( version: i64, run_query: RunJobQuery, args: PushArgsOwned, - trigger_kind: Option, + trigger: Option, ) -> error::Result<(Uuid, Option)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; let flow_path = sqlx::query_scalar!( r#" - SELECT - path - FROM - flow_version - WHERE - id = $1 AND - workspace_id = $2 - "#, + SELECT + path + FROM + flow_version + WHERE + id = $1 AND + workspace_id = $2 + "#, version, &w_id ) @@ -4313,18 +4353,21 @@ pub async fn run_flow_by_version_inner( let flow_version_info = get_flow_version_info_from_version(&db, version, &w_id, &flow_path).await?; - run_flow( + let (uuid, early_return, _) = run_flow( &authed, &db, + None, user_db, &w_id, &flow_path, flow_version_info, run_query, args, - trigger_kind, + trigger, ) - .await + .await?; + + Ok((uuid, early_return)) } #[cfg(not(feature = "enterprise"))] @@ -4362,17 +4405,17 @@ pub async fn restart_flow( let mut tx = user_db.clone().begin(&authed).await?; let completed_job = sqlx::query!( - "SELECT - j.runnable_path as script_path, j.args AS \"args: sqlx::types::Json>>\", - j.tag AS \"tag!\", j.priority - FROM v2_job j - WHERE j.id = $1 and j.workspace_id = $2", - job_id, - &w_id, - ) - .fetch_optional(&mut *tx) - .await? - .with_context(|| "Unable to find completed job with the given job UUID")?; + "SELECT + j.runnable_path as script_path, j.args AS \"args: sqlx::types::Json>>\", + j.tag AS \"tag!\", j.priority + FROM v2_job j + WHERE j.id = $1 and j.workspace_id = $2", + job_id, + &w_id, + ) + .fetch_optional(&mut *tx) + .await? + .with_context(|| "Unable to find completed job with the given job UUID")?; drop(tx); let flow_path = completed_job @@ -4420,6 +4463,7 @@ pub async fn restart_flow( None, None, None, + run_query.suspended_mode, ) .await?; tx.commit().await?; @@ -4444,9 +4488,10 @@ pub async fn run_script_by_path( ) .await?; - let (uuid, _) = run_script_by_path_inner( + let (uuid, _, _) = push_script_job_by_path_into_queue( authed, db, + None, user_db, w_id, script_path, @@ -4459,16 +4504,21 @@ pub async fn run_script_by_path( Ok((StatusCode::CREATED, uuid.to_string())) } -pub async fn run_script_by_path_inner( +pub async fn push_script_job_by_path_into_queue<'c>( authed: ApiAuthed, db: DB, + tx_o: Option>, user_db: UserDB, w_id: String, script_path: StripPath, run_query: RunJobQuery, args: PushArgsOwned, - trigger_kind: Option, -) -> error::Result<(Uuid, Option)> { + trigger: Option, +) -> error::Result<( + Uuid, + Option, + Option>, +)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -4489,22 +4539,30 @@ pub async fn run_script_by_path_inner( let tag = run_query.tag.clone().or(tag); check_tag_available_for_workspace(&db, &w_id, &tag, &authed).await?; - let (email, permissioned_as, push_authed, tx) = - if let Some(on_behalf_of) = on_behalf_of.as_ref() { - ( - on_behalf_of.email.as_str(), - on_behalf_of.permissioned_as.clone(), - None, - PushIsolationLevel::IsolatedRoot(db.clone()), - ) - } else { - ( - authed.email.as_str(), - username_to_permissioned_as(&authed.username), - Some(authed.clone().into()), - PushIsolationLevel::Isolated(user_db, authed.clone().into()), - ) - }; + let return_tx = tx_o.is_some(); + + let (email, permissioned_as, push_authed, tx) = if let Some(tx) = tx_o { + ( + authed.email.as_str(), + username_to_permissioned_as(&authed.username), + Some(authed.clone().into()), + PushIsolationLevel::Transaction(tx), + ) + } else if let Some(on_behalf_of) = on_behalf_of.as_ref() { + ( + on_behalf_of.email.as_str(), + on_behalf_of.permissioned_as.clone(), + None, + PushIsolationLevel::IsolatedRoot(db.clone()), + ) + } else { + ( + authed.email.as_str(), + username_to_permissioned_as(&authed.username), + Some(authed.clone().into()), + PushIsolationLevel::Isolated(user_db, authed.clone().into()), + ) + }; let (uuid, tx) = push( &db, @@ -4539,12 +4597,18 @@ pub async fn run_script_by_path_inner( false, None, None, - trigger_kind, + trigger, + run_query.suspended_mode, ) .await?; - tx.commit().await?; - Ok((uuid, delete_after_use)) + // If we were given a transaction, return it; otherwise commit it + if return_tx { + Ok((uuid, delete_after_use, Some(tx))) + } else { + tx.commit().await?; + Ok((uuid, delete_after_use, None)) + } } #[derive(Deserialize)] @@ -4700,6 +4764,7 @@ pub async fn run_workflow_as_code( None, None, None, + None, ) .await?; @@ -4711,13 +4776,13 @@ pub async fn run_workflow_as_code( if !wkflow_query.skip_update.unwrap_or(false) { sqlx::query!( "INSERT INTO v2_job_status (id, workflow_as_code_status) - VALUES ($1, JSONB_SET('{}'::JSONB, array[$2], $3)) - ON CONFLICT (id) DO UPDATE SET - workflow_as_code_status = JSONB_SET( - COALESCE(v2_job_status.workflow_as_code_status, '{}'::JSONB), - array[$2], - $3 - )", + VALUES ($1, JSONB_SET('{}'::JSONB, array[$2], $3)) + ON CONFLICT (id) DO UPDATE SET + workflow_as_code_status = JSONB_SET( + COALESCE(v2_job_status.workflow_as_code_status, '{}'::JSONB), + array[$2], + $3 + )", job_id, uuid.to_string(), serde_json::json!({ "scheduled_for": Utc::now(), "name": entrypoint }), @@ -4728,7 +4793,7 @@ pub async fn run_workflow_as_code( tracing::info!("Skipping update of flow status for job {job_id} in workspace {w_id}"); sqlx::query!( "INSERT INTO v2_job_status (id, workflow_as_code_status) VALUES ($1, '{}'::JSONB) - ON CONFLICT (id) DO NOTHING", + ON CONFLICT (id) DO NOTHING", job_id, ) .execute(&mut *tx) @@ -4860,16 +4925,16 @@ pub async fn run_wait_result_internal( if result.is_none() { let row = sqlx::query!( " - SELECT - result AS \"result: sqlx::types::Json>\", - result_columns, - status = 'success' AS \"success!\" - FROM - v2_job_completed - WHERE - id = $1 AND - workspace_id = $2 - ", + SELECT + result AS \"result: sqlx::types::Json>\", + result_columns, + status = 'success' AS \"success!\" + FROM + v2_job_completed + WHERE + id = $1 AND + workspace_id = $2 + ", uuid, &w_id ) @@ -5036,13 +5101,13 @@ pub async fn delete_job_metadata_after_use(db: &DB, job_uuid: Uuid) -> Result<() pub async fn check_queue_too_long(db: &DB, queue_limit: Option) -> error::Result<()> { if let Some(limit) = queue_limit { let count = sqlx::query_scalar!( - "SELECT COUNT(*) FROM v2_job_queue q WHERE q.canceled_by IS NULL AND (q.scheduled_for <= now() - OR (q.suspend_until IS NOT NULL - AND (q.suspend <= 0 OR q.suspend_until <= now())))", - ) - .fetch_one(db) - .await? - .unwrap_or(0); + "SELECT COUNT(*) FROM v2_job_queue q WHERE q.canceled_by IS NULL AND (q.scheduled_for <= now() + OR (q.suspend_until IS NOT NULL + AND (q.suspend <= 0 OR q.suspend_until <= now())))", + ) + .fetch_one(db) + .await? + .unwrap_or(0); if count > queue_limit.unwrap() { return Err(Error::internal_err(format!( @@ -5238,6 +5303,7 @@ pub async fn run_wait_result_job_by_path_get( None, None, None, + run_query.suspended_mode, ) .await?; tx.commit().await?; @@ -5383,6 +5449,7 @@ pub async fn run_wait_result_script_by_path_internal( None, None, None, + run_query.suspended_mode, ) .await?; tx.commit().await?; @@ -5515,6 +5582,7 @@ pub async fn run_wait_result_script_by_hash( None, None, None, + run_query.suspended_mode, ) .await?; tx.commit().await?; @@ -5677,9 +5745,10 @@ pub async fn stream_job( let uuid = match runnable_id { RunnableId::ScriptId(ScriptId::ScriptPath(script_path)) | RunnableId::HubScript(script_path) => { - run_script_by_path_inner( + let (uuid, _, _) = push_script_job_by_path_into_queue( authed.clone(), db.clone(), + None, user_db, w_id.clone(), StripPath(script_path), @@ -5687,8 +5756,8 @@ pub async fn stream_job( args, None, ) - .await? - .0 + .await?; + uuid } RunnableId::ScriptId(ScriptId::ScriptHash(script_hash)) => { run_job_by_hash_inner( @@ -5699,14 +5768,16 @@ pub async fn stream_job( script_hash, run_query, args, + None, ) .await? .0 } RunnableId::FlowId(FlowId::FlowPath(flow_path)) => { - run_flow_by_path_inner( + push_flow_job_by_path_into_queue( authed.clone(), db.clone(), + None, user_db, w_id.clone(), StripPath(flow_path), @@ -5876,14 +5947,14 @@ pub async fn run_wait_result_flow_by_version( let flow_path = sqlx::query_scalar!( r#" - SELECT - path - FROM - flow_version - WHERE - id = $1 AND - workspace_id = $2 - "#, + SELECT + path + FROM + flow_version + WHERE + id = $1 AND + workspace_id = $2 + "#, version, &w_id ) @@ -5986,6 +6057,7 @@ async fn run_preview_script( None, None, None, + None, ) .await?; tx.commit().await?; @@ -6138,6 +6210,7 @@ async fn run_bundle_preview_script( None, None, None, + None, ) .await?; job_id = Some(uuid); @@ -6241,8 +6314,8 @@ async fn run_dependencies_job( if req.raw_scripts.len() != 1 || req.raw_scripts[0].script_path != req.entrypoint { return Err(error::Error::internal_err( - "For now only a single raw script can be passed to this endpoint, and the entrypoint should be set to the script path".to_string(), - )); + "For now only a single raw script can be passed to this endpoint, and the entrypoint should be set to the script path".to_string(), + )); } let RawScriptForDependencies { @@ -6289,6 +6362,7 @@ async fn run_dependencies_job( None, None, None, + None, ) .await?; tx.commit().await?; @@ -6377,6 +6451,7 @@ async fn run_flow_dependencies_job( None, None, None, + None, ) .await?; tx.commit().await?; @@ -6431,15 +6506,15 @@ async fn add_batch_jobs( let db_authed = UserDbWithAuthed { db: user_db.clone(), authed: &authed.to_authed_ref() }; let ScriptHashInfo { - hash: script_hash, - concurrency_key, - concurrent_limit, - concurrency_time_window_s, - language, - dedicated_worker, - timeout, - .. // TODO: consider on_behalf_of_email and created_by for batch jobs - } = get_latest_deployed_hash_for_path(Some(db_authed), db.clone(), &w_id, &path).await?; + hash: script_hash, + concurrency_key, + concurrent_limit, + concurrency_time_window_s, + language, + dedicated_worker, + timeout, + .. // TODO: consider on_behalf_of_email and created_by for batch jobs + } = get_latest_deployed_hash_for_path(Some(db_authed), db.clone(), &w_id, &path).await?; ( Some(script_hash), Some(path), @@ -6490,17 +6565,17 @@ async fn add_batch_jobs( } else if let Some(path) = batch_info.path { let mut tx = user_db.clone().begin(&authed).await?; let value_json = sqlx::query!( - "SELECT coalesce(flow_version_lite.value, flow_version.value) as \"value!: sqlx::types::Json>\" FROM flow - LEFT JOIN flow_version - ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] - LEFT JOIN flow_version_lite - ON flow_version_lite.id = flow_version.id - WHERE flow.path = $1 AND flow.workspace_id = $2 LIMIT 1", - &path, &w_id - ) - .fetch_optional(&mut *tx) - .await? - .ok_or_else(|| Error::internal_err(format!("not found flow at path {:?}", path)))?; + "SELECT coalesce(flow_version_lite.value, flow_version.value) as \"value!: sqlx::types::Json>\" FROM flow + LEFT JOIN flow_version + ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] + LEFT JOIN flow_version_lite + ON flow_version_lite.id = flow_version.id + WHERE flow.path = $1 AND flow.workspace_id = $2 LIMIT 1", + &path, &w_id + ) + .fetch_optional(&mut *tx) + .await? + .ok_or_else(|| Error::internal_err(format!("not found flow at path {:?}", path)))?; let value = serde_json::from_str::(value_json.value.get()).map_err(|err| { Error::internal_err(format!( @@ -6571,44 +6646,44 @@ async fn add_batch_jobs( let mut tx = user_db.begin(&authed).await?; let uuids = sqlx::query_scalar!( - r#"WITH uuid_table as ( - select gen_random_uuid() as uuid from generate_series(1, $16) + r#"WITH uuid_table as ( + select gen_random_uuid() as uuid from generate_series(1, $16) + ) + INSERT INTO v2_job + (id, workspace_id, raw_code, raw_lock, raw_flow, tag, runnable_id, runnable_path, kind, + script_lang, created_by, permissioned_as, permissioned_as_email, concurrent_limit, + concurrency_time_window_s, timeout, args) + (SELECT uuid, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, + ('{ "uuid": "' || uuid || '" }')::jsonb FROM uuid_table) + RETURNING id AS "id!""#, + w_id, + raw_code, + raw_lock, + raw_flow.map(sqlx::types::Json) as Option>, + tag, + hash, + path, + job_kind.clone() as JobKind, + language as ScriptLang, + authed.username, + username_to_permissioned_as(&authed.username), + authed.email, + concurrent_limit, + concurrent_time_window_s, + timeout, + n, ) - INSERT INTO v2_job - (id, workspace_id, raw_code, raw_lock, raw_flow, tag, runnable_id, runnable_path, kind, - script_lang, created_by, permissioned_as, permissioned_as_email, concurrent_limit, - concurrency_time_window_s, timeout, args) - (SELECT uuid, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, - ('{ "uuid": "' || uuid || '" }')::jsonb FROM uuid_table) - RETURNING id AS "id!""#, - w_id, - raw_code, - raw_lock, - raw_flow.map(sqlx::types::Json) as Option>, - tag, - hash, - path, - job_kind.clone() as JobKind, - language as ScriptLang, - authed.username, - username_to_permissioned_as(&authed.username), - authed.email, - concurrent_limit, - concurrent_time_window_s, - timeout, - n, - ) - .fetch_all(&mut *tx) - .await?; + .fetch_all(&mut *tx) + .await?; let uuids = sqlx::query_scalar!( r#"WITH uuid_table as ( - select unnest($4::uuid[]) as uuid - ) - INSERT INTO v2_job_queue - (id, workspace_id, scheduled_for, tag) - (SELECT uuid, $1, $2, $3 FROM uuid_table) - RETURNING id"#, + select unnest($4::uuid[]) as uuid + ) + INSERT INTO v2_job_queue + (id, workspace_id, scheduled_for, tag) + (SELECT uuid, $1, $2, $3 FROM uuid_table) + RETURNING id"#, w_id, Utc::now(), tag, @@ -6625,24 +6700,24 @@ async fn add_batch_jobs( .await?; sqlx::query!( - "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) - SELECT unnest($1::uuid[]), $2, $3, $4, $5, $6, $7, $8", - &uuids, - authed.email, - authed.username, - authed.is_admin, - authed.is_operator, - &[], - &[], - w_id, - ) - .execute(&mut *tx) - .await?; + "INSERT INTO job_perms (job_id, email, username, is_admin, is_operator, folders, groups, workspace_id) + SELECT unnest($1::uuid[]), $2, $3, $4, $5, $6, $7, $8", + &uuids, + authed.email, + authed.username, + authed.is_admin, + authed.is_operator, + &[], + &[], + w_id, + ) + .execute(&mut *tx) + .await?; if let Some(flow_status) = flow_status { sqlx::query!( "INSERT INTO v2_job_status (id, flow_status) - SELECT unnest($1::uuid[]), $2", + SELECT unnest($1::uuid[]), $2", &uuids, sqlx::types::Json(flow_status) as sqlx::types::Json ) @@ -6653,7 +6728,7 @@ async fn add_batch_jobs( if let Some(custom_concurrency_key) = custom_concurrency_key { sqlx::query!( "INSERT INTO concurrency_counter(concurrency_id, job_uuids) - VALUES ($1, '{}'::jsonb)", + VALUES ($1, '{}'::jsonb)", &custom_concurrency_key ) .execute(&mut *tx) @@ -6731,6 +6806,7 @@ async fn run_preview_flow_job( None, None, None, + None, ) .await?; @@ -6815,9 +6891,10 @@ async fn run_dynamic_select( let push_args = PushArgsOwned { extra: None, args: script_args.clone() }; - let (uuid, _) = run_script_by_path_inner( + let (uuid, _, _) = push_script_job_by_path_into_queue( authed.clone(), db.clone(), + None, user_db.clone(), w_id.clone(), StripPath(path), @@ -6838,14 +6915,14 @@ async fn run_dynamic_select( None => { let dynamic_input = sqlx::query_scalar!( r#" - SELECT - schema - FROM - flow - WHERE - workspace_id = $1 AND - path = $2 - "#, + SELECT + schema + FROM + flow + WHERE + workspace_id = $1 AND + path = $2 + "#, &w_id, &path ) @@ -6927,6 +7004,7 @@ async fn run_dynamic_select( None, None, None, + None, ) .await?; tx.commit().await?; @@ -6952,8 +7030,17 @@ pub async fn run_job_by_hash( ) .await?; - let (uuid, _) = - run_job_by_hash_inner(authed, db, user_db, w_id, script_hash, run_query, args).await?; + let (uuid, _) = run_job_by_hash_inner( + authed, + db, + user_db, + w_id, + script_hash, + run_query, + args, + None, + ) + .await?; Ok((StatusCode::CREATED, uuid.to_string())) } @@ -6966,6 +7053,7 @@ pub async fn run_job_by_hash_inner( script_hash: ScriptHash, run_query: RunJobQuery, args: PushArgsOwned, + trigger: Option, ) -> error::Result<(Uuid, Option)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -7068,7 +7156,8 @@ pub async fn run_job_by_hash_inner( false, None, None, - None, + trigger, + run_query.suspended_mode, ) .await?; tx.commit().await?; @@ -7185,15 +7274,15 @@ async fn get_log_file(Path((_w_id, file_p)): Path<(String, String)>) -> error::R } } else { return Err(error::Error::internal_err(format!( - "Object store client not present and file not found on server logs volume at {local_file}" - ))); + "Object store client not present and file not found on server logs volume at {local_file}" + ))); } #[cfg(not(all(feature = "enterprise", feature = "parquet")))] - return Err(error::Error::NotFound(format!( - "File not found on server logs volume /tmp/windmill/logs and no distributed logs s3 storage for {}", - file_p - ))); + return Err(error::Error::NotFound(format!( + "File not found on server logs volume /tmp/windmill/logs and no distributed logs s3 storage for {}", + file_p + ))); } async fn get_job_update( @@ -7557,12 +7646,12 @@ async fn get_flow_stream_delta( if let Some(job_id) = flow_stream_job_id { let record = sqlx::query!( " - SELECT - string_agg(stream, '' order by idx asc) as stream, - max(idx) + 1 as offset - FROM job_result_stream_v2 - WHERE job_id = $2 AND idx >= $1 - ", + SELECT + string_agg(stream, '' order by idx asc) as stream, + max(idx) + 1 as offset + FROM job_result_stream_v2 + WHERE job_id = $2 AND idx >= $1 + ", stream_offset.unwrap_or(0), job_id, ) @@ -7618,37 +7707,37 @@ async fn get_job_update_data( let (result, running, mut result_stream, mut new_stream_offset, new_flow_stream_job_id) = if let Some(tags) = tags { let r = sqlx::query!( - " - WITH result_stream AS ( + " + WITH result_stream AS ( + SELECT + string_agg(stream, '' order by idx asc) as stream, + job_id, + max(idx) + 1 as offset + FROM job_result_stream_v2 + WHERE job_id = $2 AND idx >= $3 + GROUP BY job_id + ) SELECT - string_agg(stream, '' order by idx asc) as stream, - job_id, - max(idx) + 1 as offset - FROM job_result_stream_v2 - WHERE job_id = $2 AND idx >= $3 - GROUP BY job_id + jc.result as \"result: sqlx::types::Json>\", + v2_job.tag, + v2_job_queue.running as \"running: Option\", + rs.stream AS \"result_stream: Option\", + rs.offset AS stream_offset, + CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job + FROM v2_job + LEFT JOIN v2_job_queue USING (id) + LEFT JOIN v2_job_completed jc USING (id) + LEFT JOIN v2_job_status js USING (id) + LEFT JOIN result_stream rs ON rs.job_id = $2 + WHERE v2_job.id = $2 AND v2_job.workspace_id = $1", + w_id, + job_id, + stream_offset.unwrap_or(0), + ignore_flow_stream_job_id, ) - SELECT - jc.result as \"result: sqlx::types::Json>\", - v2_job.tag, - v2_job_queue.running as \"running: Option\", - rs.stream AS \"result_stream: Option\", - rs.offset AS stream_offset, - CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job - FROM v2_job - LEFT JOIN v2_job_queue USING (id) - LEFT JOIN v2_job_completed jc USING (id) - LEFT JOIN v2_job_status js USING (id) - LEFT JOIN result_stream rs ON rs.job_id = $2 - WHERE v2_job.id = $2 AND v2_job.workspace_id = $1", - w_id, - job_id, - stream_offset.unwrap_or(0), - ignore_flow_stream_job_id, - ) - .fetch_optional(db) - .await? - .ok_or_else(|| Error::NotFound(format!("Job not found: {}", job_id)))?; + .fetch_optional(db) + .await? + .ok_or_else(|| Error::NotFound(format!("Job not found: {}", job_id)))?; if !tags.contains(&r.tag.as_str()) { return Err(Error::NotAuthorized(format!( @@ -7668,35 +7757,35 @@ async fn get_job_update_data( } else { if running.is_some_and(|x| !x) { let r = sqlx::query!( - " - WITH result_stream AS ( - SELECT - string_agg(stream, '' order by idx asc) as stream, - job_id, - max(idx) + 1 as offset - FROM job_result_stream_v2 - WHERE job_id = $1 AND idx >= $3 - GROUP BY job_id - ) - SELECT - COALESCE(jc.result, jc.result) as \"result: sqlx::types::Json>\", - jq.running as \"running: Option\", - rs.stream AS \"result_stream: Option\", - rs.offset AS stream_offset, - CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job - FROM ( - SELECT $1::uuid as job_id, $2::text as workspace_id - ) base - LEFT JOIN v2_job_completed jc ON jc.id = base.job_id AND jc.workspace_id = base.workspace_id - LEFT JOIN v2_job_queue jq ON jq.id = base.job_id AND jq.workspace_id = base.workspace_id - LEFT JOIN v2_job_status js ON js.id = base.job_id - LEFT JOIN result_stream rs ON rs.job_id = base.job_id - WHERE base.job_id = $1", - job_id, - w_id, - stream_offset.unwrap_or(0), - ignore_flow_stream_job_id, - ).fetch_optional(db).await?; + " + WITH result_stream AS ( + SELECT + string_agg(stream, '' order by idx asc) as stream, + job_id, + max(idx) + 1 as offset + FROM job_result_stream_v2 + WHERE job_id = $1 AND idx >= $3 + GROUP BY job_id + ) + SELECT + COALESCE(jc.result, jc.result) as \"result: sqlx::types::Json>\", + jq.running as \"running: Option\", + rs.stream AS \"result_stream: Option\", + rs.offset AS stream_offset, + CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job + FROM ( + SELECT $1::uuid as job_id, $2::text as workspace_id + ) base + LEFT JOIN v2_job_completed jc ON jc.id = base.job_id AND jc.workspace_id = base.workspace_id + LEFT JOIN v2_job_queue jq ON jq.id = base.job_id AND jq.workspace_id = base.workspace_id + LEFT JOIN v2_job_status js ON js.id = base.job_id + LEFT JOIN result_stream rs ON rs.job_id = base.job_id + WHERE base.job_id = $1", + job_id, + w_id, + stream_offset.unwrap_or(0), + ignore_flow_stream_job_id, + ).fetch_optional(db).await?; if let Some(r) = r { let running = r.running.as_ref().map(|x| *x); ( @@ -7711,36 +7800,36 @@ async fn get_job_update_data( } } else { let q = sqlx::query!( - " - WITH result_stream AS ( - SELECT - string_agg(stream, '' order by idx asc) as stream, - job_id, - max(idx) + 1 as offset - FROM job_result_stream_v2 - WHERE job_id = $2 AND idx >= $3 - GROUP BY job_id + " + WITH result_stream AS ( + SELECT + string_agg(stream, '' order by idx asc) as stream, + job_id, + max(idx) + 1 as offset + FROM job_result_stream_v2 + WHERE job_id = $2 AND idx >= $3 + GROUP BY job_id + ) + SELECT + COALESCE(jc.result, NULL) as \"result: sqlx::types::Json>\", + rs.stream AS \"result_stream: Option\", + rs.offset AS stream_offset, + COALESCE(js.flow_status, jc.flow_status) as \"flow_status: sqlx::types::Json>\", + CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job + FROM ( + SELECT $2::uuid as job_id, $1::text as workspace_id + ) base + LEFT JOIN v2_job_completed jc ON jc.id = base.job_id AND jc.workspace_id = base.workspace_id + LEFT JOIN v2_job_status js ON js.id = base.job_id + LEFT JOIN result_stream rs ON rs.job_id = base.job_id + WHERE base.job_id = $2", + w_id, + job_id, + stream_offset.unwrap_or(0), + ignore_flow_stream_job_id, ) - SELECT - COALESCE(jc.result, NULL) as \"result: sqlx::types::Json>\", - rs.stream AS \"result_stream: Option\", - rs.offset AS stream_offset, - COALESCE(js.flow_status, jc.flow_status) as \"flow_status: sqlx::types::Json>\", - CASE WHEN $4 THEN NULL ELSE (COALESCE(js.flow_status, jc.flow_status)->>'stream_job')::uuid END as stream_job - FROM ( - SELECT $2::uuid as job_id, $1::text as workspace_id - ) base - LEFT JOIN v2_job_completed jc ON jc.id = base.job_id AND jc.workspace_id = base.workspace_id - LEFT JOIN v2_job_status js ON js.id = base.job_id - LEFT JOIN result_stream rs ON rs.job_id = base.job_id - WHERE base.job_id = $2", - w_id, - job_id, - stream_offset.unwrap_or(0), - ignore_flow_stream_job_id, - ) - .fetch_optional(db) - .await?; + .fetch_optional(db) + .await?; if let Some(r) = q { ( r.result.map(|x| x.0), @@ -7782,56 +7871,56 @@ async fn get_job_update_data( }) } else { let mut record = sqlx::query!( - " - WITH result_stream AS ( - SELECT - string_agg(stream, '' order by idx asc) as stream, - job_id, - max(idx) + 1 as offset - FROM job_result_stream_v2 - WHERE job_id = $3 AND idx >= $8 - GROUP BY job_id + " + WITH result_stream AS ( + SELECT + string_agg(stream, '' order by idx asc) as stream, + job_id, + max(idx) + 1 as offset + FROM job_result_stream_v2 + WHERE job_id = $3 AND idx >= $8 + GROUP BY job_id + ) + SELECT + c.id IS NOT NULL AS completed, + CASE + WHEN q.id IS NOT NULL THEN (CASE WHEN NOT $5 AND q.running THEN true ELSE null END) + ELSE false + END AS running, + CASE WHEN $7::BOOLEAN THEN NULL ELSE SUBSTR(logs, GREATEST($1 - log_offset, 0)) END AS logs, + rs.stream AS new_result_stream, + COALESCE(r.memory_peak, c.memory_peak) AS mem_peak, + COALESCE(c.flow_status, f.flow_status) AS \"flow_status: sqlx::types::Json>\", + (COALESCE(c.flow_status, f.flow_status)->>'stream_job')::uuid AS stream_job, + COALESCE(c.workflow_as_code_status, f.workflow_as_code_status) AS \"workflow_as_code_status: sqlx::types::Json>\", + CASE WHEN $7::BOOLEAN THEN NULL ELSE job_logs.log_offset + CHAR_LENGTH(job_logs.logs) + 1 END AS log_offset, + rs.offset AS stream_offset, + created_by AS \"created_by!\", + CASE WHEN $4::BOOLEAN THEN ( + SELECT scalar_int FROM job_stats WHERE job_id = $3 AND metric_id = 'progress_perc' + ) END AS progress, + rs.stream AS \"result_stream: Option\" + FROM v2_job j + LEFT JOIN v2_job_queue q USING (id) + LEFT JOIN v2_job_runtime r USING (id) + LEFT JOIN v2_job_status f USING (id) + LEFT JOIN v2_job_completed c USING (id) + LEFT JOIN result_stream rs ON rs.job_id = $3 + LEFT JOIN job_logs ON job_logs.job_id = $3 + WHERE j.workspace_id = $2 AND j.id = $3 + AND ($6::text[] IS NULL OR j.tag = ANY($6))", + log_offset, + w_id, + job_id, + get_progress, + running, + tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, + no_logs.unwrap_or(false), + stream_offset.unwrap_or(0), ) - SELECT - c.id IS NOT NULL AS completed, - CASE - WHEN q.id IS NOT NULL THEN (CASE WHEN NOT $5 AND q.running THEN true ELSE null END) - ELSE false - END AS running, - CASE WHEN $7::BOOLEAN THEN NULL ELSE SUBSTR(logs, GREATEST($1 - log_offset, 0)) END AS logs, - rs.stream AS new_result_stream, - COALESCE(r.memory_peak, c.memory_peak) AS mem_peak, - COALESCE(c.flow_status, f.flow_status) AS \"flow_status: sqlx::types::Json>\", - (COALESCE(c.flow_status, f.flow_status)->>'stream_job')::uuid AS stream_job, - COALESCE(c.workflow_as_code_status, f.workflow_as_code_status) AS \"workflow_as_code_status: sqlx::types::Json>\", - CASE WHEN $7::BOOLEAN THEN NULL ELSE job_logs.log_offset + CHAR_LENGTH(job_logs.logs) + 1 END AS log_offset, - rs.offset AS stream_offset, - created_by AS \"created_by!\", - CASE WHEN $4::BOOLEAN THEN ( - SELECT scalar_int FROM job_stats WHERE job_id = $3 AND metric_id = 'progress_perc' - ) END AS progress, - rs.stream AS \"result_stream: Option\" - FROM v2_job j - LEFT JOIN v2_job_queue q USING (id) - LEFT JOIN v2_job_runtime r USING (id) - LEFT JOIN v2_job_status f USING (id) - LEFT JOIN v2_job_completed c USING (id) - LEFT JOIN result_stream rs ON rs.job_id = $3 - LEFT JOIN job_logs ON job_logs.job_id = $3 - WHERE j.workspace_id = $2 AND j.id = $3 - AND ($6::text[] IS NULL OR j.tag = ANY($6))", - log_offset, - w_id, - job_id, - get_progress, - running, - tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, - no_logs.unwrap_or(false), - stream_offset.unwrap_or(0), - ) - .fetch_optional(db) - .await? - .ok_or_else(|| Error::NotFound(format!("Job not found: {}", job_id)))?; + .fetch_optional(db) + .await? + .ok_or_else(|| Error::NotFound(format!("Job not found: {}", job_id)))?; if opt_authed.is_none() && record.created_by != "anonymous" { return Err(Error::BadRequest( @@ -7899,9 +7988,9 @@ pub fn filter_list_completed_query( if let Some(label) = &lq.label { if lq.allow_wildcards.unwrap_or(false) { let wh = format!( - "EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE jsonb_typeof(result->'wm_labels') = 'array' AND label LIKE '{}')", - &label.replace("*", "%").replace("'", "''") - ); + "EXISTS (SELECT 1 FROM jsonb_array_elements_text(result->'wm_labels') label WHERE jsonb_typeof(result->'wm_labels') = 'array' AND label LIKE '{}')", + &label.replace("*", "%").replace("'", "''") + ); sqlb.and_where("result ? 'wm_labels'"); sqlb.and_where(&wh); } else { @@ -8041,6 +8130,10 @@ pub fn filter_list_completed_query( sqlb.and_where_eq("trigger_kind", "?".bind(&format!("{}", tk))); } + if let Some(tp) = &lq.trigger_path { + sqlb.and_where_eq("trigger", "?".bind(tp)); + } + sqlb } @@ -8118,6 +8211,7 @@ pub struct ListCompletedQuery { pub worker: Option, pub allow_wildcards: Option, pub trigger_kind: Option, + pub trigger_path: Option, } async fn list_completed_jobs( @@ -8240,12 +8334,12 @@ async fn get_completed_job_result( sqlx::query_as!( RawResult, "SELECT - result #> $3 AS \"result: sqlx::types::Json>\", - result_columns, - created_by AS \"created_by!\" - FROM v2_job_completed c - JOIN v2_job USING (id) - WHERE c.id = $1 AND c.workspace_id = $2 AND ($4::text[] IS NULL OR tag = ANY($4))", + result #> $3 AS \"result: sqlx::types::Json>\", + result_columns, + created_by AS \"created_by!\" + FROM v2_job_completed c + JOIN v2_job USING (id) + WHERE c.id = $1 AND c.workspace_id = $2 AND ($4::text[] IS NULL OR tag = ANY($4))", id, &w_id, json_path.split(".").collect::>() as Vec<&str>, @@ -8257,12 +8351,12 @@ async fn get_completed_job_result( sqlx::query_as!( RawResult, "SELECT - result AS \"result: sqlx::types::Json>\", - result_columns, - created_by AS \"created_by!\" - FROM v2_job_completed c - JOIN v2_job USING (id) - WHERE c.id = $1 AND c.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", + result AS \"result: sqlx::types::Json>\", + result_columns, + created_by AS \"created_by!\" + FROM v2_job_completed c + JOIN v2_job USING (id) + WHERE c.id = $1 AND c.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", id, &w_id, tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, @@ -8349,19 +8443,19 @@ async fn count_by_tag( let horizon = query.horizon_secs.unwrap_or(3600); // Default to 1 hour if not specified let counts = sqlx::query_as!( - TagCount, - r#" - SELECT j.tag as "tag!", COUNT(*) as "count!" - FROM v2_job_completed c JOIN v2_job j USING (id) - WHERE c.started_at > NOW() - make_interval(secs => $1) AND ($2::text IS NULL OR j.workspace_id = $2) - GROUP BY j.tag - ORDER BY "count!" DESC - "#, - horizon as f64, - query.workspace_id - ) - .fetch_all(&db) - .await?; + TagCount, + r#" + SELECT j.tag as "tag!", COUNT(*) as "count!" + FROM v2_job_completed c JOIN v2_job j USING (id) + WHERE c.started_at > NOW() - make_interval(secs => $1) AND ($2::text IS NULL OR j.workspace_id = $2) + GROUP BY j.tag + ORDER BY "count!" DESC + "#, + horizon as f64, + query.workspace_id + ) + .fetch_all(&db) + .await?; Ok(Json(counts)) } @@ -8392,13 +8486,13 @@ async fn get_completed_job_result_maybe( .flatten(); let result_o = sqlx::query!( "SELECT - result AS \"result: sqlx::types::Json>\", - result_columns, - status = 'success' AS \"success!\", - created_by AS \"created_by!\" - FROM v2_job_completed c - JOIN v2_job j USING (id) - WHERE c.id = $1 AND c.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", + result AS \"result: sqlx::types::Json>\", + result_columns, + status = 'success' AS \"success!\", + created_by AS \"created_by!\" + FROM v2_job_completed c + JOIN v2_job j USING (id) + WHERE c.id = $1 AND c.workspace_id = $2 AND ($3::text[] IS NULL OR tag = ANY($3))", id, &w_id, tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, @@ -8470,15 +8564,15 @@ async fn delete_completed_job<'a>( let tags = get_scope_tags(&authed); let job_o = sqlx::query_scalar!( "UPDATE v2_job_completed c SET - result = NULL, - deleted = TRUE - FROM v2_job j - WHERE c.id = $1 - AND j.id = c.id - AND c.workspace_id = $2 - AND ($3::TEXT[] IS NULL OR tag = ANY($3)) - RETURNING c.id - ", + result = NULL, + deleted = TRUE + FROM v2_job j + WHERE c.id = $1 + AND j.id = c.id + AND c.workspace_id = $2 + AND ($3::TEXT[] IS NULL OR tag = ANY($3)) + RETURNING c.id + ", id, &w_id, tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index fb777b6ed6..272f132b7e 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -1035,6 +1035,7 @@ async fn create_script_internal<'c>( None, None, None, + None, ) .await?; diff --git a/backend/windmill-api/src/triggers/global_handler.rs b/backend/windmill-api/src/triggers/global_handler.rs new file mode 100644 index 0000000000..3143d6806f --- /dev/null +++ b/backend/windmill-api/src/triggers/global_handler.rs @@ -0,0 +1,324 @@ +use crate::{ + db::{ApiAuthed, DB}, + jobs::cancel_jobs, + triggers::trigger_helpers::trigger_runnable_inner, +}; +use axum::{ + extract::{Extension, Path}, + response::Json, +}; +use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; +use serde_json::value::RawValue; +use sqlx::PgConnection; +use std::collections::HashMap; +use uuid::Uuid; +use windmill_common::{ + db::UserDB, + error::{self, Error, Result}, + jobs::JobTriggerKind, + triggers::TriggerMetadata, +}; + +#[derive(sqlx::FromRow)] +pub struct SuspendedTrigger { + pub script_path: String, + pub is_flow: bool, + pub edited_by: String, + pub email: String, + pub edited_at: DateTime, + pub error_handler_path: Option, + pub error_handler_args: Option>>, + pub retry: Option>, +} + +async fn get_suspended_trigger( + tx: &mut PgConnection, + workspace_id: &str, + trigger_kind: &JobTriggerKind, + path: &str, +) -> Result { + match trigger_kind { + JobTriggerKind::Webhook | JobTriggerKind::Schedule => { + return Err(Error::BadRequest(format!( + "{} triggers do not support job reassignment", + trigger_kind + ))); + } + _ => {} + } + + let table_name = format!("{}_trigger", trigger_kind.to_string()); + + let fields = vec![ + "script_path", + "is_flow", + "edited_by", + "email", + "edited_at", + "error_handler_path", + "error_handler_args", + "retry", + ]; + + let sql = format!( + r#"SELECT + {} + FROM + {} + WHERE + workspace_id = $1 AND + path = $2 + "#, + fields.join(", "), + table_name + ); + + sqlx::query_as(&sql) + .bind(workspace_id) + .bind(path) + .fetch_optional(&mut *tx) + .await? + .ok_or_else(|| Error::NotFound(format!("Trigger not found at path: {}", path))) +} + +struct JobWithArgs { + id: Uuid, + args: Option>>>, + created_at: chrono::DateTime, +} + +#[derive(Deserialize, Serialize, Default)] +pub struct ReassignJobsBody { + #[serde(default)] + pub job_ids: Option>, +} + +pub async fn resume_suspended_trigger_jobs( + authed: ApiAuthed, + Extension(db): Extension, + Extension(user_db): Extension, + Path((w_id, trigger_kind, trigger_path)): Path<(String, JobTriggerKind, String)>, + Json(body): Json, +) -> error::Result> { + let mut tx = user_db.clone().begin(&authed).await?; + + let trigger = get_suspended_trigger(&mut *tx, &w_id, &trigger_kind, &trigger_path).await?; + + let jobs = if let Some(job_ids) = body.job_ids.as_ref() { + if job_ids.is_empty() { + vec![] + } else { + sqlx::query_as!( + JobWithArgs, + r#" + SELECT + id, + args as "args: _", + created_at + FROM v2_job + WHERE workspace_id = $1 + AND ( + kind = 'unassigned_script'::JOB_KIND OR + kind = 'unassigned_flow'::JOB_KIND OR + kind = 'unassigned_singlestepflow'::JOB_KIND + ) + AND trigger_kind = $2 + AND trigger = $3 + AND id = ANY($4) + "#, + w_id, + trigger_kind as _, + trigger_path, + job_ids as _, + ) + .fetch_all(&mut *tx) + .await? + } + } else { + sqlx::query_as!( + JobWithArgs, + r#" + SELECT + id, + args as "args: _", + created_at + FROM v2_job + WHERE workspace_id = $1 + AND ( + kind = 'unassigned_script'::JOB_KIND OR + kind = 'unassigned_flow'::JOB_KIND OR + kind = 'unassigned_singlestepflow'::JOB_KIND + ) + AND trigger_kind = $2 + AND trigger = $3 + "#, + w_id, + trigger_kind as _, + trigger_path, + ) + .fetch_all(&mut *tx) + .await? + }; + + let trigger_metadata = TriggerMetadata::new(Some(trigger_path.clone()), trigger_kind); + + let l = jobs.len(); + + for job in jobs { + // If job was created before trigger was edited, simply update it to unsuspend + // instead of deleting and repushing + if job.created_at > trigger.edited_at { + let job_kind = if trigger.is_flow { + windmill_common::jobs::JobKind::Flow + } else { + windmill_common::jobs::JobKind::Script + }; + + sqlx::query!( + "UPDATE v2_job SET kind = $1 WHERE id = $2", + job_kind as _, + job.id + ) + .execute(&mut *tx) + .await?; + + // Update the job to unsuspend it and set the correct kind + sqlx::query!( + "UPDATE v2_job_queue SET scheduled_for = now() WHERE id = $1", + job.id + ) + .execute(&mut *tx) + .await?; + } else { + // Job was created after trigger edit - delete and repush with new configuration + // Pass the transaction to trigger_runnable_inner so everything is in the same transaction + let (_uuid, _delete_after_use, _early_return, tx_o) = trigger_runnable_inner( + &db, + Some(tx), + Some(user_db.clone()), + authed.clone(), + &w_id, + &trigger.script_path, + trigger.is_flow, + windmill_queue::PushArgsOwned { + extra: None, + args: job.args.map(|a| a.0).unwrap_or_default(), + }, + trigger.retry.as_ref(), + trigger.error_handler_path.as_deref(), + trigger.error_handler_args.as_ref(), + trigger_path.clone(), + None, + trigger_metadata.clone(), + None, + ) + .await?; + + tx = match tx_o { + Some(tx) => tx, + None => { + return Err(error::Error::internal_err( + "Transaction should be returned when passed in".to_string(), + )); + } + }; + + // Delete the unassigned job from all related tables + sqlx::query!("DELETE FROM v2_job_queue WHERE id = $1", job.id) + .execute(&mut *tx) + .await?; + + sqlx::query!("DELETE FROM v2_job_runtime WHERE id = $1", job.id) + .execute(&mut *tx) + .await?; + + sqlx::query!("DELETE FROM job_perms WHERE job_id = $1", job.id) + .execute(&mut *tx) + .await?; + + sqlx::query!("DELETE FROM concurrency_key WHERE job_id = $1", job.id) + .execute(&mut *tx) + .await?; + + sqlx::query!("DELETE FROM debounce_key WHERE job_id = $1", job.id) + .execute(&mut *tx) + .await?; + + sqlx::query!("DELETE FROM debounce_stale_data WHERE job_id = $1", job.id) + .execute(&mut *tx) + .await?; + + sqlx::query!("DELETE FROM v2_job WHERE id = $1", job.id) + .execute(&mut *tx) + .await?; + } + } + + tx.commit().await?; + + Ok(Json(format!("Reassigned {} jobs", l))) +} + +pub async fn cancel_suspended_trigger_jobs( + authed: ApiAuthed, + Extension(db): Extension, + Extension(user_db): Extension, + Path((w_id, trigger_kind, trigger_path)): Path<(String, JobTriggerKind, String)>, + Json(body): Json, +) -> error::Result> { + let mut tx = user_db.clone().begin(&authed).await?; + + // Get the list of job IDs to cancel + let jobs_to_cancel = if let Some(job_ids) = body.job_ids.as_ref() { + if job_ids.is_empty() { + vec![] + } else { + sqlx::query_scalar!( + "SELECT id FROM v2_job + WHERE workspace_id = $1 + AND (kind = 'unassigned_script'::JOB_KIND OR kind = 'unassigned_flow'::JOB_KIND OR kind = 'unassigned_singlestepflow'::JOB_KIND) + AND trigger_kind = $2 + AND trigger = $3 + AND id = ANY($4)", + w_id, + trigger_kind as _, + trigger_path, + job_ids as _, + ) + .fetch_all(&mut *tx) + .await? + } + } else { + sqlx::query_scalar!( + "SELECT id FROM v2_job + WHERE workspace_id = $1 + AND (kind = 'unassigned_script'::JOB_KIND OR kind = 'unassigned_flow'::JOB_KIND OR kind = 'unassigned_singlestepflow'::JOB_KIND) + AND trigger_kind = $2 + AND trigger = $3", + w_id, + trigger_kind as _, + trigger_path, + ) + .fetch_all(&mut *tx) + .await? + }; + + tx.commit().await?; + + let count = jobs_to_cancel.len(); + + if count > 0 { + let cancelled_jobs = cancel_jobs( + jobs_to_cancel, + &db, + authed.username.as_str(), + w_id.as_str(), + true, + ) + .await?; + Ok(Json(format!("Canceled {} jobs", cancelled_jobs.0.len()))) + } else { + Ok(Json(format!("No jobs to cancel"))) + } +} diff --git a/backend/windmill-api/src/triggers/handler.rs b/backend/windmill-api/src/triggers/handler.rs index 05042bd385..68012a5bcc 100644 --- a/backend/windmill-api/src/triggers/handler.rs +++ b/backend/windmill-api/src/triggers/handler.rs @@ -1,6 +1,6 @@ use crate::{ db::ApiAuthed, - triggers::{StandardTriggerQuery, TriggerData}, + triggers::{StandardTriggerQuery, TriggerData, TriggerMode}, }; use async_trait::async_trait; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -143,7 +143,7 @@ pub trait TriggerCrud: Send + Sync + 'static { "email", "edited_at", "extra_perms", - "enabled", + "mode", ]; if Self::SUPPORTS_SERVER_STATE { @@ -206,17 +206,17 @@ pub trait TriggerCrud: Send + Sync + 'static { Ok(deleted > 0) } - async fn set_enabled_extra_action(&self, _: &mut PgConnection) -> Result<()> { + async fn set_trigger_mode_extra_action(&self, _: &mut PgConnection) -> Result<()> { Ok(()) } - async fn set_enabled( + async fn set_trigger_mode( &self, authed: &ApiAuthed, tx: &mut PgConnection, workspace_id: &str, path: &str, - enabled: bool, + mode: &TriggerMode, ) -> Result { let updated = if Self::SUPPORTS_SERVER_STATE { sqlx::query(&format!( @@ -224,7 +224,7 @@ pub trait TriggerCrud: Send + Sync + 'static { UPDATE {} SET - enabled = $1, + mode = $1, email = $2, edited_by = $3, edited_at = now(), @@ -236,7 +236,7 @@ pub trait TriggerCrud: Send + Sync + 'static { "#, Self::TABLE_NAME )) - .bind(enabled) + .bind(mode) .bind(&authed.email) .bind(&authed.username) .bind(workspace_id) @@ -250,7 +250,7 @@ pub trait TriggerCrud: Send + Sync + 'static { UPDATE {} SET - enabled = $1, + mode = $1, email = $2, edited_by = $3, edited_at = now() @@ -260,7 +260,7 @@ pub trait TriggerCrud: Send + Sync + 'static { "#, Self::TABLE_NAME )) - .bind(enabled) + .bind(mode) .bind(&authed.email) .bind(&authed.username) .bind(workspace_id) @@ -270,7 +270,7 @@ pub trait TriggerCrud: Send + Sync + 'static { .rows_affected() }; - self.set_enabled_extra_action(&mut *tx).await?; + self.set_trigger_mode_extra_action(&mut *tx).await?; Ok(updated > 0) } @@ -321,7 +321,7 @@ pub trait TriggerCrud: Send + Sync + 'static { "email", "edited_at", "extra_perms", - "enabled", + "mode", ]; if Self::SUPPORTS_SERVER_STATE { @@ -374,7 +374,7 @@ pub fn trigger_routes() -> Router { .route("/update/*path", post(update_trigger::)) .route("/delete/*path", delete(delete_trigger::)) .route("/exists/*path", get(exists_trigger::)) - .route("/setenabled/*path", post(set_enabled_trigger::)); + .route("/setmode/*path", post(set_trigger_mode::)); if T::SUPPORTS_TEST_CONNECTION { router = router.route("/test", post(test_connection::)); @@ -506,6 +506,7 @@ async fn update_trigger( let mut tx = user_db.begin(&authed).await?; let new_path = edit_trigger.base.path.to_string(); + handler .update_trigger(&db, &mut *tx, &authed, &workspace_id, path, edit_trigger) .await?; @@ -592,24 +593,24 @@ async fn exists_trigger( } #[derive(serde::Deserialize)] -struct SetEnabledPayload { - enabled: bool, +struct SetTriggerModePayload { + mode: TriggerMode, } -async fn set_enabled_trigger( +async fn set_trigger_mode( Extension(handler): Extension>, authed: ApiAuthed, Extension(user_db): Extension, Extension(db): Extension, Path((workspace_id, path)): Path<(String, StripPath)>, - Json(payload): Json, + Json(payload): Json, ) -> Result { let path = path.to_path(); check_scopes(&authed, || format!("{}:write", T::scope_domain_name()))?; let mut tx = user_db.begin(&authed).await?; let updated = handler - .set_enabled(&authed, &mut *tx, &workspace_id, path, payload.enabled) + .set_trigger_mode(&authed, &mut *tx, &workspace_id, path, &payload.mode) .await?; if !updated { @@ -635,10 +636,12 @@ async fn set_enabled_trigger( Ok(format!( "Trigger '{}' {}", path, - if payload.enabled { + if payload.mode == TriggerMode::Enabled { "enabled" - } else { + } else if payload.mode == TriggerMode::Disabled { "disabled" + } else { + "suspended" } )) } @@ -770,6 +773,22 @@ pub fn generate_trigger_routers() -> Router { ); } + { + use crate::triggers::global_handler::{ + cancel_suspended_trigger_jobs, resume_suspended_trigger_jobs, + }; + + router = router + .route( + "/trigger/:trigger_kind/resume_suspended_trigger_jobs/*trigger_path", + post(resume_suspended_trigger_jobs), + ) + .route( + "/trigger/:trigger_kind/cancel_suspended_trigger_jobs/*trigger_path", + post(cancel_suspended_trigger_jobs), + ); + } + router } diff --git a/backend/windmill-api/src/triggers/http/handler.rs b/backend/windmill-api/src/triggers/http/handler.rs index cf1a004c32..a9a8b694fe 100644 --- a/backend/windmill-api/src/triggers/http/handler.rs +++ b/backend/windmill-api/src/triggers/http/handler.rs @@ -16,7 +16,7 @@ use crate::{ get_runnable_format, trigger_runnable, trigger_runnable_and_wait_for_result, trigger_runnable_inner, RunnableId, }, - Trigger, TriggerCrud, TriggerData, + Trigger, TriggerCrud, TriggerData, TriggerMode, }, users::fetch_api_authed, utils::ExpiringCacheEntry, @@ -40,7 +40,8 @@ use windmill_audit::{audit_oss::audit_log, ActionKind}; use windmill_common::{ db::UserDB, error::{Error, Result}, - triggers::TriggerKind, + jobs::JobTriggerKind, + triggers::{TriggerKind, TriggerMetadata}, utils::{not_found_if_none, require_admin, StripPath}, worker::CLOUD_HOSTED, }; @@ -196,7 +197,7 @@ pub async fn insert_new_trigger_into_db( summary, description, is_flow, - enabled, + mode, request_type, authentication_method, http_method, @@ -225,7 +226,7 @@ pub async fn insert_new_trigger_into_db( trigger.config.summary, trigger.config.description, trigger.base.is_flow, - trigger.base.enabled.unwrap_or(true), + trigger.base.mode() as _, request_type as _, trigger.config.authentication_method as _, trigger.config.http_method as _, @@ -483,7 +484,7 @@ impl TriggerCrud for HttpTrigger { script_path = $7, path = $8, is_flow = $9, - enabled = $10, + mode = $10, http_method = $11, static_asset_config = $12, edited_by = $13, @@ -510,7 +511,7 @@ impl TriggerCrud for HttpTrigger { trigger.base.script_path, trigger.base.path, trigger.base.is_flow, - trigger.base.enabled.unwrap_or(true), + trigger.base.mode() as _, trigger.config.http_method as _, trigger.config.static_asset_config as _, &authed.username, @@ -542,7 +543,7 @@ impl TriggerCrud for HttpTrigger { script_path = $4, path = $5, is_flow = $6, - enabled = $7, + mode = $7, http_method = $8, static_asset_config = $9, edited_by = $10, @@ -566,7 +567,7 @@ impl TriggerCrud for HttpTrigger { trigger.base.script_path, trigger.base.path, trigger.base.is_flow, - trigger.base.enabled.unwrap_or(true), + trigger.base.mode() as _, trigger.config.http_method as _, trigger.config.static_asset_config as _, &authed.username, @@ -591,7 +592,7 @@ impl TriggerCrud for HttpTrigger { Ok(()) } - async fn set_enabled_extra_action(&self, tx: &mut PgConnection) -> Result<()> { + async fn set_trigger_mode_extra_action(&self, tx: &mut PgConnection) -> Result<()> { increase_trigger_version(tx).await } @@ -1042,12 +1043,44 @@ async fn route_job( ) .map_err(|e| e.into_response())?; + let trigger_info = TriggerMetadata::new(Some(trigger.path.clone()), JobTriggerKind::Http); + if trigger.mode == TriggerMode::Suspended { + let _ = trigger_runnable( + &db, + Some(user_db), + authed, + &trigger.workspace_id, + &trigger.script_path, + trigger.is_flow, + args, + trigger.retry.as_ref(), + trigger.error_handler_path.as_deref(), + trigger.error_handler_args.as_ref(), + format!("http_trigger/{}", trigger.path), + None, + true, + trigger_info, + ) + .await + .map_err(|e| e.into_response())?; + + return Ok(( + StatusCode::OK, + format!( + "Trigger {} is in suspended mode, jobs are added to the queue but suspended", + &trigger.path + ), + ) + .into_response()); + } + // Handle execution based on the execution mode match trigger.request_type { RequestType::SyncSse => { // Trigger the job (always async when streaming) - let (uuid, _, _) = trigger_runnable_inner( + let (uuid, _, _, _) = trigger_runnable_inner( &db, + None, Some(user_db.clone()), authed.clone(), &trigger.workspace_id, @@ -1059,7 +1092,8 @@ async fn route_job( trigger.error_handler_args.as_ref(), format!("http_trigger/{}", trigger.path), None, - Some(windmill_common::jobs::JobTriggerKind::Http), + trigger_info, + None, ) .await .map_err(|e| e.into_response())?; @@ -1118,7 +1152,8 @@ async fn route_job( trigger.error_handler_args.as_ref(), format!("http_trigger/{}", trigger.path), None, - Some(windmill_common::jobs::JobTriggerKind::Http), + false, + trigger_info, ) .await .map_err(|e| e.into_response()), @@ -1134,7 +1169,7 @@ async fn route_job( trigger.error_handler_path.as_deref(), trigger.error_handler_args.as_ref(), format!("http_trigger/{}", trigger.path), - Some(windmill_common::jobs::JobTriggerKind::Http), + trigger_info, ) .await .map_err(|e| e.into_response()), diff --git a/backend/windmill-api/src/triggers/http/mod.rs b/backend/windmill-api/src/triggers/http/mod.rs index 96944eccad..291d9eadfe 100644 --- a/backend/windmill-api/src/triggers/http/mod.rs +++ b/backend/windmill-api/src/triggers/http/mod.rs @@ -12,7 +12,7 @@ use windmill_common::{ DB, }; -use crate::{db::ApiAuthed, utils::ExpiringCacheEntry}; +use crate::{db::ApiAuthed, triggers::TriggerMode, utils::ExpiringCacheEntry}; pub mod handler; pub mod http_trigger_args; @@ -48,6 +48,7 @@ pub struct TriggerRoute { error_handler_path: Option, error_handler_args: Option>>, retry: Option>, + mode: TriggerMode, } pub struct RoutersCache { @@ -257,12 +258,13 @@ pub async fn refresh_routers(db: &DB) -> Result<(bool, RwLockReadGuard<'_, Route is_static_website, error_handler_path, error_handler_args as "error_handler_args: _", - retry as "retry: _" + retry as "retry: _", + mode as "mode: _" FROM http_trigger WHERE http_method = $1 AND - enabled is TRUE + (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE) "#, &http_method as &HttpMethod ) diff --git a/backend/windmill-api/src/triggers/listener.rs b/backend/windmill-api/src/triggers/listener.rs index 23e0dfd936..613774d919 100644 --- a/backend/windmill-api/src/triggers/listener.rs +++ b/backend/windmill-api/src/triggers/listener.rs @@ -6,7 +6,7 @@ use crate::{ triggers::{ handler::TriggerCrud, trigger_helpers::{trigger_runnable, TriggerJobArgs}, - Trigger, TriggerErrorHandling, + Trigger, TriggerErrorHandling, TriggerMode, }, users::fetch_api_authed, }; @@ -22,7 +22,7 @@ use tokio::sync::RwLock; use windmill_common::{ error::{Error, Result}, jobs::JobTriggerKind, - triggers::TriggerKind, + triggers::{TriggerKind, TriggerMetadata}, utils::report_critical_error, DB, INSTANCE_NAME, }; @@ -34,8 +34,6 @@ pub trait Listener: TriggerCrud + TriggerJobArgs { type Extra: Send + Sync; type ExtraState: Send + Sync; - //to use in next PR to add job trigger kind to eow - #[allow(unused)] const JOB_TRIGGER_KIND: JobTriggerKind; const EXTRA_TRIGGER_AND_WHERE_CLAUSE: &[&'static str] = &[]; const EXTRA_CAPTURE_AND_WHERE_CLAUSE: &[&'static str] = &[]; @@ -69,7 +67,7 @@ pub trait Listener: TriggerCrud + TriggerJobArgs { "email", "edited_at", "extra_perms", - "enabled", + "mode", "error_handler_path", "error_handler_args", "retry", @@ -79,9 +77,11 @@ pub trait Listener: TriggerCrud + TriggerJobArgs { let mut sqlb = SqlBuilder::select_from(Self::TABLE_NAME); - sqlb.fields(&fields).and_where("enabled IS TRUE").and_where( - "(last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')", - ); + sqlb.fields(&fields) + .and_where("(mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE)") + .and_where( + "(last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')", + ); for where_clause in Self::EXTRA_TRIGGER_AND_WHERE_CLAUSE { sqlb.and_where(where_clause); @@ -106,6 +106,7 @@ pub trait Listener: TriggerCrud + TriggerJobArgs { trigger_config: trigger.config, error_handling: Some(trigger.error_handling), trigger_mode: true, + suspended_mode: trigger.base.mode == TriggerMode::Suspended, }) .collect_vec(); @@ -155,6 +156,7 @@ pub trait Listener: TriggerCrud + TriggerJobArgs { trigger_mode: false, is_flow: capture.is_flow, error_handling: None, + suspended_mode: false, }) .collect_vec(); @@ -240,7 +242,7 @@ pub trait Listener: TriggerCrud + TriggerJobArgs { workspace_id = $2 AND path = $3 AND server_id = $4 AND - enabled IS TRUE + (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE) RETURNING 1 "#, Self::TABLE_NAME @@ -385,7 +387,7 @@ pub trait Listener: TriggerCrud + TriggerJobArgs { UPDATE {} SET - enabled = FALSE, + mode = 'disabled'::TRIGGER_MODE, error = $1, server_id = NULL, last_server_ping = NULL @@ -515,7 +517,8 @@ pub trait Listener: TriggerCrud + TriggerJobArgs { error_handler_args, format!("{}_trigger/{}", Self::TRIGGER_KIND, listening_trigger.path), None, - Some(Self::JOB_TRIGGER_KIND), + listening_trigger.suspended_mode, + TriggerMetadata::new(Some(listening_trigger.path.clone()), Self::JOB_TRIGGER_KIND), ) .await?; @@ -648,7 +651,7 @@ async fn listen_to_unlistened_events( last_server_ping = now(), error = 'Connecting...' WHERE - enabled IS TRUE + (mode = 'enabled'::TRIGGER_MODE OR mode = 'suspended'::TRIGGER_MODE) AND workspace_id = $2 AND path = $3 AND (last_server_ping IS NULL @@ -810,6 +813,7 @@ pub struct ListeningTrigger { pub script_path: String, pub trigger_mode: bool, pub error_handling: Option, + pub suspended_mode: bool, } impl ListeningTrigger { diff --git a/backend/windmill-api/src/triggers/mod.rs b/backend/windmill-api/src/triggers/mod.rs index 6c7004dac4..20fec351bb 100644 --- a/backend/windmill-api/src/triggers/mod.rs +++ b/backend/windmill-api/src/triggers/mod.rs @@ -2,6 +2,14 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use sqlx::{types::Json as SqlxJson, FromRow}; use std::{collections::HashMap, fmt::Debug}; +use windmill_common::jobs::JobTriggerKind; + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum HandlerAction { + Trigger { path: String, trigger_kind: JobTriggerKind }, + // Future variants can be added here (e.g., Script, Flow, etc.) +} #[cfg(all(feature = "smtp", feature = "enterprise", feature = "private"))] pub mod email; @@ -22,6 +30,7 @@ pub mod sqs; #[cfg(feature = "websocket")] pub mod websocket; +pub mod global_handler; mod handler; mod listener; pub mod trigger_helpers; @@ -47,7 +56,7 @@ pub struct BaseTrigger { pub workspace_id: String, pub path: String, pub script_path: String, - pub enabled: Option, + pub mode: TriggerMode, pub is_flow: bool, pub edited_by: String, pub email: String, @@ -98,8 +107,10 @@ where T: for<'r> FromRow<'r, sqlx::postgres::PgRow>, { fn from_row(row: &sqlx::postgres::PgRow) -> std::result::Result { + let base = BaseTrigger::from_row(row)?; + Ok(Trigger { - base: BaseTrigger::from_row(row)?, + base, config: T::from_row(row)?, server_state: ServerState::from_row(row).ok(), error_handling: TriggerErrorHandling::from_row(row)?, @@ -112,7 +123,22 @@ pub struct BaseTriggerData { pub path: String, pub script_path: String, pub is_flow: bool, - pub enabled: Option, + #[deprecated(note = "Use mode instead")] + enabled: Option, // Kept for backwards compatibility, use mode instead + mode: Option, +} + +impl BaseTriggerData { + pub fn mode(&self) -> &TriggerMode { + self.mode.as_ref().unwrap_or( + #[allow(deprecated)] + if self.enabled.unwrap_or(true) { + &TriggerMode::Enabled + } else { + &TriggerMode::Disabled + }, + ) + } } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -144,3 +170,12 @@ impl Default for StandardTriggerQuery { Self { page: Some(0), per_page: Some(100), path: None, path_start: None, is_flow: None } } } + +#[derive(sqlx::Type, Serialize, Deserialize, Debug, PartialEq, Clone)] +#[sqlx(type_name = "TRIGGER_MODE", rename_all = "lowercase")] +#[serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))] +pub enum TriggerMode { + Enabled, + Disabled, + Suspended, +} diff --git a/backend/windmill-api/src/triggers/mqtt/handler.rs b/backend/windmill-api/src/triggers/mqtt/handler.rs index fd6054666c..ac4c027fff 100644 --- a/backend/windmill-api/src/triggers/mqtt/handler.rs +++ b/backend/windmill-api/src/triggers/mqtt/handler.rs @@ -96,7 +96,7 @@ impl TriggerCrud for MqttTrigger { script_path, is_flow, email, - enabled, + mode, edited_by, error_handler_path, error_handler_args, @@ -116,7 +116,7 @@ impl TriggerCrud for MqttTrigger { trigger.base.script_path, trigger.base.is_flow, authed.email, - trigger.base.enabled.unwrap_or(true), + trigger.base.mode() as _, authed.username, trigger.error_handling.error_handler_path, trigger.error_handling.error_handler_args as _, diff --git a/backend/windmill-api/src/triggers/postgres/handler.rs b/backend/windmill-api/src/triggers/postgres/handler.rs index 3b0a1b6972..c46441cd0c 100644 --- a/backend/windmill-api/src/triggers/postgres/handler.rs +++ b/backend/windmill-api/src/triggers/postgres/handler.rs @@ -119,7 +119,7 @@ impl TriggerCrud for PostgresTrigger { publication_name, script_path, is_flow, - enabled, + mode, edited_by, email, edited_at, @@ -137,7 +137,7 @@ impl TriggerCrud for PostgresTrigger { pub_name, trigger.base.script_path, trigger.base.is_flow, - trigger.base.enabled.unwrap_or(true), + trigger.base.mode() as _, authed.username, authed.email, trigger.error_handling.error_handler_path, diff --git a/backend/windmill-api/src/triggers/trigger_helpers.rs b/backend/windmill-api/src/triggers/trigger_helpers.rs index b580138191..800113f868 100644 --- a/backend/windmill-api/src/triggers/trigger_helpers.rs +++ b/backend/windmill-api/src/triggers/trigger_helpers.rs @@ -12,13 +12,10 @@ use windmill_common::{ error::Result, flows::{FlowModuleValue, Retry}, get_latest_deployed_hash_for_path, get_latest_flow_version_info_for_path, - jobs::{ - get_has_preprocessor_from_content_and_lang, script_path_to_payload, JobPayload, - JobTriggerKind, - }, + jobs::{get_has_preprocessor_from_content_and_lang, script_path_to_payload, JobPayload}, scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang}, triggers::{ - HubOrWorkspaceId, RunnableFormat, RunnableFormatVersion, TriggerKind, + HubOrWorkspaceId, RunnableFormat, RunnableFormatVersion, TriggerKind, TriggerMetadata, RUNNABLE_FORMAT_VERSION_CACHE, }, users::username_to_permissioned_as, @@ -32,8 +29,9 @@ use crate::jobs::check_license_key_valid; use crate::{ db::{ApiAuthed, DB}, jobs::{ - check_tag_available_for_workspace, delete_job_metadata_after_use, result_to_response, - run_flow_by_path_inner, run_script_by_path_inner, run_wait_result_internal, RunJobQuery, + check_tag_available_for_workspace, delete_job_metadata_after_use, + push_flow_job_by_path_into_queue, push_script_job_by_path_into_queue, result_to_response, + run_wait_result_internal, RunJobQuery, }, utils::check_scopes, HTTP_CLIENT, @@ -506,8 +504,9 @@ pub trait TriggerJobArgs { } #[allow(dead_code)] -pub async fn trigger_runnable_inner( +pub async fn trigger_runnable_inner<'c>( db: &DB, + tx_o: Option>, user_db: Option, authed: ApiAuthed, workspace_id: &str, @@ -519,8 +518,14 @@ pub async fn trigger_runnable_inner( error_handler_args: Option<&sqlx::types::Json>>, trigger_path: String, job_id: Option, - trigger_kind: Option, -) -> Result<(Uuid, Option, Option)> { + trigger: TriggerMetadata, + suspended_mode: Option, +) -> Result<( + Uuid, + Option, + Option, + Option>, +)> { let error_handler_args = error_handler_args.map(|args| { let args = args .0 @@ -531,24 +536,26 @@ pub async fn trigger_runnable_inner( }); let user_db = user_db.unwrap_or_else(|| UserDB::new(db.clone())); - let (uuid, delete_after_use, early_return) = if is_flow { - let run_query = RunJobQuery { job_id, ..Default::default() }; + let (uuid, delete_after_use, early_return, tx_out) = if is_flow { + let run_query = RunJobQuery { job_id, suspended_mode, ..Default::default() }; let path = StripPath(runnable_path.to_string()); - let (uuid, early_return) = run_flow_by_path_inner( + let (uuid, early_return, tx_out) = push_flow_job_by_path_into_queue( authed, db.clone(), + tx_o, user_db, workspace_id.to_string(), path, run_query, args, - trigger_kind, + Some(trigger), ) .await?; - (uuid, None, early_return) + (uuid, None, early_return, tx_out) } else { - let (uuid, delete_after_use) = trigger_script_internal( + let (uuid, delete_after_use, tx_out) = trigger_script_internal( db, + tx_o, user_db, authed, workspace_id, @@ -559,13 +566,14 @@ pub async fn trigger_runnable_inner( error_handler_args.as_ref(), trigger_path, job_id, - trigger_kind, + trigger, + suspended_mode, ) .await?; - (uuid, delete_after_use, None) + (uuid, delete_after_use, None, tx_out) }; - Ok((uuid, delete_after_use, early_return)) + Ok((uuid, delete_after_use, early_return, tx_out)) } #[allow(dead_code)] @@ -582,10 +590,12 @@ pub async fn trigger_runnable( error_handler_args: Option<&sqlx::types::Json>>, trigger_path: String, job_id: Option, - trigger_kind: Option, + suspended_mode: bool, + trigger: TriggerMetadata, ) -> Result { - let (uuid, _, _) = trigger_runnable_inner( + let uuid = trigger_runnable_inner( db, + None, user_db, authed, workspace_id, @@ -597,9 +607,11 @@ pub async fn trigger_runnable( error_handler_args, trigger_path, job_id, - trigger_kind, + trigger, + Some(suspended_mode), ) - .await?; + .await? + .0; Ok((StatusCode::CREATED, uuid.to_string()).into_response()) } @@ -616,11 +628,12 @@ pub async fn trigger_runnable_and_wait_for_result( error_handler_path: Option<&str>, error_handler_args: Option<&sqlx::types::Json>>, trigger_path: String, - trigger_kind: Option, + trigger: TriggerMetadata, ) -> Result { let username = authed.username.clone(); - let (uuid, delete_after_use, early_return) = trigger_runnable_inner( + let (uuid, delete_after_use, early_return, _) = trigger_runnable_inner( db, + None, user_db, authed, workspace_id, @@ -632,7 +645,8 @@ pub async fn trigger_runnable_and_wait_for_result( error_handler_args, trigger_path, None, - trigger_kind, + trigger, + None, ) .await?; let (result, success) = @@ -658,11 +672,12 @@ pub async fn trigger_runnable_and_wait_for_raw_result( error_handler_path: Option<&str>, error_handler_args: Option<&sqlx::types::Json>>, trigger_path: String, - trigger_kind: Option, + trigger: TriggerMetadata, ) -> Result<(Box, bool)> { let username = authed.username.clone(); - let (uuid, delete_after_use, early_return) = trigger_runnable_inner( + let (uuid, delete_after_use, early_return, _) = trigger_runnable_inner( db, + None, user_db, authed, workspace_id, @@ -674,7 +689,8 @@ pub async fn trigger_runnable_and_wait_for_raw_result( error_handler_args, trigger_path, None, - trigger_kind, + trigger, + None, ) .await?; @@ -708,7 +724,7 @@ pub async fn trigger_runnable_and_wait_for_raw_result_with_error_ctx( error_handler_path: Option<&str>, error_handler_args: Option<&sqlx::types::Json>>, trigger_path: String, - trigger_kind: Option, + trigger: TriggerMetadata, ) -> Result> { let (result, success) = trigger_runnable_and_wait_for_raw_result( db, @@ -722,7 +738,7 @@ pub async fn trigger_runnable_and_wait_for_raw_result_with_error_ctx( error_handler_path, error_handler_args, trigger_path, - trigger_kind, + trigger, ) .await?; @@ -737,8 +753,9 @@ pub async fn trigger_runnable_and_wait_for_raw_result_with_error_ctx( } } -async fn trigger_script_internal( +async fn trigger_script_internal<'c>( db: &DB, + tx_o: Option>, user_db: UserDB, authed: ApiAuthed, workspace_id: &str, @@ -749,25 +766,33 @@ async fn trigger_script_internal( error_handler_args: Option<&sqlx::types::Json>>>, trigger_path: String, job_id: Option, - trigger_kind: Option, -) -> Result<(Uuid, Option)> { + trigger: TriggerMetadata, + suspended_mode: Option, +) -> Result<( + Uuid, + Option, + Option>, +)> { if retry.is_none() && error_handler_path.is_none() { - let run_query = RunJobQuery { job_id, ..Default::default() }; + let run_query = RunJobQuery { job_id, suspended_mode, ..Default::default() }; let path = StripPath(script_path.to_string()); - run_script_by_path_inner( + let (uuid, delete_after_use, tx_out) = push_script_job_by_path_into_queue( authed, db.clone(), + tx_o, user_db, workspace_id.to_string(), path, run_query, args, - trigger_kind, + Some(trigger), ) - .await + .await?; + Ok((uuid, delete_after_use, tx_out)) } else { - trigger_script_with_retry_and_error_handler( + let (uuid, delete_after_use, tx_out) = trigger_script_with_retry_and_error_handler( db, + tx_o, user_db, authed, workspace_id, @@ -778,14 +803,17 @@ async fn trigger_script_internal( error_handler_args, trigger_path, job_id, - trigger_kind, + trigger, + suspended_mode, ) - .await + .await?; + Ok((uuid, delete_after_use, tx_out)) } } -async fn trigger_script_with_retry_and_error_handler( +async fn trigger_script_with_retry_and_error_handler<'c>( db: &DB, + tx_o: Option>, user_db: UserDB, authed: ApiAuthed, workspace_id: &str, @@ -796,8 +824,13 @@ async fn trigger_script_with_retry_and_error_handler( error_handler_args: Option<&sqlx::types::Json>>>, trigger_path: String, job_id: Option, - trigger_kind: Option, -) -> Result<(Uuid, Option)> { + trigger: TriggerMetadata, + suspended_mode: Option, +) -> Result<( + Uuid, + Option, + Option>, +)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -821,22 +854,30 @@ async fn trigger_script_with_retry_and_error_handler( check_tag_available_for_workspace(&db, &workspace_id, &tag, &authed).await?; - let (email, permissioned_as, push_authed, tx) = - if let Some(on_behalf_of) = on_behalf_of.as_ref() { - ( - on_behalf_of.email.as_str(), - on_behalf_of.permissioned_as.clone(), - None, - PushIsolationLevel::IsolatedRoot(db.clone()), - ) - } else { - ( - authed.email.as_str(), - username_to_permissioned_as(&authed.username), - Some(authed.clone().into()), - PushIsolationLevel::Isolated(user_db, authed.clone().into()), - ) - }; + let return_tx = tx_o.is_some(); + + let (email, permissioned_as, push_authed, tx) = if let Some(tx) = tx_o { + ( + authed.email.as_str(), + username_to_permissioned_as(&authed.username), + Some(authed.clone().into()), + PushIsolationLevel::Transaction(tx), + ) + } else if let Some(on_behalf_of) = on_behalf_of.as_ref() { + ( + on_behalf_of.email.as_str(), + on_behalf_of.permissioned_as.clone(), + None, + PushIsolationLevel::IsolatedRoot(db.clone()), + ) + } else { + ( + authed.email.as_str(), + username_to_permissioned_as(&authed.username), + Some(authed.clone().into()), + PushIsolationLevel::Isolated(user_db, authed.clone().into()), + ) + }; let push_args = PushArgs { args: &args.args, extra: args.extra }; @@ -865,7 +906,7 @@ async fn trigger_script_with_retry_and_error_handler( priority, tag_override: tag.clone(), apply_preprocessor, - trigger_path: Some(trigger_path), + trigger_path: Some(trigger_path.clone()), concurrency_settings, debouncing_settings, }, @@ -876,7 +917,6 @@ async fn trigger_script_with_retry_and_error_handler( ))) } }; - let (uuid, tx) = push( &db, tx, @@ -905,10 +945,16 @@ async fn trigger_script_with_retry_and_error_handler( false, None, None, - trigger_kind, + Some(trigger), + suspended_mode, ) .await?; - tx.commit().await?; - Ok((uuid, delete_after_use)) + // If we were given a transaction, return it; otherwise commit it + if return_tx { + Ok((uuid, delete_after_use, Some(tx))) + } else { + tx.commit().await?; + Ok((uuid, delete_after_use, None)) + } } diff --git a/backend/windmill-api/src/triggers/websocket/handler.rs b/backend/windmill-api/src/triggers/websocket/handler.rs index 2439be8292..24bea03f34 100644 --- a/backend/windmill-api/src/triggers/websocket/handler.rs +++ b/backend/windmill-api/src/triggers/websocket/handler.rs @@ -100,7 +100,7 @@ impl TriggerCrud for WebsocketTrigger { url, script_path, is_flow, - enabled, + mode, filters, initial_messages, url_runnable_args, @@ -121,7 +121,7 @@ impl TriggerCrud for WebsocketTrigger { trigger.config.url, trigger.base.script_path, trigger.base.is_flow, - trigger.base.enabled.unwrap_or(true), + trigger.base.mode() as _, &filters as _, &initial_messages as _, trigger diff --git a/backend/windmill-api/src/triggers/websocket/listener.rs b/backend/windmill-api/src/triggers/websocket/listener.rs index a89532566a..906f895fa4 100644 --- a/backend/windmill-api/src/triggers/websocket/listener.rs +++ b/backend/windmill-api/src/triggers/websocket/listener.rs @@ -24,6 +24,7 @@ use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, Web use windmill_common::{ error::{to_anyhow, Error, Result}, jobs::JobTriggerKind, + triggers::TriggerMetadata, utils::report_critical_error, worker::to_raw_value, DB, @@ -94,7 +95,7 @@ impl ListeningTrigger { None, None, "".to_string(), // doesn't matter as no retry/error handler - Some(windmill_common::jobs::JobTriggerKind::Websocket), + TriggerMetadata::new(Some(self.path.to_owned()), JobTriggerKind::Websocket), ) .await .map(|r| r.get().to_owned())?; @@ -333,6 +334,7 @@ impl Listener for WebsocketTrigger { trigger_config, script_path, error_handling, + suspended_mode, .. } = listening_trigger; @@ -365,7 +367,26 @@ impl Listener for WebsocketTrigger { ), None => (None, None, None), }; - if let Some(ReturnMessageChannels { send_message_tx, mut killpill_rx }) = extra { + let trigger = TriggerMetadata::new(Some(path.to_owned()), Self::JOB_TRIGGER_KIND); + if *suspended_mode || extra.is_none() { + trigger_runnable( + db, + None, + authed, + &workspace_id, + &script_path, + *is_flow, + args, + retry, + error_handler_path, + error_handler_args, + format!("websocket_trigger/{}", listening_trigger.path), + None, + *suspended_mode, + trigger, + ) + .await?; + } else if let Some(ReturnMessageChannels { send_message_tx, mut killpill_rx }) = extra { let db_ = db.clone(); let url = url.to_owned(); let script_path = script_path.to_owned(); @@ -393,7 +414,7 @@ impl Listener for WebsocketTrigger { error_handler_path.as_deref(), error_handler_args.as_ref(), format!("websocket_trigger/{}", trigger_path), - Some(windmill_common::jobs::JobTriggerKind::Websocket), + trigger, ) => { if let Ok((result, success)) = result { if !success && !can_return_error_result { @@ -416,23 +437,6 @@ impl Listener for WebsocketTrigger { }; tokio::spawn(handle_response_f); - } else { - trigger_runnable( - db, - None, - authed, - &workspace_id, - &script_path, - *is_flow, - args, - retry, - error_handler_path, - error_handler_args, - format!("websocket_trigger/{}", listening_trigger.path), - None, - Some(windmill_common::jobs::JobTriggerKind::Websocket), - ) - .await?; } Ok(()) diff --git a/backend/windmill-api/src/triggers/websocket/mod.rs b/backend/windmill-api/src/triggers/websocket/mod.rs index e19c958423..f20b6334f7 100644 --- a/backend/windmill-api/src/triggers/websocket/mod.rs +++ b/backend/windmill-api/src/triggers/websocket/mod.rs @@ -11,7 +11,8 @@ use serde_json::value::RawValue; use sqlx::{types::Json as SqlxJson, FromRow}; use windmill_common::{ error::{Error, Result}, - triggers::TriggerKind, + jobs::JobTriggerKind, + triggers::{TriggerMetadata, TriggerKind}, worker::to_raw_value, DB, }; @@ -113,7 +114,7 @@ pub async fn get_url_from_runnable_value( None, None, "".to_string(), // doesn't matter as no retry/error handler - Some(windmill_common::jobs::JobTriggerKind::Websocket), + TriggerMetadata::new(Some(path.to_owned()), JobTriggerKind::Websocket), ) .await?; diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index e88fb9f25c..80d0dbe668 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -97,6 +97,15 @@ pub enum JobKind { FlowNode, AppScript, AIAgent, + #[serde(rename = "unassigned_script")] + #[sqlx(rename = "unassigned_script")] + UnassignedScript, + #[serde(rename = "unassigned_flow")] + #[sqlx(rename = "unassigned_flow")] + UnassignedFlow, + #[serde(rename = "unassigned_singlestepflow")] + #[sqlx(rename = "unassigned_singlestepflow")] + UnassignedSinglestepFlow, } #[derive(sqlx::Type, Serialize, Deserialize, Debug, PartialEq, Copy, Clone)] diff --git a/backend/windmill-common/src/triggers.rs b/backend/windmill-common/src/triggers.rs index 43c4ae50fc..51c5ea41a2 100644 --- a/backend/windmill-common/src/triggers.rs +++ b/backend/windmill-common/src/triggers.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use std::fmt; use strum_macros::EnumIter; +use crate::jobs::JobTriggerKind; + #[derive(sqlx::Type, Serialize, Deserialize, Debug, Clone, Eq, PartialEq, Hash, EnumIter)] #[sqlx(type_name = "TRIGGER_KIND", rename_all = "snake_case")] #[serde(rename_all = "snake_case")] @@ -82,3 +84,15 @@ lazy_static! { pub static ref RUNNABLE_FORMAT_VERSION_CACHE: Cache = Cache::new(1000); } + +#[derive(Debug, Clone)] +pub struct TriggerMetadata { + pub trigger_path: Option, + pub trigger_kind: JobTriggerKind, +} + +impl TriggerMetadata { + pub fn new(trigger_path: Option, trigger_kind: JobTriggerKind) -> TriggerMetadata { + TriggerMetadata { trigger_path, trigger_kind } + } +} diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 6dce1bb229..326ee794a5 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -42,6 +42,7 @@ use windmill_common::jobs::{ ConcurrencySettings, ConcurrencySettingsWithCustom, DebouncingSettings, JobTriggerKind, EMAIL_ERROR_HANDLER_USER_EMAIL, }; +use windmill_common::triggers::TriggerMetadata; use windmill_common::utils::{configure_client, now_from_db}; use windmill_common::worker::{Connection, MIN_VERSION_SUPPORTS_DEBOUNCING, SCRIPT_TOKEN_EXPIRY}; @@ -460,6 +461,7 @@ pub async fn push_init_job<'c>( None, None, None, + None, ) .await?; inner_tx.commit().await?; @@ -517,6 +519,7 @@ pub async fn push_periodic_bash_job<'c>( None, None, None, + None, ) .await?; inner_tx.commit().await?; @@ -1364,6 +1367,7 @@ async fn restart_job_if_perpetual_inner( None, None, None, + None, ) .await?; tx.commit().await?; @@ -1930,6 +1934,7 @@ pub async fn push_error_handler<'a, 'c, T: Serialize + Send + Sync>( None, None, None, + None, ) .await?; tx.commit().await?; @@ -3843,7 +3848,7 @@ pub async fn push<'c, 'd>( mut permissioned_as: String, token_prefix: Option<&str>, #[allow(unused_mut)] mut scheduled_for_o: Option>, - schedule_path: Option, + schedule_path: Option, //should be removed in favor of the trigger param below parent_job: Option, root_job: Option, flow_innermost_root_job: Option, @@ -3862,7 +3867,8 @@ pub async fn push<'c, 'd>( // If we know there is already a debounce job, we can use this for debouncing. // NOTE: Only works with dependency jobs triggered by relative imports debounce_job_id_o: Option, - trigger_kind: Option, + trigger: Option, + suspended_mode: Option, ) -> Result<(Uuid, Transaction<'c, Postgres>), Error> { #[cfg(feature = "cloud")] if *CLOUD_HOSTED { @@ -4066,7 +4072,7 @@ pub async fn push<'c, 'd>( runnable_id, runnable_path, raw_code_tuple, - job_kind, + mut job_kind, raw_flow, flow_status, language, @@ -5027,6 +5033,13 @@ pub async fn push<'c, 'd>( } } + let (trigger_path, trigger_kind) = trigger + .map_or_else( + || schedule_path.map(|path| (Some(path), JobTriggerKind::Schedule)), + |trigger| Some((trigger.trigger_path, trigger.trigger_kind)), + ) + .unzip(); + if concurrent_limit.is_some() { insert_concurrency_key( workspace_id, @@ -5102,14 +5115,6 @@ pub async fn push<'c, 'd>( // tracing::error!("Could not insert job_perms for job {job_id}: {err:#}"); // } - let trigger_kind = trigger_kind.or_else(|| { - if schedule_path.is_some() { - Some(JobTriggerKind::Schedule) - } else { - None - } - }); - let root_job = if root_job.is_some() && (root_job == flow_innermost_root_job.or(parent_job).or(Some(job_id))) { @@ -5120,6 +5125,21 @@ pub async fn push<'c, 'd>( root_job }; + let (job_kind, scheduled_for_o) = if suspended_mode.unwrap_or(false) { + let unassigned_job_kind = match job_kind { + JobKind::Script => JobKind::UnassignedScript, + JobKind::Flow => JobKind::UnassignedFlow, + JobKind::SingleStepFlow => JobKind::UnassignedSinglestepFlow, + _ => return Err(Error::internal_err(format!("Cannot suspend job of kind {job_kind:?} as it is not a script, flow or single step flow"))), + }; + ( + unassigned_job_kind, + Some(Utc::now() + chrono::Duration::days(30)), + ) + } else { + (job_kind, scheduled_for_o) + }; + sqlx::query!( "WITH inserted_job AS ( INSERT INTO v2_job (id, workspace_id, raw_code, raw_lock, raw_flow, tag, parent_job, @@ -5155,7 +5175,7 @@ pub async fn push<'c, 'd>( runnable_path.clone(), Json(args) as Json, job_kind.clone() as JobKind, - schedule_path, + trigger_path.flatten(), language as Option, same_worker, pre_run_error.map(|e| e.to_string()), @@ -5260,6 +5280,9 @@ pub async fn push<'c, 'd>( JobKind::FlowNode => "jobs.run.flow_node", JobKind::AppScript => "jobs.run.app_script", JobKind::AIAgent => "jobs.run.ai_agent", + JobKind::UnassignedScript => "jobs.run.unassigned_script", + JobKind::UnassignedFlow => "jobs.run.unassigned_flow", + JobKind::UnassignedSinglestepFlow => "jobs.run.unassigned_singlestepflow", }; let audit_author = if format!("u/{user}") != permissioned_as && user != permissioned_as { diff --git a/backend/windmill-queue/src/schedule.rs b/backend/windmill-queue/src/schedule.rs index 16358b453e..658ba8e463 100644 --- a/backend/windmill-queue/src/schedule.rs +++ b/backend/windmill-queue/src/schedule.rs @@ -23,8 +23,10 @@ use windmill_common::jobs::check_tag_available_for_workspace_internal; use windmill_common::jobs::ConcurrencySettings; use windmill_common::jobs::DebouncingSettings; use windmill_common::jobs::JobPayload; +use windmill_common::jobs::JobTriggerKind; use windmill_common::schedule::schedule_to_user; use windmill_common::scripts::ScriptHash; +use windmill_common::triggers::TriggerMetadata; use windmill_common::utils::WarnAfterExt; use windmill_common::worker::to_raw_value; use windmill_common::FlowVersionInfo; @@ -506,7 +508,11 @@ pub async fn push_scheduled_job<'c>( false, None, None, - Some(windmill_common::jobs::JobTriggerKind::Schedule), + Some(TriggerMetadata::new( + Some(schedule.path.clone()), + JobTriggerKind::Schedule, + )), + None, ) .warn_after_seconds_with_sql(1, "push in push_scheduled_job".to_string()) .await?; diff --git a/backend/windmill-worker/src/ai/tools.rs b/backend/windmill-worker/src/ai/tools.rs index 638129e34d..a0755aeee7 100644 --- a/backend/windmill-worker/src/ai/tools.rs +++ b/backend/windmill-worker/src/ai/tools.rs @@ -459,6 +459,7 @@ async fn execute_windmill_tool( None, None, None, + None, ) .await?; diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index 1ffb0f11d9..afee5e6ba5 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -982,7 +982,6 @@ pub fn start_interactive_worker_shell( Connection::Sql(db) => { let common_worker_prefix = retrieve_common_worker_prefix(&worker_name); let query = ("".to_string(), make_pull_query(&[common_worker_prefix])); - #[cfg(feature = "benchmark")] let mut bench = windmill_common::bench::BenchmarkIter::new(); let job = pull( @@ -1025,7 +1024,6 @@ pub fn start_interactive_worker_shell( match pulled_job { Ok(Some(job)) => { tracing::debug!(worker = %worker_name, hostname = %hostname, "started handling of job {}", job.id); - let job_dir = create_job_dir(&worker_dir, job.id).await; #[cfg(feature = "benchmark")] let mut bench = windmill_common::bench::BenchmarkIter::new(); @@ -2640,6 +2638,13 @@ pub async fn handle_queued_job( return Err(Error::ExecutionErr(e.to_string())); } + match job.kind { + JobKind::UnassignedScript | JobKind::UnassignedFlow | JobKind::UnassignedSinglestepFlow => { + return Err(Error::ExecutionErr("Suspended job was not handled by the user within 30 days, job will not be executed.".to_string())); + } + _ => {} + } + #[cfg(any(not(feature = "enterprise"), feature = "sqlx"))] match conn { Connection::Sql(db) => { @@ -3168,6 +3173,9 @@ async fn try_validate_schema( JobKind::Noop => 13, JobKind::FlowNode => 14, JobKind::AIAgent => 15, + JobKind::UnassignedScript => 16, + JobKind::UnassignedFlow => 17, + JobKind::UnassignedSinglestepFlow => 18, }; let sv = match job.runnable_id { diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index fdbc9ba636..5b8ce19d92 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -3339,6 +3339,7 @@ async fn push_next_flow_job( None, None, None, + None, ) .warn_after_seconds(2) .await?; diff --git a/backend/windmill-worker/src/worker_lockfiles.rs b/backend/windmill-worker/src/worker_lockfiles.rs index 56a3c82fe2..68eb02353d 100644 --- a/backend/windmill-worker/src/worker_lockfiles.rs +++ b/backend/windmill-worker/src/worker_lockfiles.rs @@ -660,6 +660,7 @@ pub async fn trigger_dependents_to_recompute_dependencies( None, debounce_job_id_o, None, + None, ) .await?; diff --git a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte index a21307977b..01ec14221c 100644 --- a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte +++ b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte @@ -1,3 +1,24 @@ + + + +{#if includeModalConfig && triggerMode === 'suspended'} + +{/if} + +
+ {#if triggerMode === 'suspended'} + { + if (value === 'disabled' || value === 'enabled') { + const hasJobs = await suspendedJobsModal?.hasJobs() + if (hasJobs) { + innerTriggerMode = 'suspended' + suspendedJobsModal?.openModal(value === 'disabled' ? 'disable' : 'enable') + } else { + onToggleMode(value) + } + } + }} + bind:selected={innerTriggerMode} + > + {#snippet children({ item })} + + + + {/snippet} + + {:else} + { + onToggleMode(e.detail ? 'enabled' : 'disabled') + }} + /> + {#if !hideDropdown} + { + triggerMode = 'suspended' + onToggleMode?.('suspended') + }, + tooltip: + 'When a trigger is in suspended mode, it will continue to accept payloads and queue jobs, but those jobs will not run automatically. You can review the list of suspended jobs, and resume or cancel them individually.' + } + ]} + /> + {/if} + {/if} +
diff --git a/frontend/src/lib/components/triggers/TriggerSuspendedJobsAlert.svelte b/frontend/src/lib/components/triggers/TriggerSuspendedJobsAlert.svelte new file mode 100644 index 0000000000..3f88555689 --- /dev/null +++ b/frontend/src/lib/components/triggers/TriggerSuspendedJobsAlert.svelte @@ -0,0 +1,27 @@ + + + +
+ When a trigger is in suspended mode, it will continue to accept payloads and queue jobs, but + those jobs will not run automatically. You can review the list of suspended jobs, and resume or + cancel them individually. + +
+
diff --git a/frontend/src/lib/components/triggers/TriggerSuspendedJobsModal.svelte b/frontend/src/lib/components/triggers/TriggerSuspendedJobsModal.svelte new file mode 100644 index 0000000000..2985a38fdb --- /dev/null +++ b/frontend/src/lib/components/triggers/TriggerSuspendedJobsModal.svelte @@ -0,0 +1,523 @@ + + + + +{#snippet runnable({ + path, + kind, + outdated +}: { + path?: string + kind: QueuedJob['job_kind'] + outdated?: boolean +})} + {@const JobKindIcon = getJobKindIcon(kind)} +
+ + + {#snippet text()} + {mapJobKindToRunnableKind(kind)} + {/snippet} + +
{path || '-'}
+
+{/snippet} + + +
+ {#if action !== 'none'} + + You can only {action} the trigger when there are no more suspended jobs for this trigger. + + {/if} + {#if queuedJobs.length > 0 && hasChanged} + + You can only resume suspended jobs when the trigger has no unsaved changes. + + {:else if queuedJobs.length > 0} + + If you modify the trigger's configuration (such as changing the runnable, retry settings, or + error handler), resumed jobs will be run using the updated configuration. + + {/if} + +
+ + + + +
+ +
+
+ Created at + Runnable + Cancellation date + + + {#if loading} + + {#each new Array(3) as _} + + {#each new Array(4) as _} + + + + {/each} + + {/each} + + {:else if queuedJobs.length === 0} +
+

No suspended jobs found.

+
+ {:else} + + {#each queuedJobs as job} + {@const changedRunnable = + runnableConfig.kind !== mapJobKindToRunnableKind(job.job_kind) || + runnableConfig.path !== job.script_path} + + window.open(`/run/${job.id}?workspace=${workspace}`, '_blank')} + > + +
+ toggleJobSelection(job.id)} + disabled={processingAction} + class="cursor-pointer" + /> +
+
+ + {displayDate(job.created_at)} + + + {@render runnable({ + path: job.script_path, + kind: job.job_kind, + outdated: changedRunnable + })} + {#if changedRunnable} + {@render runnable({ + path: runnableConfig.path, + kind: runnableConfig.kind + })} + {/if} + {#await checkIfAdvancedOptionsChanged(job) then changedAdvancedOptions} + {#if changedAdvancedOptions} + Changed retry/error handler + {/if} + {/await} + + {displayDate(job.scheduled_for)} +
+ {/each} + + {/if} +
+
+ + +
+ {#if hasSelectedJobs} + + + + {:else} + + + {/if} +
+
+
diff --git a/frontend/src/lib/components/triggers/email/EmailTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/email/EmailTriggerEditorInner.svelte index 4e8a37e9e0..815c8470d6 100644 --- a/frontend/src/lib/components/triggers/email/EmailTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/email/EmailTriggerEditorInner.svelte @@ -10,10 +10,11 @@ type ErrorHandler, type EmailTrigger, type NewEmailTrigger, - type Retry + type Retry, + type TriggerMode } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' - import { canWrite, emptyString, sendUserToast } from '$lib/utils' + import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils' import Section from '$lib/components/Section.svelte' import { Loader2 } from 'lucide-svelte' import Label from '$lib/components/Label.svelte' @@ -25,6 +26,9 @@ import Tab from '$lib/components/common/tabs/Tab.svelte' import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' import { saveEmailTriggerFromCfg } from './utils' + import { deepEqual } from 'fast-equals' + import TriggerSuspendedJobsAlert from '../TriggerSuspendedJobsAlert.svelte' + import TriggerSuspendedJobsModal from '../TriggerSuspendedJobsModal.svelte' let { useDrawer = true, @@ -65,18 +69,28 @@ let error_handler_path: string | undefined = $state() let error_handler_args: Record = $state({}) let retry: Retry | undefined = $state() - let enabled = $state(false) + let mode = $state('enabled') // Component references let drawer = $state(undefined) let initialConfig: NewEmailTrigger | undefined = undefined let deploymentLoading = $state(false) let optionTabSelected: 'error_handler' | 'retries' = $state('error_handler') let errorHandlerSelected: ErrorHandler = $state('slack') + + let suspendedJobsModal = $state(null) + let originalConfig = $state(undefined) + + let hasChanged = $derived(!deepEqual(getEmailTriggerConfig(), originalConfig ?? {})) const isAdmin = $derived($userStore?.is_admin || $userStore?.is_super_admin) const emailConfig = $derived.by(getEmailTriggerConfig) const captureConfig = $derived.by(isEditor ? getCaptureConfig : () => ({})) const saveDisabled = $derived( - drawerLoading || !can_write || pathError != '' || !isValid || emptyString(script_path) + drawerLoading || + !can_write || + pathError != '' || + !isValid || + emptyString(script_path) || + !hasChanged ) $effect(() => { @@ -101,6 +115,7 @@ dirtyPath = false dirtyLocalPart = false await loadTrigger(defaultConfig) + originalConfig = structuredClone($state.snapshot(getEmailTriggerConfig())) } catch (err) { sendUserToast(`Could not load email trigger: ${err}`, true) } finally { @@ -142,7 +157,8 @@ error_handler_args = defaultValues?.error_handler_args ?? {} retry = defaultValues?.retry ?? undefined errorHandlerSelected = getHandlerType(error_handler_path ?? '') - enabled = defaultValues?.enabled ?? false + mode = defaultValues?.mode ?? 'enabled' + originalConfig = undefined } finally { clearTimeout(loader) drawerLoading = false @@ -163,7 +179,7 @@ error_handler_args = cfg?.error_handler_args ?? {} retry = cfg?.retry errorHandlerSelected = getHandlerType(error_handler_path ?? '') - enabled = cfg?.enabled ?? false + mode = cfg?.mode ?? 'enabled' } async function loadTrigger(defaultConfig?: Partial): Promise { @@ -197,7 +213,12 @@ ) if (isSaved) { onUpdate(saveCfg.path) - drawer?.closeDrawer() + originalConfig = structuredClone($state.snapshot(getEmailTriggerConfig())) + initialPath = saveCfg.path + initialScriptPath = saveCfg.script_path + if (mode !== 'suspended') { + drawer?.closeDrawer() + } } deploymentLoading = false } @@ -214,21 +235,26 @@ error_handler_path, error_handler_args, retry, - enabled + mode } return nCfg } - async function handleToggleEnabled(newEnabled: boolean) { - enabled = newEnabled + async function handleToggleMode(newMode: TriggerMode) { + mode = newMode if (!trigger?.draftConfig) { - await EmailTriggerService.setEmailTriggerEnabled({ + await EmailTriggerService.setEmailTriggerMode({ path: initialPath, workspace: $workspaceStore ?? '', - requestBody: { enabled: newEnabled } + requestBody: { mode: newMode } }) - sendUserToast(`${newEnabled ? 'enabled' : 'disabled'} email trigger ${initialPath}`) + sendUserToast(`${capitalize(newMode)} email trigger ${initialPath}`) + + onUpdate(initialPath) + } + if (originalConfig) { + originalConfig['mode'] = newMode } } @@ -254,6 +280,23 @@ }) +{#if mode === 'suspended'} + +{/if} + {#snippet config()} {#if drawerLoading} {#if showLoader} @@ -261,6 +304,9 @@ {/if} {:else}
+ {#if mode === 'suspended'} + + {/if}
{:else}
+ {#if mode === 'suspended'} + + {/if} {#if description} {@render description()} {/if} diff --git a/frontend/src/lib/components/triggers/gcp/utils.ts b/frontend/src/lib/components/triggers/gcp/utils.ts index eb8430786f..ec8a93942e 100644 --- a/frontend/src/lib/components/triggers/gcp/utils.ts +++ b/frontend/src/lib/components/triggers/gcp/utils.ts @@ -28,7 +28,7 @@ export async function saveGcpTriggerFromCfg( topic_id: cfg.topic_id, path: cfg.path, script_path: cfg.script_path, - enabled: cfg.enabled, + mode: cfg.mode, is_flow: cfg.is_flow, auto_acknowledge_msg: cfg.auto_acknowledge_msg, ack_deadline: cfg.ack_deadline, @@ -46,7 +46,7 @@ export async function saveGcpTriggerFromCfg( workspace: workspace, requestBody: { ...requestBody, - enabled: true + mode: 'enabled' } }) sendUserToast(`GCP Pub/Sub trigger ${cfg.path} created`) diff --git a/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte b/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte index b17ac5eeb2..e2766fc7fa 100644 --- a/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte +++ b/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte @@ -12,10 +12,11 @@ type ErrorHandler, type HttpTrigger, type NewHttpTrigger, - type Retry + type Retry, + type TriggerMode } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' - import { canWrite, emptyString, sendUserToast } from '$lib/utils' + import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils' import Section from '$lib/components/Section.svelte' import { Loader2, Pipette, Plus } from 'lucide-svelte' import Label from '$lib/components/Label.svelte' @@ -45,6 +46,9 @@ import Tabs from '$lib/components/common/tabs/Tabs.svelte' import Tab from '$lib/components/common/tabs/Tab.svelte' import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' + import { deepEqual } from 'fast-equals' + import TriggerSuspendedJobsAlert from '../TriggerSuspendedJobsAlert.svelte' + import TriggerSuspendedJobsModal from '../TriggerSuspendedJobsModal.svelte' let { useDrawer = true, @@ -83,7 +87,7 @@ let static_asset_config = $state<{ s3: string; storage?: string; filename?: string } | undefined>( undefined ) - let enabled: boolean = $state(false) + let mode = $state('enabled') let is_static_website = $state(false) let s3FileUploadRawMode = $state(false) let workspaced_route = $state(false) @@ -111,6 +115,12 @@ let deploymentLoading = $state(false) let optionTabSelected: 'request_options' | 'error_handler' | 'retries' = $state('request_options') let errorHandlerSelected: ErrorHandler = $state('slack') + + let suspendedJobsModal = $state(null) + let originalConfig = $state(undefined) + + let hasChanged = $derived(!deepEqual(getRouteConfig(), originalConfig ?? {})) + const isAdmin = $derived($userStore?.is_admin || $userStore?.is_super_admin) const routeConfig = $derived.by(getRouteConfig) const captureConfig = $derived.by(isEditor ? getCaptureConfig : () => ({})) @@ -119,7 +129,8 @@ !can_write || pathError != '' || !isValid || - (!static_asset_config && emptyString(script_path)) + (!static_asset_config && emptyString(script_path)) || + !hasChanged ) $effect(() => { @@ -194,6 +205,7 @@ dirtyPath = false dirtyRoutePath = false await loadTrigger(defaultConfig) + originalConfig = structuredClone($state.snapshot(getRouteConfig())) } catch (err) { sendUserToast(`Could not load route: ${err}`, true) } finally { @@ -236,7 +248,7 @@ s3FileUploadRawMode = defaultValues?.s3FileUploadRawMode ?? false path = defaultValues?.path ?? '' initialPath = '' - enabled = defaultValues?.enabled ?? false + mode = defaultValues?.mode ?? 'enabled' dirtyPath = false is_static_website = defaultValues?.is_static_website ?? false workspaced_route = defaultValues?.workspaced_route ?? false @@ -251,6 +263,7 @@ error_handler_args = defaultValues?.error_handler_args ?? {} retry = defaultValues?.retry ?? undefined errorHandlerSelected = getHandlerType(error_handler_path ?? '') + originalConfig = undefined } finally { clearTimeout(loader) drawerLoading = false @@ -270,7 +283,7 @@ wrap_body = cfg?.wrap_body ?? false raw_string = cfg?.raw_string ?? false summary = cfg?.summary ?? '' - enabled = cfg?.enabled ?? false + mode = cfg?.mode ?? 'enabled' routeDescription = cfg?.description ?? '' authentication_resource_path = cfg?.authentication_resource_path ?? '' if (cfg?.authentication_method === 'custom_script') { @@ -324,7 +337,12 @@ ) if (isSaved) { onUpdate(saveCfg.path) - drawer?.closeDrawer() + originalConfig = structuredClone($state.snapshot(getRouteConfig())) + initialPath = saveCfg.path + initialScriptPath = saveCfg.script_path + if (mode !== 'suspended') { + drawer?.closeDrawer() + } } deploymentLoading = false } @@ -347,7 +365,7 @@ http_method, request_type, workspaced_route, - enabled, + mode, wrap_body, raw_string, authentication_resource_path, @@ -365,15 +383,20 @@ return nCfg } - async function handleToggleEnabled(newEnabled: boolean) { - enabled = newEnabled + async function handleToggleMode(newMode: TriggerMode) { + mode = newMode if (!trigger?.draftConfig) { - await HttpTriggerService.setHttpTriggerEnabled({ + await HttpTriggerService.setHttpTriggerMode({ path: initialPath, workspace: $workspaceStore ?? '', - requestBody: { enabled: newEnabled } + requestBody: { mode: newMode } }) - sendUserToast(`${newEnabled ? 'enabled' : 'disabled'} HTTP trigger ${initialPath}`) + sendUserToast(`${capitalize(newMode)} HTTP trigger ${initialPath}`) + + onUpdate(initialPath) + } + if (originalConfig) { + originalConfig['mode'] = newMode } } @@ -415,6 +438,23 @@ /> {/if} +{#if mode === 'suspended'} + +{/if} + {#snippet config()} {#if drawerLoading} {#if showLoader} @@ -422,6 +462,9 @@ {/if} {:else}
+ {#if mode === 'suspended'} + + {/if}
+ {#if mode === 'suspended'} + + {/if}
+ {#if mode === 'suspended'} + + {/if}
+ {#if mode === 'suspended'} + + {/if}
+ {#if mode === 'suspended'} + + {/if}
{/if} +
{#snippet badge()} {#if isEditor} diff --git a/frontend/src/lib/components/triggers/postgres/utils.ts b/frontend/src/lib/components/triggers/postgres/utils.ts index bfe143642b..3c01b53152 100644 --- a/frontend/src/lib/components/triggers/postgres/utils.ts +++ b/frontend/src/lib/components/triggers/postgres/utils.ts @@ -114,7 +114,7 @@ export async function savePostgresTriggerFromCfg( ? { error_handler_path: config.error_handler_path, error_handler_args: config.error_handler_path ? config.error_handler_args : undefined, - retry: config.retry, + retry: config.retry } : {} const requestBody: EditPostgresTrigger = { @@ -125,7 +125,7 @@ export async function savePostgresTriggerFromCfg( replication_slot_name: config.replication_slot_name, publication_name: config.publication_name, publication: config.publication, - enabled: config.enabled, + mode: config.mode, ...errorHandlerAndRetries } if (edit) { @@ -140,7 +140,7 @@ export async function savePostgresTriggerFromCfg( workspace: workspace, requestBody: { ...requestBody, - enabled: true + mode: 'enabled' } }) sendUserToast(`PostgresTrigger ${config.path} created`) diff --git a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte index 0eb998be30..0b7734b8d5 100644 --- a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte +++ b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte @@ -617,6 +617,7 @@ requestBody: { enabled: nEnabled } }) sendUserToast(`${nEnabled ? 'enabled' : 'disabled'} schedule ${initialPath}`) + onUpdate?.(initialPath) } } @@ -634,15 +635,16 @@ {trigger} permissions={drawerLoading || !can_write ? 'none' : 'create'} {saveDisabled} - {enabled} + mode={enabled ? 'enabled' : 'disabled'} {allowDraft} {edit} isLoading={deploymentLoading} onUpdate={scheduleScript} {onReset} {onDelete} - onToggleEnabled={handleToggleEnabled} + onToggleMode={(mode) => handleToggleEnabled(mode === 'enabled')} {isDeployed} + disableSuspendedMode > {#snippet extra()} {#if !drawerLoading && edit} diff --git a/frontend/src/lib/components/triggers/sqs/SqsTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/sqs/SqsTriggerEditorInner.svelte index 26553ec080..f688438789 100644 --- a/frontend/src/lib/components/triggers/sqs/SqsTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/sqs/SqsTriggerEditorInner.svelte @@ -4,14 +4,15 @@ import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte' import Path from '$lib/components/Path.svelte' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' - import { canWrite, emptyString, sendUserToast } from '$lib/utils' + import { canWrite, capitalize, emptyString, sendUserToast } from '$lib/utils' import { Loader2 } from 'lucide-svelte' import Label from '$lib/components/Label.svelte' import { SqsTriggerService, type AwsAuthResourceType, type ErrorHandler, - type Retry + type Retry, + type TriggerMode } from '$lib/gen' import SqsTriggerEditorConfigSection from './SqsTriggerEditorConfigSection.svelte' import Section from '$lib/components/Section.svelte' @@ -24,6 +25,9 @@ import Tabs from '$lib/components/common/tabs/Tabs.svelte' import Tab from '$lib/components/common/tabs/Tab.svelte' import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' + import TriggerSuspendedJobsAlert from '../TriggerSuspendedJobsAlert.svelte' + import TriggerSuspendedJobsModal from '../TriggerSuspendedJobsModal.svelte' + import { deepEqual } from 'fast-equals' interface Props { useDrawer?: boolean @@ -71,7 +75,7 @@ let fixedScriptPath = $state('') let path: string = $state('') let pathError = $state('') - let enabled = $state(false) + let mode = $state('enabled') let dirtyPath = $state(false) let can_write = $state(true) let drawerLoading = $state(true) @@ -88,11 +92,14 @@ let error_handler_path: string | undefined = $state() let error_handler_args: Record = $state({}) let retry: Retry | undefined = $state() + let suspendedJobsModal = $state(null) + let originalConfig = $state | undefined>(undefined) const sqsConfig = $derived.by(getSaveCfg) const captureConfig = $derived.by(getCaptureConfig) + const hasChanged = $derived(!deepEqual(sqsConfig, originalConfig ?? {})) const saveDisabled = $derived( - pathError != '' || emptyString(script_path) || !isValid || !can_write + pathError != '' || emptyString(script_path) || !isValid || !can_write || !hasChanged ) $effect(() => { is_flow = itemKind === 'flow' @@ -114,9 +121,13 @@ edit = true dirtyPath = false await loadTrigger(defaultConfig) + originalConfig = structuredClone($state.snapshot(getSaveCfg())) } catch (err) { sendUserToast(`Could not load sqs trigger: ${err.body}`, true) } finally { + if (!defaultConfig) { + initialConfig = structuredClone($state.snapshot(getSaveCfg())) + } clearTimeout(loadingTimeout) drawerLoading = false showLoading = false @@ -147,11 +158,12 @@ initialPath = '' edit = false dirtyPath = false - enabled = defaultValues?.enabled ?? false + mode = defaultValues?.mode ?? 'enabled' error_handler_path = defaultValues?.error_handler_path ?? undefined error_handler_args = defaultValues?.error_handler_args ?? {} retry = defaultValues?.retry ?? undefined errorHandlerSelected = getHandlerType(error_handler_path ?? '') + originalConfig = undefined } finally { initialConfig = structuredClone($state.snapshot(getSaveCfg())) clearTimeout(loadingTimeout) @@ -169,7 +181,7 @@ is_flow = cfg?.is_flow message_attributes = cfg?.message_attributes ?? [] path = cfg?.path - enabled = cfg?.enabled + mode = cfg?.mode ?? 'enabled' aws_auth_resource_type = cfg?.aws_auth_resource_type can_write = canWrite(cfg?.path, cfg?.extra_perms, $userStore) error_handler_path = cfg?.error_handler_path @@ -207,22 +219,27 @@ queue_url, message_attributes, aws_auth_resource_type, - enabled, + mode, error_handler_path, error_handler_args, retry } } - async function handleToggleEnabled(nEnabled: boolean) { - enabled = nEnabled + async function handleToggleMode(newMode: TriggerMode) { + mode = newMode if (!trigger?.draftConfig) { - await SqsTriggerService.setSqsTriggerEnabled({ + await SqsTriggerService.setSqsTriggerMode({ path: initialPath, workspace: $workspaceStore ?? '', - requestBody: { enabled: nEnabled } + requestBody: { mode: newMode } }) - sendUserToast(`${nEnabled ? 'enabled' : 'disabled'} SQS trigger ${initialPath}`) + sendUserToast(`${capitalize(newMode)} SQS trigger ${initialPath}`) + + onUpdate?.(initialPath) + } + if (originalConfig) { + originalConfig['mode'] = newMode } } @@ -238,7 +255,12 @@ ) if (isSaved) { onUpdate?.(cfg.path) - drawer?.closeDrawer() + originalConfig = structuredClone($state.snapshot(getSaveCfg())) + initialPath = cfg.path + initialScriptPath = cfg.script_path + if (mode !== 'suspended') { + drawer?.closeDrawer() + } } deploymentLoading = false } @@ -265,6 +287,23 @@ }) +{#if mode === 'suspended'} + +{/if} + {#if useDrawer} {/if} @@ -325,6 +365,9 @@ {#if description} {@render description()} {/if} + {#if mode === 'suspended'} + + {/if} {#if !hideTooltips} {#if edit} diff --git a/frontend/src/lib/components/triggers/sqs/utils.ts b/frontend/src/lib/components/triggers/sqs/utils.ts index 61a00b9858..1481639f74 100644 --- a/frontend/src/lib/components/triggers/sqs/utils.ts +++ b/frontend/src/lib/components/triggers/sqs/utils.ts @@ -13,7 +13,7 @@ export async function saveSqsTriggerFromCfg( ? { error_handler_path: cfg.error_handler_path, error_handler_args: cfg.error_handler_path ? cfg.error_handler_args : undefined, - retry: cfg.retry, + retry: cfg.retry } : {} const requestBody: EditSqsTrigger = { @@ -24,7 +24,7 @@ export async function saveSqsTriggerFromCfg( queue_url: cfg.queue_url, message_attributes: cfg.message_attributes, aws_auth_resource_type: cfg.aws_auth_resource_type, - enabled: cfg.enabled, + mode: cfg.mode, ...errorHandlerAndRetries } try { @@ -38,7 +38,7 @@ export async function saveSqsTriggerFromCfg( } else { await SqsTriggerService.createSqsTrigger({ workspace, - requestBody: { ...requestBody, enabled: true } + requestBody: { ...requestBody, mode: 'enabled' } }) sendUserToast(`SQS trigger ${cfg.path} created`) } diff --git a/frontend/src/lib/components/triggers/utils.ts b/frontend/src/lib/components/triggers/utils.ts index 2d060589b4..f24f899ad6 100644 --- a/frontend/src/lib/components/triggers/utils.ts +++ b/frontend/src/lib/components/triggers/utils.ts @@ -8,6 +8,7 @@ import type { CaptureTriggerKind, ErrorHandler, Flow, + JobTriggerKind, NewScript, TriggersCount } from '$lib/gen/types.gen' @@ -54,8 +55,9 @@ export type TriggerType = | 'poll' | 'cli' -export const jobTriggerTypes = [ +export const jobTriggerKinds: JobTriggerKind[] = [ 'webhook', + 'default_email', 'http', 'websocket', 'kafka', @@ -66,9 +68,7 @@ export const jobTriggerTypes = [ 'postgres', 'schedule', 'gcp' -] as const - -export type JobTriggerType = (typeof jobTriggerTypes)[number] +] export type Trigger = { type: TriggerType diff --git a/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte index 7643c2ce5a..36e7481b33 100644 --- a/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte @@ -14,7 +14,8 @@ type ScriptArgs, type WebsocketTriggerInitialMessage, type Retry, - type ErrorHandler + type ErrorHandler, + type TriggerMode } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptySchema, emptyString, sendUserToast } from '$lib/utils' @@ -34,6 +35,10 @@ import Tabs from '$lib/components/common/tabs/Tabs.svelte' import Tab from '$lib/components/common/tabs/Tab.svelte' import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' + import { deepEqual } from 'fast-equals' + import TriggerSuspendedJobsAlert from '../TriggerSuspendedJobsAlert.svelte' + import TriggerSuspendedJobsModal from '../TriggerSuspendedJobsModal.svelte' + import { capitalize } from '$lib/utils' interface Props { useDrawer?: boolean @@ -84,7 +89,6 @@ let pathError = $state('') let url = $state('') let dirtyUrl = $state(false) - let enabled = $state(false) let filters: { key: string value: any @@ -105,7 +109,12 @@ let error_handler_path: string | undefined = $state() let error_handler_args: Record = $state({}) let retry: Retry | undefined = $state() + let mode = $state('enabled') + let suspendedJobsModal = $state(null) + let originalConfig = $state | undefined>(undefined) + + let hasChanged = $derived(!deepEqual(getSaveCfg(), originalConfig ?? {})) const websocketCfg = $derived.by(getSaveCfg) const captureConfig = $derived.by(isEditor ? getCaptureConfig : () => ({})) const saveDisabled = $derived.by(() => { @@ -121,7 +130,8 @@ invalidInitialMessages || drawerLoading || !can_write || - emptyString(script_path) + emptyString(script_path) || + !hasChanged ) }) @@ -152,6 +162,7 @@ dirtyPath = false dirtyUrl = false await loadTrigger(defaultConfig) + originalConfig = structuredClone($state.snapshot(getSaveCfg())) } catch (err) { sendUserToast(`Could not load websocket trigger: ${err}`, true) } finally { @@ -195,6 +206,8 @@ error_handler_args = defaultValues?.error_handler_args ?? {} retry = defaultValues?.retry ?? undefined errorHandlerSelected = getHandlerType(error_handler_path ?? '') + mode = defaultValues?.mode ?? 'enabled' + originalConfig = undefined } finally { clearTimeout(loadingTimeout) drawerLoading = false @@ -208,7 +221,6 @@ is_flow = cfg?.is_flow path = cfg?.path url = cfg?.url - enabled = cfg?.enabled filters = cfg?.filters initial_messages = cfg?.initial_messages ?? [] url_runnable_args = cfg?.url_runnable_args @@ -219,12 +231,12 @@ error_handler_args = cfg?.error_handler_args ?? {} retry = cfg?.retry errorHandlerSelected = getHandlerType(error_handler_path ?? '') + mode = cfg?.mode ?? 'enabled' } function getSaveCfg() { return { script_path, - initialScriptPath, is_flow, path, url, @@ -233,10 +245,10 @@ url_runnable_args, can_return_message, can_return_error_result, - enabled, error_handler_path, error_handler_args, - retry + retry, + mode } } @@ -303,7 +315,12 @@ ) if (isSaved) { onUpdate?.(saveCfg.path) - drawer?.closeDrawer() + originalConfig = structuredClone($state.snapshot(getSaveCfg())) + initialPath = saveCfg.path + initialScriptPath = saveCfg.script_path + if (mode !== 'suspended') { + drawer?.closeDrawer() + } } deploymentLoading = false } @@ -316,15 +333,20 @@ } } - async function handleToggleEnabled(newEnabled: boolean) { - enabled = newEnabled + async function handleToggleMode(newMode: TriggerMode) { + mode = newMode if (!trigger?.draftConfig) { - await WebsocketTriggerService.setWebsocketTriggerEnabled({ + await WebsocketTriggerService.setWebsocketTriggerMode({ path: initialPath, - workspace: $workspaceStore!, - requestBody: { enabled: newEnabled } + workspace: $workspaceStore ?? '', + requestBody: { mode: newMode } }) - sendUserToast(`${newEnabled ? 'enabled' : 'disabled'} websocket trigger ${initialPath}`) + sendUserToast(`${capitalize(newMode)} websocket trigger ${initialPath}`) + + onUpdate?.(initialPath) + } + if (originalConfig) { + originalConfig['mode'] = newMode } } @@ -340,6 +362,23 @@ }) +{#if mode === 'suspended'} + +{/if} + {#if useDrawer} {/if} @@ -397,6 +437,9 @@ {/if} {:else}
+ {#if mode === 'suspended'} + + {/if} {#if description} {@render description()} {/if} diff --git a/frontend/src/lib/components/triggers/websocket/utils.ts b/frontend/src/lib/components/triggers/websocket/utils.ts index 3802255e97..d71a41faa8 100644 --- a/frontend/src/lib/components/triggers/websocket/utils.ts +++ b/frontend/src/lib/components/triggers/websocket/utils.ts @@ -16,7 +16,7 @@ export async function saveWebsocketTriggerFromCfg( error_handler_args: triggerCfg.error_handler_path ? triggerCfg.error_handler_args : undefined, - retry: triggerCfg.retry, + retry: triggerCfg.retry } : {} const requestBody: EditWebsocketTrigger = { @@ -42,7 +42,7 @@ export async function saveWebsocketTriggerFromCfg( } else { await WebsocketTriggerService.createWebsocketTrigger({ workspace: workspace, - requestBody: { ...requestBody, enabled: true } + requestBody: { ...requestBody, mode: 'enabled' } }) sendUserToast(`Websocket trigger ${triggerCfg.path} created`) } diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index eeb3cf8589..69b060ea66 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -1642,6 +1642,9 @@ export function validateRetryConfig(retry: Retry | undefined): string | null { export type CssColor = keyof (typeof tokensFile)['tokens']['light'] import tokensFile from './assets/tokens/tokens.json' import { darkModeName, lightModeName } from './assets/tokens/colorTokensConfig' +import BarsStaggered from './components/icons/BarsStaggered.svelte' +import { GitIcon } from './components/icons' +import { Bot, Code, Package } from 'lucide-svelte' export function getCssColor( color: CssColor, { @@ -1662,3 +1665,28 @@ export function getCssColor( } export type IconType = Component<{ size?: number }> | typeof import('lucide-svelte').Dot + +export function getJobKindIcon(jobKind: Job['job_kind']) { + if (jobKind === 'flow' || isFlowPreview(jobKind) || jobKind === 'unassigned_flow') { + return BarsStaggered + } else if (jobKind === 'deploymentcallback') { + return GitIcon + } else if ( + jobKind === 'dependencies' || + jobKind === 'appdependencies' || + jobKind === 'flowdependencies' + ) { + return Package + } else if ( + jobKind === 'script' || + isScriptPreview(jobKind) || + jobKind === 'script_hub' || + jobKind === 'singlestepflow' || + jobKind === 'unassigned_script' || + jobKind === 'unassigned_singlestepflow' + ) { + return Code + } else if (jobKind === 'aiagent') { + return Bot + } else if (jobKind) return Code +} diff --git a/frontend/src/lib/utils_deployable.ts b/frontend/src/lib/utils_deployable.ts index 762206913e..cd67ddab30 100644 --- a/frontend/src/lib/utils_deployable.ts +++ b/frontend/src/lib/utils_deployable.ts @@ -212,14 +212,14 @@ export async function getTriggersDeployData(kind: TriggerKind, path: string, wor export async function getTriggerValue(kind: TriggerKind, path: string, workspace: string) { if (kind === 'sqs') { - const { enabled, script_path, is_flow, queue_url, aws_resource_path, message_attributes } = + const { mode, script_path, is_flow, queue_url, aws_resource_path, message_attributes } = await SqsTriggerService.getSqsTrigger({ workspace: workspace!, path: path }) return { - enabled, + mode, script_path, is_flow, queue_url, @@ -227,14 +227,14 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace message_attributes } } else if (kind === 'kafka') { - const { enabled, script_path, is_flow, kafka_resource_path, topics, group_id } = + const { mode, script_path, is_flow, kafka_resource_path, topics, group_id } = await KafkaTriggerService.getKafkaTrigger({ workspace: workspace!, path: path }) return { - enabled, + mode, script_path, is_flow, kafka_resource_path, @@ -243,7 +243,7 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace } } else if (kind === 'mqtt') { const { - enabled, + mode, script_path, is_flow, mqtt_resource_path, @@ -258,7 +258,7 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace }) return { - enabled, + mode, script_path, is_flow, mqtt_resource_path, @@ -270,7 +270,7 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace } } else if (kind === 'nats') { const { - enabled, + mode, script_path, is_flow, nats_resource_path, @@ -284,7 +284,7 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace }) return { - enabled, + mode, script_path, is_flow, nats_resource_path, @@ -295,7 +295,7 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace } } else if (kind === 'postgres') { const { - enabled, + mode, script_path, is_flow, postgres_resource_path, @@ -307,7 +307,7 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace }) return { - enabled, + mode, script_path, is_flow, postgres_resource_path, @@ -315,21 +315,22 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace publication_name } } else if (kind === 'gcp') { - const { enabled, script_path, is_flow, gcp_resource_path } = - await GcpTriggerService.getGcpTrigger({ + const { mode, script_path, is_flow, gcp_resource_path } = await GcpTriggerService.getGcpTrigger( + { workspace: workspace!, path: path - }) + } + ) return { - enabled, + mode, script_path, is_flow, gcp_resource_path } } else if (kind === 'websockets') { const { - enabled, + mode, script_path, is_flow, url, @@ -343,7 +344,7 @@ export async function getTriggerValue(kind: TriggerKind, path: string, workspace }) return { - enabled, + mode, script_path, is_flow, url, diff --git a/frontend/src/routes/(root)/(logged)/email_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/email_triggers/+page.svelte index 386db77449..cbd8428894 100644 --- a/frontend/src/routes/(root)/(logged)/email_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/email_triggers/+page.svelte @@ -5,6 +5,7 @@ EmailTriggerService, WorkspaceService, type EmailTrigger, + type TriggerMode, type WorkspaceDeployUISettings } from '$lib/gen' import { @@ -31,7 +32,18 @@ enterpriseLicense, usedTriggerKinds } from '$lib/stores' - import { Mail, Code, Eye, Pen, Plus, Share, Trash, FileUp, ClipboardCopy } from 'lucide-svelte' + import { + Mail, + Code, + Eye, + Pen, + Plus, + Share, + Trash, + FileUp, + ClipboardCopy, + Pause + } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' import NoItemFound from '$lib/components/home/NoItemFound.svelte' @@ -45,6 +57,7 @@ import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte' import { ALL_DEPLOYABLE, isDeployable } from '$lib/utils_deployable' import { getEmailAddress, getEmailDomain } from '$lib/components/triggers/email/utils' + import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte' type TriggerW = EmailTrigger & { canWrite: boolean } @@ -193,16 +206,18 @@ } } - async function setTriggerEnabled(path: string, enabled: boolean): Promise { + async function onToggleMode(path: string, mode: TriggerMode): Promise { try { - await EmailTriggerService.setEmailTriggerEnabled({ + await EmailTriggerService.setEmailTriggerMode({ path, workspace: $workspaceStore!, - requestBody: { enabled } + requestBody: { mode } }) } catch (err) { sendUserToast( - `Cannot ` + (enabled ? 'enable' : 'disable') + ` email trigger: ${err.body}`, + `Cannot ` + + (mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend') + + ` email trigger: ${err.body}`, true ) } finally { @@ -291,7 +306,7 @@
No email triggers
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { workspace_id, workspaced_local_part, path, edited_by, edited_at, script_path, is_flow, extra_perms, canWrite, marked, local_part, enabled } (path)} + {#each items.slice(0, nbDisplayed) as { workspace_id, workspaced_local_part, path, edited_by, edited_at, script_path, is_flow, extra_perms, canWrite, marked, local_part, mode, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} {@const emailAddress = getEmailAddress( local_part, @@ -334,32 +349,44 @@ - { - setTriggerEnabled(path, e.detail) + + onToggleMode(path, newMode)} + triggerMode={mode} + includeModalConfig={{ + triggerPath: path, + triggerKind: 'email', + runnableConfig: { + path: script_path, + kind: is_flow ? 'flow' : 'script', + retry, + errorHandlerPath: error_handler_path, + errorHandlerArgs: error_handler_args + } }} + {canWrite} + hideToggleLabels + hideDropdown />
@@ -372,25 +399,17 @@ goto(href) } }, - { - displayName: 'Delete', - type: 'delete', - icon: Trash, - disabled: - !canWrite || !($userStore?.is_admin || $userStore?.is_super_admin), - action: async () => { - try { - await EmailTriggerService.deleteEmailTrigger({ - workspace: $workspaceStore ?? '', - path - }) - sendUserToast(`Successfully deleted Email trigger: ${path}`) - loadTriggers() - } catch (error) { - sendUserToast(error.body || error.message, true) - } - } - }, + ...(canWrite && mode !== 'suspended' + ? [ + { + displayName: 'Suspend job execution', + icon: Pause, + action: () => { + onToggleMode(path, 'suspended') + } + } + ] + : []), { displayName: canWrite ? 'Edit' : 'View', icon: canWrite ? Pen : Eye, @@ -424,6 +443,25 @@ action: () => { shareModal?.openDrawer(path, 'email_trigger') } + }, + { + displayName: 'Delete', + type: 'delete', + icon: Trash, + disabled: + !canWrite || !($userStore?.is_admin || $userStore?.is_super_admin), + action: async () => { + try { + await EmailTriggerService.deleteEmailTrigger({ + workspace: $workspaceStore ?? '', + path + }) + sendUserToast(`Successfully deleted Email trigger: ${path}`) + loadTriggers() + } catch (error) { + sendUserToast(error.body || error.message, true) + } + } } ]} /> diff --git a/frontend/src/routes/(root)/(logged)/gcp_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/gcp_triggers/+page.svelte index 639ffd3226..21898bc071 100644 --- a/frontend/src/routes/(root)/(logged)/gcp_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/gcp_triggers/+page.svelte @@ -5,7 +5,8 @@ GcpTriggerService, WorkspaceService, type GcpTrigger, - type WorkspaceDeployUISettings + type WorkspaceDeployUISettings, + type TriggerMode } from '$lib/gen' import { canWrite, @@ -25,7 +26,18 @@ import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' - import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp, ClipboardCopy } from 'lucide-svelte' + import { + Code, + Eye, + Pen, + Plus, + Share, + Trash, + Circle, + FileUp, + ClipboardCopy, + Pause + } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' import NoItemFound from '$lib/components/home/NoItemFound.svelte' @@ -43,6 +55,7 @@ import { GoogleCloudIcon } from '$lib/components/icons' import { getHttpRoute } from '$lib/components/triggers/http/utils' import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte' + import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte' type TriggerD = GcpTrigger & { canWrite: boolean } @@ -83,7 +96,7 @@ ...triggers[i], error: newTrigger.error, last_server_ping: newTrigger.last_server_ping, - enabled: newTrigger.enabled, + mode: newTrigger.mode, server_id: newTrigger.server_id } } @@ -97,16 +110,16 @@ clearInterval(interval) }) - async function setTriggerEnabled(path: string, enabled: boolean): Promise { + async function onToggleMode(path: string, mode: TriggerMode): Promise { try { - await GcpTriggerService.setGcpTriggerEnabled({ + await GcpTriggerService.setGcpTriggerMode({ path, workspace: $workspaceStore!, - requestBody: { enabled } + requestBody: { mode } }) } catch (err) { sendUserToast( - `Cannot ` + (enabled ? 'enable' : 'disable') + ` GCP Pub/Sub trigger: ${err.body}`, + `Cannot ${mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend'} GCP Pub/Sub trigger: ${err.body}`, true ) } finally { @@ -344,10 +357,11 @@
No GCP Pub/Sub triggers
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { gcp_resource_path, topic_id, workspace_id, delivery_type, path, edited_by, error, edited_at, script_path, is_flow, extra_perms, canWrite, enabled, server_id, subscription_id } (path)} + {#each items.slice(0, nbDisplayed) as { gcp_resource_path, topic_id, workspace_id, delivery_type, path, edited_by, error, edited_at, script_path, is_flow, extra_perms, canWrite, mode, server_id, subscription_id, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} {@const ping = new Date()} {@const pinging = ping && ping.getTime() > new Date().getTime() - 15 * 1000} + {@const enabled = mode === 'enabled' || mode === 'suspended'}
No Kafka triggers
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, is_flow, kafka_resource_path, topics, extra_perms, canWrite, marked, server_id, error, last_server_ping, enabled } (path)} + {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, is_flow, kafka_resource_path, topics, extra_perms, canWrite, marked, server_id, error, last_server_ping, mode, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} {@const ping = last_server_ping ? new Date(last_server_ping) : undefined} {@const pinging = ping && ping.getTime() > new Date().getTime() - 15 * 1000} + {@const enabled = mode === 'enabled' || mode === 'suspended'}
@@ -428,6 +444,17 @@ goto(href) } }, + ...(canWrite && mode !== 'suspended' + ? [ + { + displayName: 'Suspend job execution', + icon: Pause, + action: () => { + onToggleMode(path, 'suspended') + } + } + ] + : []), { displayName: 'Delete', type: 'delete', diff --git a/frontend/src/routes/(root)/(logged)/mqtt_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/mqtt_triggers/+page.svelte index 399e1c4a19..ed08283c36 100644 --- a/frontend/src/routes/(root)/(logged)/mqtt_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/mqtt_triggers/+page.svelte @@ -5,10 +5,12 @@ MqttTriggerService, WorkspaceService, type MqttTrigger, + type TriggerMode, type WorkspaceDeployUISettings } from '$lib/gen' import { canWrite, + capitalize, displayDate, getLocalSetting, sendUserToast, @@ -24,7 +26,7 @@ import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' - import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' + import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp, Pause } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' import NoItemFound from '$lib/components/home/NoItemFound.svelte' @@ -40,6 +42,7 @@ import { MqttIcon } from '$lib/components/icons' import { ALL_DEPLOYABLE, isDeployable } from '$lib/utils_deployable' import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte' + import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte' type TriggerM = MqttTrigger & { canWrite: boolean } @@ -81,7 +84,7 @@ ...triggers[i], error: newTrigger.error, last_server_ping: newTrigger.last_server_ping, - enabled: newTrigger.enabled, + mode: newTrigger.mode, server_id: newTrigger.server_id } } @@ -95,16 +98,17 @@ clearInterval(interval) }) - async function setTriggerEnabled(path: string, enabled: boolean): Promise { + async function onToggleMode(path: string, mode: TriggerMode): Promise { try { - await MqttTriggerService.setMqttTriggerEnabled({ + await MqttTriggerService.setMqttTriggerMode({ path, workspace: $workspaceStore!, - requestBody: { enabled } + requestBody: { mode } }) + sendUserToast(`${capitalize(mode)} MQTT trigger ${path}`) } catch (err) { sendUserToast( - `Cannot ` + (enabled ? 'enable' : 'disable') + ` mqtt trigger: ${err.body}`, + `Cannot ${mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend'} mqtt trigger: ${err.body}`, true ) } finally { @@ -306,10 +310,11 @@
No MQTT triggers
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, is_flow, extra_perms, canWrite, error, last_server_ping, server_id, enabled } (path)} + {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, is_flow, extra_perms, canWrite, error, last_server_ping, server_id, mode, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} {@const ping = last_server_ping ? new Date(last_server_ping) : undefined} {@const pinging = ping && ping.getTime() > new Date().getTime() - 15 * 1000} + {@const enabled = mode === 'enabled' || mode === 'suspended'}
@@ -403,19 +419,17 @@ goto(href) } }, - { - displayName: 'Delete', - type: 'delete', - icon: Trash, - disabled: !canWrite, - action: async () => { - await MqttTriggerService.deleteMqttTrigger({ - workspace: $workspaceStore ?? '', - path - }) - loadTriggers() - } - }, + ...(canWrite && mode !== 'suspended' + ? [ + { + displayName: 'Suspend job execution', + icon: Pause, + action: () => { + onToggleMode(path, 'suspended') + } + } + ] + : []), { displayName: canWrite ? 'Edit' : 'View', icon: canWrite ? Pen : Eye, @@ -449,6 +463,19 @@ action: () => { shareModal?.openDrawer(path, 'mqtt_trigger') } + }, + { + displayName: 'Delete', + type: 'delete', + icon: Trash, + disabled: !canWrite, + action: async () => { + await MqttTriggerService.deleteMqttTrigger({ + workspace: $workspaceStore ?? '', + path + }) + loadTriggers() + } } ]} /> diff --git a/frontend/src/routes/(root)/(logged)/nats_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/nats_triggers/+page.svelte index 0d1013a649..86ccfe2158 100644 --- a/frontend/src/routes/(root)/(logged)/nats_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/nats_triggers/+page.svelte @@ -5,6 +5,7 @@ NatsTriggerService, WorkspaceService, type NatsTrigger, + type TriggerMode, type WorkspaceDeployUISettings } from '$lib/gen' import { @@ -30,7 +31,7 @@ enterpriseLicense, usedTriggerKinds } from '$lib/stores' - import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' + import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp, Pause } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' import NoItemFound from '$lib/components/home/NoItemFound.svelte' @@ -46,6 +47,7 @@ import NatsTriggerEditor from '$lib/components/triggers/nats/NatsTriggerEditor.svelte' import { ALL_DEPLOYABLE, isDeployable } from '$lib/utils_deployable' import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte' + import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte' type TriggerW = NatsTrigger & { canWrite: boolean } @@ -86,7 +88,7 @@ ...triggers[i], error: newTrigger.error, last_server_ping: newTrigger.last_server_ping, - enabled: newTrigger.enabled, + mode: newTrigger.mode, server_id: newTrigger.server_id } } @@ -100,16 +102,18 @@ clearInterval(interval) }) - async function setTriggerEnabled(path: string, enabled: boolean): Promise { + async function onToggleMode(path: string, mode: TriggerMode): Promise { try { - await NatsTriggerService.setNatsTriggerEnabled({ + await NatsTriggerService.setNatsTriggerMode({ path, workspace: $workspaceStore!, - requestBody: { enabled } + requestBody: { mode } }) } catch (err) { sendUserToast( - `Cannot ` + (enabled ? 'enable' : 'disable') + ` NATS trigger: ${err.body}`, + `Cannot ` + + (mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend') + + ` NATS trigger: ${err.body}`, true ) } finally { @@ -317,10 +321,11 @@
No NATS triggers
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, is_flow, nats_resource_path, subjects, extra_perms, canWrite, marked, server_id, error, last_server_ping, enabled } (path)} + {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, is_flow, nats_resource_path, subjects, extra_perms, canWrite, marked, server_id, error, last_server_ping, mode, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} {@const ping = last_server_ping ? new Date(last_server_ping) : undefined} {@const pinging = ping && ping.getTime() > new Date().getTime() - 15 * 1000} + {@const enabled = mode === 'enabled' || mode === 'suspended'}
@@ -426,19 +442,17 @@ goto(href) } }, - { - displayName: 'Delete', - type: 'delete', - icon: Trash, - disabled: !canWrite, - action: async () => { - await NatsTriggerService.deleteNatsTrigger({ - workspace: $workspaceStore ?? '', - path - }) - loadTriggers() - } - }, + ...(canWrite && mode !== 'suspended' + ? [ + { + displayName: 'Suspend job execution', + icon: Pause, + action: () => { + onToggleMode(path, 'suspended') + } + } + ] + : []), { displayName: canWrite ? 'Edit' : 'View', icon: canWrite ? Pen : Eye, @@ -472,6 +486,19 @@ action: () => { shareModal?.openDrawer(path, 'nats_trigger') } + }, + { + displayName: 'Delete', + type: 'delete', + icon: Trash, + disabled: !canWrite, + action: async () => { + await NatsTriggerService.deleteNatsTrigger({ + workspace: $workspaceStore ?? '', + path + }) + loadTriggers() + } } ]} /> diff --git a/frontend/src/routes/(root)/(logged)/postgres_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/postgres_triggers/+page.svelte index 15939ba62e..cd8e6237af 100644 --- a/frontend/src/routes/(root)/(logged)/postgres_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/postgres_triggers/+page.svelte @@ -5,6 +5,7 @@ PostgresTriggerService, WorkspaceService, type PostgresTrigger, + type TriggerMode, type WorkspaceDeployUISettings } from '$lib/gen' import { @@ -13,7 +14,8 @@ getLocalSetting, sendUserToast, storeLocalSetting, - removeTriggerKindIfUnused + removeTriggerKindIfUnused, + capitalize } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -24,7 +26,18 @@ import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' - import { Code, Eye, Pen, Plus, Share, Trash, Circle, Database, FileUp } from 'lucide-svelte' + import { + Code, + Eye, + Pen, + Plus, + Share, + Trash, + Circle, + Database, + FileUp, + Pause + } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' import NoItemFound from '$lib/components/home/NoItemFound.svelte' @@ -41,6 +54,7 @@ import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte' import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte' import TextInput from '$lib/components/text_input/TextInput.svelte' + import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte' type TriggerD = PostgresTrigger & { canWrite: boolean } @@ -81,7 +95,7 @@ ...triggers[i], error: newTrigger.error, last_server_ping: newTrigger.last_server_ping, - enabled: newTrigger.enabled, + mode: newTrigger.mode, server_id: newTrigger.server_id } } @@ -95,18 +109,16 @@ clearInterval(interval) }) - async function setTriggerEnabled(path: string, enabled: boolean): Promise { + async function onToggleMode(path: string, mode: TriggerMode): Promise { try { - await PostgresTriggerService.setPostgresTriggerEnabled({ + await PostgresTriggerService.setPostgresTriggerMode({ path, workspace: $workspaceStore!, - requestBody: { enabled } + requestBody: { mode } }) + sendUserToast(`${capitalize(mode)} postgres trigger ${path}`) } catch (err) { - sendUserToast( - `Cannot ` + (enabled ? 'enable' : 'disable') + ` postgres trigger: ${err.body}`, - true - ) + sendUserToast(`Cannot change postgres trigger mode: ${err.body}`, true) } finally { loadTriggers() } @@ -374,10 +386,11 @@
No postgres triggers
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { postgres_resource_path, publication_name, replication_slot_name, path, edited_by, error, edited_at, script_path, is_flow, extra_perms, canWrite, enabled, server_id } (path)} + {#each items.slice(0, nbDisplayed) as { postgres_resource_path, publication_name, replication_slot_name, path, edited_by, error, edited_at, script_path, is_flow, extra_perms, canWrite, mode, server_id, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} {@const ping = new Date()} {@const pinging = ping && ping.getTime() > new Date().getTime() - 15 * 1000} + {@const enabled = mode === 'enabled' || mode === 'suspended'}
@@ -472,6 +496,17 @@ goto(href) } }, + ...(canWrite && mode !== 'suspended' + ? [ + { + displayName: 'Suspend job execution', + icon: Pause, + action: () => { + onToggleMode(path, 'suspended') + } + } + ] + : []), { displayName: 'Delete', type: 'delete', diff --git a/frontend/src/routes/(root)/(logged)/routes/+page.svelte b/frontend/src/routes/(root)/(logged)/routes/+page.svelte index c87d1861e1..c9c5c85fcd 100644 --- a/frontend/src/routes/(root)/(logged)/routes/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/routes/+page.svelte @@ -5,6 +5,7 @@ HttpTriggerService, WorkspaceService, type HttpTrigger, + type TriggerMode, type WorkspaceDeployUISettings } from '$lib/gen' import { @@ -31,7 +32,18 @@ enterpriseLicense, usedTriggerKinds } from '$lib/stores' - import { Route, Code, Eye, Pen, Plus, Share, Trash, FileUp, ClipboardCopy } from 'lucide-svelte' + import { + Route, + Code, + Eye, + Pen, + Plus, + Share, + Trash, + FileUp, + ClipboardCopy, + Pause + } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' import NoItemFound from '$lib/components/home/NoItemFound.svelte' @@ -48,6 +60,7 @@ import { getHttpRoute } from '$lib/components/triggers/http/utils' import RoutesGenerator from '$lib/components/triggers/http/RoutesGenerator.svelte' import OpenApiSpecGenerator from '$lib/components/triggers/http/OpenAPISpecGenerator.svelte' + import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte' type TriggerW = HttpTrigger & { canWrite: boolean } @@ -196,16 +209,18 @@ } } - async function setTriggerEnabled(path: string, enabled: boolean): Promise { + async function onToggleMode(path: string, mode: TriggerMode): Promise { try { - await HttpTriggerService.setHttpTriggerEnabled({ + await HttpTriggerService.setHttpTriggerMode({ path, workspace: $workspaceStore!, - requestBody: { enabled } + requestBody: { mode } }) } catch (err) { sendUserToast( - `Cannot ` + (enabled ? 'enable' : 'disable') + ` http trigger: ${err.body}`, + `Cannot ` + + (mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend') + + ` http trigger: ${err.body}`, true ) } finally { @@ -314,7 +329,7 @@
No routes
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { workspace_id, workspaced_route, enabled, path, edited_by, edited_at, script_path, route_path, is_flow, extra_perms, canWrite, marked, http_method, static_asset_config } (path)} + {#each items.slice(0, nbDisplayed) as { workspace_id, workspaced_route, mode, path, edited_by, edited_at, script_path, route_path, is_flow, extra_perms, canWrite, marked, http_method, static_asset_config, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`}
- { - setTriggerEnabled(path, e.detail) + onToggleMode(path, mode)} + triggerMode={mode} + includeModalConfig={{ + triggerPath: path, + triggerKind: 'http', + runnableConfig: { + path: script_path, + kind: is_flow ? 'flow' : 'script', + retry, + errorHandlerPath: error_handler_path, + errorHandlerArgs: error_handler_args + } }} + {canWrite} + hideToggleLabels + hideDropdown />
@@ -401,25 +427,17 @@ goto(href) } }, - { - displayName: 'Delete', - type: 'delete', - icon: Trash, - disabled: - !canWrite || !($userStore?.is_admin || $userStore?.is_super_admin), - action: async () => { - try { - await HttpTriggerService.deleteHttpTrigger({ - workspace: $workspaceStore ?? '', - path - }) - sendUserToast(`Successfully deleted HTTP route: ${path}`) - loadTriggers() - } catch (error) { - sendUserToast(error.body || error.message, true) - } - } - }, + ...(canWrite && mode !== 'suspended' + ? [ + { + displayName: 'Suspend job execution', + icon: Pause, + action: () => { + onToggleMode(path, 'suspended') + } + } + ] + : []), { displayName: canWrite ? 'Edit' : 'View', icon: canWrite ? Pen : Eye, @@ -453,6 +471,25 @@ action: () => { shareModal?.openDrawer(path, 'http_trigger') } + }, + { + displayName: 'Delete', + type: 'delete', + icon: Trash, + disabled: + !canWrite || !($userStore?.is_admin || $userStore?.is_super_admin), + action: async () => { + try { + await HttpTriggerService.deleteHttpTrigger({ + workspace: $workspaceStore ?? '', + path + }) + sendUserToast(`Successfully deleted HTTP route: ${path}`) + loadTriggers() + } catch (error) { + sendUserToast(error.body || error.message, true) + } + } } ]} /> diff --git a/frontend/src/routes/(root)/(logged)/sqs_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/sqs_triggers/+page.svelte index 3c494e4165..41fe59bdb0 100644 --- a/frontend/src/routes/(root)/(logged)/sqs_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/sqs_triggers/+page.svelte @@ -5,6 +5,7 @@ SqsTriggerService, WorkspaceService, type SqsTrigger, + type TriggerMode, type WorkspaceDeployUISettings } from '$lib/gen' import { @@ -24,7 +25,7 @@ import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' - import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' + import { Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp, Pause } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' import NoItemFound from '$lib/components/home/NoItemFound.svelte' @@ -40,6 +41,7 @@ import { ALL_DEPLOYABLE, isDeployable } from '$lib/utils_deployable' import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte' import { AwsIcon } from '$lib/components/icons' + import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte' type TriggerD = SqsTrigger & { canWrite: boolean } @@ -80,7 +82,7 @@ ...triggers[i], error: newTrigger.error, last_server_ping: newTrigger.last_server_ping, - enabled: newTrigger.enabled, + mode: newTrigger.mode, server_id: newTrigger.server_id } } @@ -94,16 +96,18 @@ clearInterval(interval) }) - async function setTriggerEnabled(path: string, enabled: boolean): Promise { + async function onToggleMode(path: string, mode: TriggerMode): Promise { try { - await SqsTriggerService.setSqsTriggerEnabled({ + await SqsTriggerService.setSqsTriggerMode({ path, workspace: $workspaceStore!, - requestBody: { enabled } + requestBody: { mode } }) } catch (err) { sendUserToast( - `Cannot ` + (enabled ? 'enable' : 'disable') + ` sqs trigger: ${err.body}`, + `Cannot ` + + (mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend') + + ` sqs trigger: ${err.body}`, true ) } finally { @@ -302,10 +306,11 @@
No sqs triggers
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { path, edited_by, error, edited_at, script_path, is_flow, extra_perms, canWrite, enabled, server_id } (path)} + {#each items.slice(0, nbDisplayed) as { path, edited_by, error, edited_at, script_path, is_flow, extra_perms, canWrite, mode, server_id, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} {@const ping = new Date()} {@const pinging = ping && ping.getTime() > new Date().getTime() - 15 * 1000} + {@const enabled = mode === 'enabled' || mode === 'suspended'}
@@ -398,23 +414,17 @@ goto(href) } }, - { - displayName: 'Delete', - type: 'delete', - icon: Trash, - disabled: !canWrite, - action: async () => { - try { - await SqsTriggerService.deleteSqsTrigger({ - workspace: $workspaceStore ?? '', - path - }) - loadTriggers() - } catch (error) { - sendUserToast(error.body, true) - } - } - }, + ...(canWrite && mode !== 'suspended' + ? [ + { + displayName: 'Suspend job execution', + icon: Pause, + action: () => { + onToggleMode(path, 'suspended') + } + } + ] + : []), { displayName: canWrite ? 'Edit' : 'View', icon: canWrite ? Pen : Eye, @@ -448,6 +458,23 @@ action: () => { shareModal?.openDrawer(path, 'sqs_trigger') } + }, + { + displayName: 'Delete', + type: 'delete', + icon: Trash, + disabled: !canWrite, + action: async () => { + try { + await SqsTriggerService.deleteSqsTrigger({ + workspace: $workspaceStore ?? '', + path + }) + loadTriggers() + } catch (error) { + sendUserToast(error.body, true) + } + } } ]} /> diff --git a/frontend/src/routes/(root)/(logged)/websocket_triggers/+page.svelte b/frontend/src/routes/(root)/(logged)/websocket_triggers/+page.svelte index 19b42040d4..9d58242c27 100644 --- a/frontend/src/routes/(root)/(logged)/websocket_triggers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/websocket_triggers/+page.svelte @@ -5,6 +5,7 @@ WebsocketTriggerService, WorkspaceService, type WebsocketTrigger, + type TriggerMode, type WorkspaceDeployUISettings } from '$lib/gen' import { @@ -13,7 +14,8 @@ getLocalSetting, sendUserToast, storeLocalSetting, - removeTriggerKindIfUnused + removeTriggerKindIfUnused, + capitalize } from '$lib/utils' import { base } from '$app/paths' import CenteredPage from '$lib/components/CenteredPage.svelte' @@ -24,7 +26,7 @@ import ShareModal from '$lib/components/ShareModal.svelte' import Toggle from '$lib/components/Toggle.svelte' import { enterpriseLicense, usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' - import { Unplug, Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp } from 'lucide-svelte' + import { Unplug, Code, Eye, Pen, Plus, Share, Trash, Circle, FileUp, Pause } from 'lucide-svelte' import { goto } from '$lib/navigation' import SearchItems from '$lib/components/SearchItems.svelte' import NoItemFound from '$lib/components/home/NoItemFound.svelte' @@ -39,6 +41,7 @@ import WebsocketTriggerEditor from '$lib/components/triggers/websocket/WebsocketTriggerEditor.svelte' import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte' import { ALL_DEPLOYABLE, isDeployable } from '$lib/utils_deployable' + import TriggerModeToggle from '$lib/components/triggers/TriggerModeToggle.svelte' type TriggerW = WebsocketTrigger & { canWrite: boolean } @@ -79,7 +82,7 @@ ...triggers[i], error: newTrigger.error, last_server_ping: newTrigger.last_server_ping, - enabled: newTrigger.enabled, + mode: newTrigger.mode, server_id: newTrigger.server_id } } @@ -93,16 +96,19 @@ clearInterval(interval) }) - async function setTriggerEnabled(path: string, enabled: boolean): Promise { + async function onToggleMode(path: string, mode: TriggerMode): Promise { try { - await WebsocketTriggerService.setWebsocketTriggerEnabled({ + await WebsocketTriggerService.setWebsocketTriggerMode({ path, workspace: $workspaceStore!, - requestBody: { enabled } + requestBody: { mode } }) + sendUserToast(`${capitalize(mode)} websocket trigger ${path}`) } catch (err) { sendUserToast( - `Cannot ` + (enabled ? 'enable' : 'disable') + ` websocket trigger: ${err.body}`, + `Cannot ` + + (mode === 'enabled' ? 'enable' : mode === 'disabled' ? 'disable' : 'suspend') + + ` websocket trigger: ${err.body}`, true ) } finally { @@ -299,10 +305,11 @@
No websocket triggers
{:else if items?.length}
- {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, url, is_flow, extra_perms, canWrite, marked, error, last_server_ping, server_id, enabled } (path)} + {#each items.slice(0, nbDisplayed) as { path, edited_by, edited_at, script_path, url, is_flow, extra_perms, canWrite, marked, error, last_server_ping, server_id, mode, retry, error_handler_path, error_handler_args } (path)} {@const href = `${is_flow ? '/flows/get' : '/scripts/get'}/${script_path}`} {@const ping = last_server_ping ? new Date(last_server_ping) : undefined} {@const pinging = ping && ping.getTime() > new Date().getTime() - 15 * 1000} + {@const enabled = mode === 'enabled' || mode === 'suspended'}
@@ -409,19 +427,17 @@ goto(href) } }, - { - displayName: 'Delete', - type: 'delete', - icon: Trash, - disabled: !canWrite, - action: async () => { - await WebsocketTriggerService.deleteWebsocketTrigger({ - workspace: $workspaceStore ?? '', - path - }) - loadTriggers() - } - }, + ...(canWrite && mode !== 'suspended' + ? [ + { + displayName: 'Suspend job execution', + icon: Pause, + action: () => { + onToggleMode(path, 'suspended') + } + } + ] + : []), { displayName: canWrite ? 'Edit' : 'View', icon: canWrite ? Pen : Eye, @@ -455,6 +471,19 @@ action: () => { shareModal?.openDrawer(path, 'websocket_trigger') } + }, + { + displayName: 'Delete', + type: 'delete', + icon: Trash, + disabled: !canWrite, + action: async () => { + await WebsocketTriggerService.deleteWebsocketTrigger({ + workspace: $workspaceStore ?? '', + path + }) + loadTriggers() + } } ]} />