diff --git a/backend/.sqlx/query-04a8482180a3f8e07761742f13ac30afc081eeed1151a0b5e043d785baebfc9d.json b/backend/.sqlx/query-04a8482180a3f8e07761742f13ac30afc081eeed1151a0b5e043d785baebfc9d.json new file mode 100644 index 0000000000..2fe60e6570 --- /dev/null +++ b/backend/.sqlx/query-04a8482180a3f8e07761742f13ac30afc081eeed1151a0b5e043d785baebfc9d.json @@ -0,0 +1,39 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n mqtt_trigger \n SET\n mqtt_resource_path = $1,\n subscribe_topics = $2,\n client_version = $3,\n client_id = $4,\n v3_config = $5,\n v5_config = $6,\n is_flow = $7, \n edited_by = $8, \n email = $9,\n script_path = $10,\n path = $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 WHERE \n workspace_id = $12 AND \n path = $13\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "JsonbArray", + { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + }, + "Varchar", + "Jsonb", + "Jsonb", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Text", + "Text", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "04a8482180a3f8e07761742f13ac30afc081eeed1151a0b5e043d785baebfc9d" +} diff --git a/backend/.sqlx/query-09f43081a8b253d059d9d4302daa139cee6f055cdbafc213b2f992667f8ed578.json b/backend/.sqlx/query-09f43081a8b253d059d9d4302daa139cee6f055cdbafc213b2f992667f8ed578.json new file mode 100644 index 0000000000..9fb3f5ab6c --- /dev/null +++ b/backend/.sqlx/query-09f43081a8b253d059d9d4302daa139cee6f055cdbafc213b2f992667f8ed578.json @@ -0,0 +1,187 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n workspace_id, \n workspaced_route,\n path, \n route_path, \n route_path_key, \n authentication_resource_path,\n script_path, \n is_flow, \n summary,\n description,\n edited_by, \n edited_at, \n email, \n extra_perms, \n is_async, \n authentication_method AS \"authentication_method: _\", \n http_method AS \"http_method: _\", \n static_asset_config AS \"static_asset_config: _\", \n is_static_website,\n wrap_body,\n raw_string,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM http_trigger\n WHERE workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "workspaced_route", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "route_path", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "route_path_key", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "authentication_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "summary", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 14, + "name": "is_async", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "authentication_method: _", + "type_info": { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + } + }, + { + "ordinal": 16, + "name": "http_method: _", + "type_info": { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + } + }, + { + "ordinal": 17, + "name": "static_asset_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 18, + "name": "is_static_website", + "type_info": "Bool" + }, + { + "ordinal": 19, + "name": "wrap_body", + "type_info": "Bool" + }, + { + "ordinal": 20, + "name": "raw_string", + "type_info": "Bool" + }, + { + "ordinal": 21, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 22, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 23, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + true, + true, + true + ] + }, + "hash": "09f43081a8b253d059d9d4302daa139cee6f055cdbafc213b2f992667f8ed578" +} diff --git a/backend/.sqlx/query-1e333ccb7657bc721d18ea2230cb20f15e4a7839c87c853f280623bafb30b80c.json b/backend/.sqlx/query-1e333ccb7657bc721d18ea2230cb20f15e4a7839c87c853f280623bafb30b80c.json new file mode 100644 index 0000000000..19ae194f04 --- /dev/null +++ b/backend/.sqlx/query-1e333ccb7657bc721d18ea2230cb20f15e4a7839c87c853f280623bafb30b80c.json @@ -0,0 +1,28 @@ +{ + "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 ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "VarcharArray", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "1e333ccb7657bc721d18ea2230cb20f15e4a7839c87c853f280623bafb30b80c" +} diff --git a/backend/.sqlx/query-208f30f8a570db41ee52f01a4402d76f7e174e39c45ede29dcd6aa9c65850fef.json b/backend/.sqlx/query-208f30f8a570db41ee52f01a4402d76f7e174e39c45ede29dcd6aa9c65850fef.json new file mode 100644 index 0000000000..24744c96fb --- /dev/null +++ b/backend/.sqlx/query-208f30f8a570db41ee52f01a4402d76f7e174e39c45ede29dcd6aa9c65850fef.json @@ -0,0 +1,153 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n mqtt_resource_path,\n subscribe_topics as \"subscribe_topics!: Vec>\",\n v3_config as \"v3_config!: Option>\",\n v5_config as \"v5_config!: Option>\",\n client_version AS \"client_version: _\",\n client_id,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n mqtt_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mqtt_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscribe_topics!: Vec>", + "type_info": "JsonbArray" + }, + { + "ordinal": 2, + "name": "v3_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "v5_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "client_version: _", + "type_info": { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "client_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 19, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 20, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + true, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "208f30f8a570db41ee52f01a4402d76f7e174e39c45ede29dcd6aa9c65850fef" +} diff --git a/backend/.sqlx/query-264bc51ec372dfc6c1c4eb3608b32d8a250bd0d65ce33d7eb19eaea62466a803.json b/backend/.sqlx/query-264bc51ec372dfc6c1c4eb3608b32d8a250bd0d65ce33d7eb19eaea62466a803.json new file mode 100644 index 0000000000..3e9ba6053e --- /dev/null +++ b/backend/.sqlx/query-264bc51ec372dfc6c1c4eb3608b32d8a250bd0d65ce33d7eb19eaea62466a803.json @@ -0,0 +1,140 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "aws_auth_resource_type: _", + "type_info": { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + } + }, + { + "ordinal": 1, + "name": "aws_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "message_attributes", + "type_info": "TextArray" + }, + { + "ordinal": 3, + "name": "queue_url", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 11, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 14, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 17, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 18, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false, + true, + true, + true + ] + }, + "hash": "264bc51ec372dfc6c1c4eb3608b32d8a250bd0d65ce33d7eb19eaea62466a803" +} diff --git a/backend/.sqlx/query-2659fe2e121ac15da08030c9e72bdb79a580711cba2139f0cf901b30bb491fd5.json b/backend/.sqlx/query-2659fe2e121ac15da08030c9e72bdb79a580711cba2139f0cf901b30bb491fd5.json new file mode 100644 index 0000000000..f1d2942f51 --- /dev/null +++ b/backend/.sqlx/query-2659fe2e121ac15da08030c9e72bdb79a580711cba2139f0cf901b30bb491fd5.json @@ -0,0 +1,150 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n mqtt_resource_path,\n subscribe_topics as \"subscribe_topics!: Vec>\",\n v3_config as \"v3_config!: Option>\",\n v5_config as \"v5_config!: Option>\",\n client_version as \"client_version: _\",\n client_id,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM\n mqtt_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mqtt_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscribe_topics!: Vec>", + "type_info": "JsonbArray" + }, + { + "ordinal": 2, + "name": "v3_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "v5_config!: Option>", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "client_version: _", + "type_info": { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "client_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 19, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 20, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + true, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "2659fe2e121ac15da08030c9e72bdb79a580711cba2139f0cf901b30bb491fd5" +} diff --git a/backend/.sqlx/query-39cd46049a6e7d21d310a4973b3b40604cbb8c5ea1d28e4f74651505c317b440.json b/backend/.sqlx/query-39cd46049a6e7d21d310a4973b3b40604cbb8c5ea1d28e4f74651505c317b440.json new file mode 100644 index 0000000000..0b80171146 --- /dev/null +++ b/backend/.sqlx/query-39cd46049a6e7d21d310a4973b3b40604cbb8c5ea1d28e4f74651505c317b440.json @@ -0,0 +1,83 @@ +{ + "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 ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "gcp_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "delivery_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 8, + "name": "retry: _", + "type_info": "Jsonb" + }, + { + "ordinal": 9, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "error_handler_args: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true + ] + }, + "hash": "39cd46049a6e7d21d310a4973b3b40604cbb8c5ea1d28e4f74651505c317b440" +} diff --git a/backend/.sqlx/query-3a6c93a91d9a21aaf9e9709932ac810221c5fc8fc29ca4f1984c819e64e29437.json b/backend/.sqlx/query-3a6c93a91d9a21aaf9e9709932ac810221c5fc8fc29ca4f1984c819e64e29437.json new file mode 100644 index 0000000000..c820c649f4 --- /dev/null +++ b/backend/.sqlx/query-3a6c93a91d9a21aaf9e9709932ac810221c5fc8fc29ca4f1984c819e64e29437.json @@ -0,0 +1,125 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n replication_slot_name,\n publication_name,\n postgres_resource_path,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n postgres_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 4, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 9, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 10, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 12, + "name": "replication_slot_name", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "publication_name", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "postgres_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 15, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 16, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false, + false, + false, + false, + true, + true, + true + ] + }, + "hash": "3a6c93a91d9a21aaf9e9709932ac810221c5fc8fc29ca4f1984c819e64e29437" +} diff --git a/backend/.sqlx/query-3f91433fc076f47dd3516b40c9337981c329ddc31132882f037eaf2709aa8805.json b/backend/.sqlx/query-3f91433fc076f47dd3516b40c9337981c329ddc31132882f037eaf2709aa8805.json new file mode 100644 index 0000000000..bbd1cbc510 --- /dev/null +++ b/backend/.sqlx/query-3f91433fc076f47dd3516b40c9337981c329ddc31132882f037eaf2709aa8805.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE kafka_trigger SET kafka_resource_path = $1, group_id = $2, topics = $3, script_path = $4, path = $5, is_flow = $6, edited_by = $7, email = $8, edited_at = now(), server_id = NULL, error = NULL, error_handler_path = $11, error_handler_args = $12, retry = $13\n WHERE workspace_id = $9 AND path = $10", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "VarcharArray", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Text", + "Text", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "3f91433fc076f47dd3516b40c9337981c329ddc31132882f037eaf2709aa8805" +} diff --git a/backend/.sqlx/query-4410b2d6e52556569cda9d945f791756a80ff2835d74bfab85f53141b6e32351.json b/backend/.sqlx/query-4410b2d6e52556569cda9d945f791756a80ff2835d74bfab85f53141b6e32351.json new file mode 100644 index 0000000000..63c326f5e2 --- /dev/null +++ b/backend/.sqlx/query-4410b2d6e52556569cda9d945f791756a80ff2835d74bfab85f53141b6e32351.json @@ -0,0 +1,151 @@ +{ + "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 is_async, \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\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "route_path", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "authentication_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "is_async", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "authentication_method: _", + "type_info": { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + } + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "static_asset_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 11, + "name": "wrap_body", + "type_info": "Bool" + }, + { + "ordinal": 12, + "name": "raw_string", + "type_info": "Bool" + }, + { + "ordinal": 13, + "name": "workspaced_route", + "type_info": "Bool" + }, + { + "ordinal": 14, + "name": "is_static_website", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 16, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + } + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + true, + true, + true + ] + }, + "hash": "4410b2d6e52556569cda9d945f791756a80ff2835d74bfab85f53141b6e32351" +} diff --git a/backend/.sqlx/query-4479beaefc36afba093e5113bb330a87979623d69b94f274863318ed351a1dac.json b/backend/.sqlx/query-4479beaefc36afba093e5113bb330a87979623d69b94f274863318ed351a1dac.json new file mode 100644 index 0000000000..02e3467f5c --- /dev/null +++ b/backend/.sqlx/query-4479beaefc36afba093e5113bb330a87979623d69b94f274863318ed351a1dac.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE websocket_trigger SET url = $1, script_path = $2, path = $3, is_flow = $4, filters = $5, initial_messages = $6, url_runnable_args = $7, edited_by = $8, email = $9, can_return_message = $10, edited_at = now(), server_id = NULL, error = NULL, error_handler_path = $13, error_handler_args = $14, retry = $15\n WHERE workspace_id = $11 AND path = $12", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Bool", + "JsonbArray", + "JsonbArray", + "Jsonb", + "Varchar", + "Varchar", + "Bool", + "Text", + "Text", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "4479beaefc36afba093e5113bb330a87979623d69b94f274863318ed351a1dac" +} diff --git a/backend/.sqlx/query-4eab48b7e86788835c3ba739cca764231013b32e7dc26fb3b775e872d2222005.json b/backend/.sqlx/query-4eab48b7e86788835c3ba739cca764231013b32e7dc26fb3b775e872d2222005.json new file mode 100644 index 0000000000..af68ad8234 --- /dev/null +++ b/backend/.sqlx/query-4eab48b7e86788835c3ba739cca764231013b32e7dc26fb3b775e872d2222005.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE postgres_trigger \n SET \n script_path = $1, \n path = $2, \n is_flow = $3, \n edited_by = $4, \n email = $5, \n postgres_resource_path = $6, \n replication_slot_name = $7,\n publication_name = $8,\n edited_at = now(), \n error = NULL,\n server_id = NULL,\n error_handler_path = $11,\n error_handler_args = $12,\n retry = $13\n WHERE \n workspace_id = $9 AND \n path = $10\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Text", + "Text", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "4eab48b7e86788835c3ba739cca764231013b32e7dc26fb3b775e872d2222005" +} diff --git a/backend/.sqlx/query-5140cfccd4a8fb7d64087de34bc2fbe1f9e0172b38492a99be19147d04a961fe.json b/backend/.sqlx/query-5140cfccd4a8fb7d64087de34bc2fbe1f9e0172b38492a99be19147d04a961fe.json new file mode 100644 index 0000000000..c80c36b18d --- /dev/null +++ b/backend/.sqlx/query-5140cfccd4a8fb7d64087de34bc2fbe1f9e0172b38492a99be19147d04a961fe.json @@ -0,0 +1,26 @@ +{ + "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": "5140cfccd4a8fb7d64087de34bc2fbe1f9e0172b38492a99be19147d04a961fe" +} diff --git a/backend/.sqlx/query-566823041170df3a6379ce58d321895746b0cd8a6891b05d51abeb7079ded5d7.json b/backend/.sqlx/query-566823041170df3a6379ce58d321895746b0cd8a6891b05d51abeb7079ded5d7.json new file mode 100644 index 0000000000..2c7d846d8f --- /dev/null +++ b/backend/.sqlx/query-566823041170df3a6379ce58d321895746b0cd8a6891b05d51abeb7079ded5d7.json @@ -0,0 +1,39 @@ +{ + "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 WHERE \n workspace_id = $12 AND \n path = $13\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + }, + "Jsonb", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Text", + "Text", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "566823041170df3a6379ce58d321895746b0cd8a6891b05d51abeb7079ded5d7" +} diff --git a/backend/.sqlx/query-570a4684bbdb390e4652301aa8d2d09b6e09b258f748d48cf2d188465621ee04.json b/backend/.sqlx/query-570a4684bbdb390e4652301aa8d2d09b6e09b258f748d48cf2d188465621ee04.json new file mode 100644 index 0000000000..f9d6c86004 --- /dev/null +++ b/backend/.sqlx/query-570a4684bbdb390e4652301aa8d2d09b6e09b258f748d48cf2d188465621ee04.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE nats_trigger SET nats_resource_path = $1, subjects = $2, stream_name = $3, consumer_name = $4, use_jetstream = $5, script_path = $6, path = $7, is_flow = $8, edited_by = $9, email = $10, edited_at = now(), server_id = NULL, error = NULL, error_handler_path = $13, error_handler_args = $14, retry = $15\n WHERE workspace_id = $11 AND path = $12", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "VarcharArray", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Text", + "Text", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "570a4684bbdb390e4652301aa8d2d09b6e09b258f748d48cf2d188465621ee04" +} diff --git a/backend/.sqlx/query-58ddf5c76455d30dc42f46a0553e761461c327e7502b707c2d2742946d7d1f4c.json b/backend/.sqlx/query-58ddf5c76455d30dc42f46a0553e761461c327e7502b707c2d2742946d7d1f4c.json new file mode 100644 index 0000000000..2a91229fbd --- /dev/null +++ b/backend/.sqlx/query-58ddf5c76455d30dc42f46a0553e761461c327e7502b707c2d2742946d7d1f4c.json @@ -0,0 +1,138 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n queue_url,\n aws_resource_path,\n message_attributes,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM\n sqs_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "aws_auth_resource_type: _", + "type_info": { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + } + }, + { + "ordinal": 1, + "name": "queue_url", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "aws_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "message_attributes", + "type_info": "TextArray" + }, + { + "ordinal": 4, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 11, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 14, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 17, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 18, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false, + true, + true, + true + ] + }, + "hash": "58ddf5c76455d30dc42f46a0553e761461c327e7502b707c2d2742946d7d1f4c" +} diff --git a/backend/.sqlx/query-5af0299841a0313d0cc270e98b04b5b5968184ae53dc076b1a9bc76af35923da.json b/backend/.sqlx/query-5af0299841a0313d0cc270e98b04b5b5968184ae53dc076b1a9bc76af35923da.json new file mode 100644 index 0000000000..ebd6dafb25 --- /dev/null +++ b/backend/.sqlx/query-5af0299841a0313d0cc270e98b04b5b5968184ae53dc076b1a9bc76af35923da.json @@ -0,0 +1,136 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT \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 edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM nats_trigger\n WHERE workspace_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "nats_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "subjects", + "type_info": "VarcharArray" + }, + { + "ordinal": 4, + "name": "stream_name", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "consumer_name", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "use_jetstream", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 14, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 15, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 18, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 19, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "5af0299841a0313d0cc270e98b04b5b5968184ae53dc076b1a9bc76af35923da" +} diff --git a/backend/.sqlx/query-6489342f52a9e1e90466bad48e0a6079fcabd696b02d57e3dbe652d28b84f008.json b/backend/.sqlx/query-6489342f52a9e1e90466bad48e0a6079fcabd696b02d57e3dbe652d28b84f008.json new file mode 100644 index 0000000000..fa87b125a2 --- /dev/null +++ b/backend/.sqlx/query-6489342f52a9e1e90466bad48e0a6079fcabd696b02d57e3dbe652d28b84f008.json @@ -0,0 +1,125 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT \n workspace_id,\n path,\n kafka_resource_path,\n group_id,\n topics,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM kafka_trigger WHERE workspace_id = $1 AND path = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "kafka_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "group_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "topics", + "type_info": "VarcharArray" + }, + { + "ordinal": 5, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 10, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 13, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 16, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "6489342f52a9e1e90466bad48e0a6079fcabd696b02d57e3dbe652d28b84f008" +} diff --git a/backend/.sqlx/query-7399ff3f22cbf2a522b6123e8ebf101ec9d41b7c8465cc501e61a1f6117981a2.json b/backend/.sqlx/query-7399ff3f22cbf2a522b6123e8ebf101ec9d41b7c8465cc501e61a1f6117981a2.json new file mode 100644 index 0000000000..13d86dfc34 --- /dev/null +++ b/backend/.sqlx/query-7399ff3f22cbf2a522b6123e8ebf101ec9d41b7c8465cc501e61a1f6117981a2.json @@ -0,0 +1,38 @@ +{ + "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 ) \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 )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "7399ff3f22cbf2a522b6123e8ebf101ec9d41b7c8465cc501e61a1f6117981a2" +} diff --git a/backend/.sqlx/query-7f039cc86874128ee8c9f577897340a155562fd6cde328298a66496c90d8cd59.json b/backend/.sqlx/query-7f039cc86874128ee8c9f577897340a155562fd6cde328298a66496c90d8cd59.json new file mode 100644 index 0000000000..51928cf94c --- /dev/null +++ b/backend/.sqlx/query-7f039cc86874128ee8c9f577897340a155562fd6cde328298a66496c90d8cd59.json @@ -0,0 +1,26 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO postgres_trigger (\n publication_name,\n replication_slot_name,\n workspace_id, \n path, \n script_path, \n is_flow, \n email, \n enabled, \n postgres_resource_path, \n edited_by,\n error_handler_path,\n error_handler_args,\n retry\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 )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "7f039cc86874128ee8c9f577897340a155562fd6cde328298a66496c90d8cd59" +} diff --git a/backend/.sqlx/query-8f511fd837a99fa37a95781e50f8a83eaf93651f82c36c9795637d3ad761d003.json b/backend/.sqlx/query-8f511fd837a99fa37a95781e50f8a83eaf93651f82c36c9795637d3ad761d003.json new file mode 100644 index 0000000000..8e37b499be --- /dev/null +++ b/backend/.sqlx/query-8f511fd837a99fa37a95781e50f8a83eaf93651f82c36c9795637d3ad761d003.json @@ -0,0 +1,124 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT \n workspace_id,\n path,\n kafka_resource_path,\n group_id,\n topics,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\" \n FROM kafka_trigger\n WHERE workspace_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "kafka_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "group_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "topics", + "type_info": "VarcharArray" + }, + { + "ordinal": 5, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 10, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 13, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 16, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "8f511fd837a99fa37a95781e50f8a83eaf93651f82c36c9795637d3ad761d003" +} diff --git a/backend/.sqlx/query-8ffd68b82cfce337fc32a6e80c8d56c53acd02eb350b46f6b815a5ddbb69d163.json b/backend/.sqlx/query-8ffd68b82cfce337fc32a6e80c8d56c53acd02eb350b46f6b815a5ddbb69d163.json new file mode 100644 index 0000000000..40ef813403 --- /dev/null +++ b/backend/.sqlx/query-8ffd68b82cfce337fc32a6e80c8d56c53acd02eb350b46f6b815a5ddbb69d163.json @@ -0,0 +1,137 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\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 edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM nats_trigger\n WHERE workspace_id = $1 AND path = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "nats_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "subjects", + "type_info": "VarcharArray" + }, + { + "ordinal": 4, + "name": "stream_name", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "consumer_name", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "use_jetstream", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 14, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 15, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 18, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 19, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "8ffd68b82cfce337fc32a6e80c8d56c53acd02eb350b46f6b815a5ddbb69d163" +} diff --git a/backend/.sqlx/query-9aa094ef0652f36d473866469e71cc41c7b1cf848d0b4beffa7eefc0fbf9d64f.json b/backend/.sqlx/query-9aa094ef0652f36d473866469e71cc41c7b1cf848d0b4beffa7eefc0fbf9d64f.json new file mode 100644 index 0000000000..fa80b194ec --- /dev/null +++ b/backend/.sqlx/query-9aa094ef0652f36d473866469e71cc41c7b1cf848d0b4beffa7eefc0fbf9d64f.json @@ -0,0 +1,160 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM\n gcp_trigger\n WHERE\n delivery_type != 'push'::DELIVERY_MODE AND\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "gcp_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscription_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "topic_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "delivery_type: _", + "type_info": { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "delivery_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 6, + "name": "subscription_mode: _", + "type_info": { + "Custom": { + "name": "gcp_subscription_mode", + "kind": { + "Enum": [ + "create_update", + "existing" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 19, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 20, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "9aa094ef0652f36d473866469e71cc41c7b1cf848d0b4beffa7eefc0fbf9d64f" +} diff --git a/backend/.sqlx/query-aaa8dde748e5bcd462122d3235e54c784399b2cac7b27f0de33e78bd77661565.json b/backend/.sqlx/query-aaa8dde748e5bcd462122d3235e54c784399b2cac7b27f0de33e78bd77661565.json new file mode 100644 index 0000000000..db7e60bbf5 --- /dev/null +++ b/backend/.sqlx/query-aaa8dde748e5bcd462122d3235e54c784399b2cac7b27f0de33e78bd77661565.json @@ -0,0 +1,62 @@ +{ + "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 is_async, \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, now(), $19, $20, $21, $22\n )\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Bool", + "Varchar", + "Varchar", + "Text", + "Bool", + "Bool", + { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + }, + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "aaa8dde748e5bcd462122d3235e54c784399b2cac7b27f0de33e78bd77661565" +} diff --git a/backend/.sqlx/query-b13d1dec373e70ffd58001cd17e9853e76ee8527e46afbfbc303528e512ebed9.json b/backend/.sqlx/query-b13d1dec373e70ffd58001cd17e9853e76ee8527e46afbfbc303528e512ebed9.json new file mode 100644 index 0000000000..154341c628 --- /dev/null +++ b/backend/.sqlx/query-b13d1dec373e70ffd58001cd17e9853e76ee8527e46afbfbc303528e512ebed9.json @@ -0,0 +1,163 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "gcp_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscription_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "topic_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "delivery_type: _", + "type_info": { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "delivery_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 6, + "name": "subscription_mode: _", + "type_info": { + "Custom": { + "name": "gcp_subscription_mode", + "kind": { + "Enum": [ + "create_update", + "existing" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 19, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 20, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "b13d1dec373e70ffd58001cd17e9853e76ee8527e46afbfbc303528e512ebed9" +} diff --git a/backend/.sqlx/query-bcc4f786fa2771a4a265aec8d1c403e660002503aad860caa12ebee2dbab3f0f.json b/backend/.sqlx/query-bcc4f786fa2771a4a265aec8d1c403e660002503aad860caa12ebee2dbab3f0f.json new file mode 100644 index 0000000000..5839ea94b4 --- /dev/null +++ b/backend/.sqlx/query-bcc4f786fa2771a4a265aec8d1c403e660002503aad860caa12ebee2dbab3f0f.json @@ -0,0 +1,134 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\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 edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM nats_trigger\n WHERE enabled IS TRUE AND (last_server_ping IS NULL OR last_server_ping < now() - interval '15 seconds')", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "nats_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "subjects", + "type_info": "VarcharArray" + }, + { + "ordinal": 4, + "name": "stream_name", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "consumer_name", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "use_jetstream", + "type_info": "Bool" + }, + { + "ordinal": 7, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 14, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 15, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 18, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 19, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "bcc4f786fa2771a4a265aec8d1c403e660002503aad860caa12ebee2dbab3f0f" +} diff --git a/backend/.sqlx/query-bf1fba73d3109aae48bda524cfafdf7f5b9b0e56396aed1b83a2c235222e8983.json b/backend/.sqlx/query-bf1fba73d3109aae48bda524cfafdf7f5b9b0e56396aed1b83a2c235222e8983.json new file mode 100644 index 0000000000..b939715091 --- /dev/null +++ b/backend/.sqlx/query-bf1fba73d3109aae48bda524cfafdf7f5b9b0e56396aed1b83a2c235222e8983.json @@ -0,0 +1,124 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT \n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n replication_slot_name,\n publication_name,\n postgres_resource_path,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\" \n FROM postgres_trigger\n WHERE workspace_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 4, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 7, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 9, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 10, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 12, + "name": "replication_slot_name", + "type_info": "Varchar" + }, + { + "ordinal": 13, + "name": "publication_name", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "postgres_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 15, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 16, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false, + false, + false, + false, + true, + true, + true + ] + }, + "hash": "bf1fba73d3109aae48bda524cfafdf7f5b9b0e56396aed1b83a2c235222e8983" +} diff --git a/backend/.sqlx/query-c22438f9bff27ccd3228a186a8093c551c30ed85eabe8ec11cafa6358f354c8b.json b/backend/.sqlx/query-c22438f9bff27ccd3228a186a8093c551c30ed85eabe8ec11cafa6358f354c8b.json new file mode 100644 index 0000000000..836709359e --- /dev/null +++ b/backend/.sqlx/query-c22438f9bff27ccd3228a186a8093c551c30ed85eabe8ec11cafa6358f354c8b.json @@ -0,0 +1,37 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sqs_trigger (\n aws_auth_resource_type,\n aws_resource_path,\n queue_url,\n message_attributes,\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, \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 )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + }, + "Varchar", + "Varchar", + "TextArray", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "c22438f9bff27ccd3228a186a8093c551c30ed85eabe8ec11cafa6358f354c8b" +} diff --git a/backend/.sqlx/query-c287f025c77c34e4d521204e7a787e794b461a7a965912bcb598619451eced31.json b/backend/.sqlx/query-c287f025c77c34e4d521204e7a787e794b461a7a965912bcb598619451eced31.json new file mode 100644 index 0000000000..e6facf2719 --- /dev/null +++ b/backend/.sqlx/query-c287f025c77c34e4d521204e7a787e794b461a7a965912bcb598619451eced31.json @@ -0,0 +1,136 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n workspace_id,\n path,\n url,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n filters AS \"filters: _\",\n initial_messages AS \"initial_messages: _\",\n url_runnable_args AS \"url_runnable_args: _\",\n can_return_message,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n websocket_trigger\n WHERE \n workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "url", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 8, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 10, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 11, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 12, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 13, + "name": "filters: _", + "type_info": "JsonbArray" + }, + { + "ordinal": 14, + "name": "initial_messages: _", + "type_info": "JsonbArray" + }, + { + "ordinal": 15, + "name": "url_runnable_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "can_return_message", + "type_info": "Bool" + }, + { + "ordinal": 17, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 18, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 19, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + false, + true, + true, + false, + true, + true, + true + ] + }, + "hash": "c287f025c77c34e4d521204e7a787e794b461a7a965912bcb598619451eced31" +} diff --git a/backend/.sqlx/query-c3730ae025a4dcc25b2713e3e670ce006b1ceed26e29d812d524c711d178167e.json b/backend/.sqlx/query-c3730ae025a4dcc25b2713e3e670ce006b1ceed26e29d812d524c711d178167e.json new file mode 100644 index 0000000000..daa7b50c85 --- /dev/null +++ b/backend/.sqlx/query-c3730ae025a4dcc25b2713e3e670ce006b1ceed26e29d812d524c711d178167e.json @@ -0,0 +1,37 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n sqs_trigger \n SET \n aws_auth_resource_type = $1,\n aws_resource_path = $2,\n queue_url = $3,\n message_attributes = $4, \n is_flow = $5, \n edited_by = $6, \n email = $7,\n script_path = $8,\n path = $9,\n edited_at = now(), \n error = NULL,\n server_id = NULL,\n error_handler_path = $12,\n error_handler_args = $13,\n retry = $14\n WHERE \n workspace_id = $10 AND \n path = $11\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + }, + "Varchar", + "Varchar", + "TextArray", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Text", + "Text", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "c3730ae025a4dcc25b2713e3e670ce006b1ceed26e29d812d524c711d178167e" +} diff --git a/backend/.sqlx/query-c4cd9668733b2b439ac7a911d15041ac728553e20adf722eb795d01d6650ff0f.json b/backend/.sqlx/query-c4cd9668733b2b439ac7a911d15041ac728553e20adf722eb795d01d6650ff0f.json new file mode 100644 index 0000000000..dc8629a042 --- /dev/null +++ b/backend/.sqlx/query-c4cd9668733b2b439ac7a911d15041ac728553e20adf722eb795d01d6650ff0f.json @@ -0,0 +1,28 @@ +{ + "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 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": { + "Left": [ + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Bool", + "JsonbArray", + "JsonbArray", + "Jsonb", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "c4cd9668733b2b439ac7a911d15041ac728553e20adf722eb795d01d6650ff0f" +} diff --git a/backend/.sqlx/query-cf76aedfdffc25057a64fc94fd6be0791d5970458810a2b465c27f02ad1d4b2f.json b/backend/.sqlx/query-cf76aedfdffc25057a64fc94fd6be0791d5970458810a2b465c27f02ad1d4b2f.json new file mode 100644 index 0000000000..6557fadd07 --- /dev/null +++ b/backend/.sqlx/query-cf76aedfdffc25057a64fc94fd6be0791d5970458810a2b465c27f02ad1d4b2f.json @@ -0,0 +1,122 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n workspace_id,\n path,\n script_path,\n replication_slot_name,\n publication_name,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n postgres_resource_path,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM\n postgres_trigger\n WHERE\n enabled IS TRUE\n AND (last_server_ping IS NULL OR\n last_server_ping < now() - interval '15 seconds'\n )\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "replication_slot_name", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "publication_name", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 6, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 9, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 11, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 12, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 13, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 14, + "name": "postgres_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 15, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 16, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false, + false, + true, + true, + true + ] + }, + "hash": "cf76aedfdffc25057a64fc94fd6be0791d5970458810a2b465c27f02ad1d4b2f" +} diff --git a/backend/.sqlx/query-d3bc837633eb92cd9a8d37ad09a72e8da54885cdd135e0877dec9cf407064158.json b/backend/.sqlx/query-d3bc837633eb92cd9a8d37ad09a72e8da54885cdd135e0877dec9cf407064158.json new file mode 100644 index 0000000000..5c129f5088 --- /dev/null +++ b/backend/.sqlx/query-d3bc837633eb92cd9a8d37ad09a72e8da54885cdd135e0877dec9cf407064158.json @@ -0,0 +1,141 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n aws_auth_resource_type AS \"aws_auth_resource_type: _\",\n aws_resource_path,\n message_attributes,\n queue_url,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n sqs_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "aws_auth_resource_type: _", + "type_info": { + "Custom": { + "name": "aws_auth_resource_type", + "kind": { + "Enum": [ + "oidc", + "credentials" + ] + } + } + } + }, + { + "ordinal": 1, + "name": "aws_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "message_attributes", + "type_info": "TextArray" + }, + { + "ordinal": 3, + "name": "queue_url", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 8, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 10, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 11, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 14, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 16, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 17, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 18, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + false, + true, + true, + true + ] + }, + "hash": "d3bc837633eb92cd9a8d37ad09a72e8da54885cdd135e0877dec9cf407064158" +} diff --git a/backend/.sqlx/query-d580bac9f4be4329da29781efa00cd0b5379955a42b7e070c7e11f8479441482.json b/backend/.sqlx/query-d580bac9f4be4329da29781efa00cd0b5379955a42b7e070c7e11f8479441482.json new file mode 100644 index 0000000000..2e063cf7d3 --- /dev/null +++ b/backend/.sqlx/query-d580bac9f4be4329da29781efa00cd0b5379955a42b7e070c7e11f8479441482.json @@ -0,0 +1,59 @@ +{ + "db_name": "PostgreSQL", + "query": "\n UPDATE \n http_trigger \n SET \n workspaced_route = $1,\n wrap_body = $2,\n raw_string = $3,\n authentication_resource_path = $4,\n script_path = $5, \n path = $6, \n is_flow = $7, \n http_method = $8, \n static_asset_config = $9, \n edited_by = $10, \n email = $11, \n is_async = $12, \n authentication_method = $13, \n edited_at = now(), \n is_static_website = $14,\n error_handler_path = $15,\n error_handler_args = $16,\n retry = $17\n WHERE \n workspace_id = $18 AND \n path = $19\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bool", + "Bool", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + }, + "Bool", + "Varchar", + "Jsonb", + "Jsonb", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "d580bac9f4be4329da29781efa00cd0b5379955a42b7e070c7e11f8479441482" +} diff --git a/backend/.sqlx/query-e82b99583d5d3004b4ad6606e7906b8f95b33c6137f136edabb0255bfa6a98f7.json b/backend/.sqlx/query-e82b99583d5d3004b4ad6606e7906b8f95b33c6137f136edabb0255bfa6a98f7.json new file mode 100644 index 0000000000..1af4d216a6 --- /dev/null +++ b/backend/.sqlx/query-e82b99583d5d3004b4ad6606e7906b8f95b33c6137f136edabb0255bfa6a98f7.json @@ -0,0 +1,162 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n gcp_resource_path,\n subscription_id,\n topic_id,\n workspace_id,\n delivery_type AS \"delivery_type: _\",\n delivery_config AS \"delivery_config: _\",\n subscription_mode AS \"subscription_mode: _\",\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n gcp_trigger\n WHERE \n workspace_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "gcp_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscription_id", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "topic_id", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "delivery_type: _", + "type_info": { + "Custom": { + "name": "delivery_mode", + "kind": { + "Enum": [ + "push", + "pull" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "delivery_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 6, + "name": "subscription_mode: _", + "type_info": { + "Custom": { + "name": "gcp_subscription_mode", + "kind": { + "Enum": [ + "create_update", + "existing" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 19, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 20, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "e82b99583d5d3004b4ad6606e7906b8f95b33c6137f136edabb0255bfa6a98f7" +} diff --git a/backend/.sqlx/query-e9b4938b8c5fbd57845cf560fc2fd2d5749015c66d7de277081598ba3bb6dd8f.json b/backend/.sqlx/query-e9b4938b8c5fbd57845cf560fc2fd2d5749015c66d7de277081598ba3bb6dd8f.json new file mode 100644 index 0000000000..5b8934eec6 --- /dev/null +++ b/backend/.sqlx/query-e9b4938b8c5fbd57845cf560fc2fd2d5749015c66d7de277081598ba3bb6dd8f.json @@ -0,0 +1,39 @@ +{ + "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, \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 )", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "JsonbArray", + { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + }, + "Varchar", + "Jsonb", + "Jsonb", + "Varchar", + "Varchar", + "Varchar", + "Bool", + "Varchar", + "Bool", + "Varchar", + "Varchar", + "Jsonb", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "e9b4938b8c5fbd57845cf560fc2fd2d5749015c66d7de277081598ba3bb6dd8f" +} diff --git a/backend/.sqlx/query-ea59131c1fba8fb14a95b4a486db29ba44c4ff4d2c5b38369f0ede61d4708a21.json b/backend/.sqlx/query-ea59131c1fba8fb14a95b4a486db29ba44c4ff4d2c5b38369f0ede61d4708a21.json new file mode 100644 index 0000000000..f9c079f0f6 --- /dev/null +++ b/backend/.sqlx/query-ea59131c1fba8fb14a95b4a486db29ba44c4ff4d2c5b38369f0ede61d4708a21.json @@ -0,0 +1,188 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n workspace_id, \n path, \n route_path, \n route_path_key,\n workspaced_route,\n script_path, \n summary,\n description,\n is_flow, \n http_method as \"http_method: _\", \n edited_by, \n email, \n edited_at, \n extra_perms, \n is_async, \n authentication_method as \"authentication_method: _\", \n static_asset_config as \"static_asset_config: _\", \n is_static_website,\n authentication_resource_path,\n wrap_body,\n raw_string,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n http_trigger\n WHERE \n workspace_id = $1 AND \n path = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "route_path", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "route_path_key", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "workspaced_route", + "type_info": "Bool" + }, + { + "ordinal": 5, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "summary", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "description", + "type_info": "Text" + }, + { + "ordinal": 8, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 9, + "name": "http_method: _", + "type_info": { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + } + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 14, + "name": "is_async", + "type_info": "Bool" + }, + { + "ordinal": 15, + "name": "authentication_method: _", + "type_info": { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + } + }, + { + "ordinal": 16, + "name": "static_asset_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 17, + "name": "is_static_website", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "authentication_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 19, + "name": "wrap_body", + "type_info": "Bool" + }, + { + "ordinal": 20, + "name": "raw_string", + "type_info": "Bool" + }, + { + "ordinal": 21, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 22, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 23, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + false, + true, + true, + true + ] + }, + "hash": "ea59131c1fba8fb14a95b4a486db29ba44c4ff4d2c5b38369f0ede61d4708a21" +} diff --git a/backend/.sqlx/query-eebc9b609d9b595524223ada91e0f7fa1ca50b45cc82e2fd9d4e61768b258735.json b/backend/.sqlx/query-eebc9b609d9b595524223ada91e0f7fa1ca50b45cc82e2fd9d4e61768b258735.json new file mode 100644 index 0000000000..1a9deeb950 --- /dev/null +++ b/backend/.sqlx/query-eebc9b609d9b595524223ada91e0f7fa1ca50b45cc82e2fd9d4e61768b258735.json @@ -0,0 +1,150 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n mqtt_resource_path,\n subscribe_topics as \"subscribe_topics: _\",\n v3_config as \"v3_config: _\",\n v5_config as \"v5_config: _\",\n client_version AS \"client_version: _\",\n client_id,\n workspace_id,\n path,\n script_path,\n is_flow,\n edited_by,\n email,\n edited_at,\n server_id,\n last_server_ping,\n extra_perms,\n error,\n enabled,\n error_handler_path,\n error_handler_args as \"error_handler_args: _\",\n retry as \"retry: _\"\n FROM \n mqtt_trigger\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "mqtt_resource_path", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "subscribe_topics: _", + "type_info": "JsonbArray" + }, + { + "ordinal": 2, + "name": "v3_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 3, + "name": "v5_config: _", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "client_version: _", + "type_info": { + "Custom": { + "name": "mqtt_client_version", + "kind": { + "Enum": [ + "v3", + "v5" + ] + } + } + } + }, + { + "ordinal": 5, + "name": "client_id", + "type_info": "Varchar" + }, + { + "ordinal": 6, + "name": "workspace_id", + "type_info": "Varchar" + }, + { + "ordinal": 7, + "name": "path", + "type_info": "Varchar" + }, + { + "ordinal": 8, + "name": "script_path", + "type_info": "Varchar" + }, + { + "ordinal": 9, + "name": "is_flow", + "type_info": "Bool" + }, + { + "ordinal": 10, + "name": "edited_by", + "type_info": "Varchar" + }, + { + "ordinal": 11, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 12, + "name": "edited_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "server_id", + "type_info": "Varchar" + }, + { + "ordinal": 14, + "name": "last_server_ping", + "type_info": "Timestamptz" + }, + { + "ordinal": 15, + "name": "extra_perms", + "type_info": "Jsonb" + }, + { + "ordinal": 16, + "name": "error", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "enabled", + "type_info": "Bool" + }, + { + "ordinal": 18, + "name": "error_handler_path", + "type_info": "Varchar" + }, + { + "ordinal": 19, + "name": "error_handler_args: _", + "type_info": "Jsonb" + }, + { + "ordinal": 20, + "name": "retry: _", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + true, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + true, + false, + true, + true, + true + ] + }, + "hash": "eebc9b609d9b595524223ada91e0f7fa1ca50b45cc82e2fd9d4e61768b258735" +} diff --git a/backend/.sqlx/query-f2a78401a363442f165d018d61a294b4575e2cdc31e8da3d453addd0edc3edf6.json b/backend/.sqlx/query-f2a78401a363442f165d018d61a294b4575e2cdc31e8da3d453addd0edc3edf6.json new file mode 100644 index 0000000000..c9c1ac057e --- /dev/null +++ b/backend/.sqlx/query-f2a78401a363442f165d018d61a294b4575e2cdc31e8da3d453addd0edc3edf6.json @@ -0,0 +1,63 @@ +{ + "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 http_method = $10, \n static_asset_config = $11, \n edited_by = $12, \n email = $13, \n is_async = $14, \n authentication_method = $15, \n summary = $16,\n description = $17,\n edited_at = now(), \n is_static_website = $18,\n error_handler_path = $19,\n error_handler_args = $20,\n retry = $21\n WHERE \n workspace_id = $22 AND \n path = $23\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Bool", + "Bool", + "Bool", + "Varchar", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "http_method", + "kind": { + "Enum": [ + "get", + "post", + "put", + "delete", + "patch" + ] + } + } + }, + "Jsonb", + "Varchar", + "Varchar", + "Bool", + { + "Custom": { + "name": "authentication_method", + "kind": { + "Enum": [ + "none", + "windmill", + "api_key", + "basic_http", + "custom_script", + "signature" + ] + } + } + }, + "Varchar", + "Text", + "Bool", + "Varchar", + "Jsonb", + "Jsonb", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "f2a78401a363442f165d018d61a294b4575e2cdc31e8da3d453addd0edc3edf6" +} diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 2f71d8d55d..68875b30be 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -c08ce449d9b443c0b9da4c157bc6c7267848bcd3 +dd87498c020e4598b55fd7de622ff3b980eab5f5 diff --git a/backend/migrations/20250707120256_trigger_retries_and_error_handling.down.sql b/backend/migrations/20250707120256_trigger_retries_and_error_handling.down.sql new file mode 100644 index 0000000000..02fd7fd1bc --- /dev/null +++ b/backend/migrations/20250707120256_trigger_retries_and_error_handling.down.sql @@ -0,0 +1,47 @@ +-- Remove error handler and retry fields from http_trigger +ALTER TABLE http_trigger + DROP COLUMN error_handler_path, + DROP COLUMN error_handler_args, + DROP COLUMN retry; + +-- Remove error handler and retry fields from nats_trigger +ALTER TABLE nats_trigger + DROP COLUMN error_handler_path, + DROP COLUMN error_handler_args, + DROP COLUMN retry; + +-- Remove error handler and retry fields from postgres_trigger +ALTER TABLE postgres_trigger + DROP COLUMN error_handler_path, + DROP COLUMN error_handler_args, + DROP COLUMN retry; + +-- Remove error handler and retry fields from mqtt_trigger +ALTER TABLE mqtt_trigger + DROP COLUMN error_handler_path, + DROP COLUMN error_handler_args, + DROP COLUMN retry; + +-- Remove error handler and retry fields from kafka_trigger +ALTER TABLE kafka_trigger + DROP COLUMN error_handler_path, + DROP COLUMN error_handler_args, + DROP COLUMN retry; + +-- Remove error handler and retry fields from websocket_trigger +ALTER TABLE websocket_trigger + DROP COLUMN error_handler_path, + DROP COLUMN error_handler_args, + DROP COLUMN retry; + +-- Remove error handler and retry fields from gcp_trigger +ALTER TABLE gcp_trigger + DROP COLUMN error_handler_path, + DROP COLUMN error_handler_args, + DROP COLUMN retry; + +-- Remove error handler and retry fields from sqs_trigger +ALTER TABLE sqs_trigger + DROP COLUMN error_handler_path, + DROP COLUMN error_handler_args, + DROP COLUMN retry; \ No newline at end of file diff --git a/backend/migrations/20250707120256_trigger_retries_and_error_handling.up.sql b/backend/migrations/20250707120256_trigger_retries_and_error_handling.up.sql new file mode 100644 index 0000000000..25bd8e0c13 --- /dev/null +++ b/backend/migrations/20250707120256_trigger_retries_and_error_handling.up.sql @@ -0,0 +1,47 @@ +-- Add error handler and retry fields to http_trigger +ALTER TABLE http_trigger + ADD COLUMN error_handler_path VARCHAR(255) NULL, + ADD COLUMN error_handler_args JSONB NULL, + ADD COLUMN retry JSONB NULL; + +-- Add error handler and retry fields to nats_trigger +ALTER TABLE nats_trigger + ADD COLUMN error_handler_path VARCHAR(255) NULL, + ADD COLUMN error_handler_args JSONB NULL, + ADD COLUMN retry JSONB NULL; + +-- Add error handler and retry fields to postgres_trigger +ALTER TABLE postgres_trigger + ADD COLUMN error_handler_path VARCHAR(255) NULL, + ADD COLUMN error_handler_args JSONB NULL, + ADD COLUMN retry JSONB NULL; + +-- Add error handler and retry fields to mqtt_trigger +ALTER TABLE mqtt_trigger + ADD COLUMN error_handler_path VARCHAR(255) NULL, + ADD COLUMN error_handler_args JSONB NULL, + ADD COLUMN retry JSONB NULL; + +-- Add error handler and retry fields to kafka_trigger +ALTER TABLE kafka_trigger + ADD COLUMN error_handler_path VARCHAR(255) NULL, + ADD COLUMN error_handler_args JSONB NULL, + ADD COLUMN retry JSONB NULL; + +-- Add error handler and retry fields to websocket_trigger +ALTER TABLE websocket_trigger + ADD COLUMN error_handler_path VARCHAR(255) NULL, + ADD COLUMN error_handler_args JSONB NULL, + ADD COLUMN retry JSONB NULL; + +-- Add error handler and retry fields to gcp_trigger +ALTER TABLE gcp_trigger + ADD COLUMN error_handler_path VARCHAR(255) NULL, + ADD COLUMN error_handler_args JSONB NULL, + ADD COLUMN retry JSONB NULL; + +-- Add error handler and retry fields to sqs_trigger +ALTER TABLE sqs_trigger + ADD COLUMN error_handler_path VARCHAR(255) NULL, + ADD COLUMN error_handler_args JSONB NULL, + ADD COLUMN retry JSONB NULL; \ No newline at end of file diff --git a/backend/tests/worker.rs b/backend/tests/worker.rs index 4bce2756da..28bce55024 100644 --- a/backend/tests/worker.rs +++ b/backend/tests/worker.rs @@ -1163,6 +1163,7 @@ async fn test_deno_flow(db: Pool) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, FlowModule { id: "b".to_string(), @@ -1206,6 +1207,7 @@ async fn test_deno_flow(db: Pool) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }], modules_node: None, } @@ -1223,6 +1225,7 @@ async fn test_deno_flow(db: Pool) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, ], same_worker: false, @@ -1334,6 +1337,7 @@ async fn test_deno_flow_same_worker(db: Pool) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, FlowModule { id: "b".to_string(), @@ -1387,6 +1391,7 @@ async fn test_deno_flow_same_worker(db: Pool) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, FlowModule { id: "e".to_string(), @@ -1427,6 +1432,7 @@ async fn test_deno_flow_same_worker(db: Pool) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, ], modules_node: None, @@ -1444,6 +1450,7 @@ async fn test_deno_flow_same_worker(db: Pool) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, FlowModule { id: "c".to_string(), @@ -1490,6 +1497,7 @@ async fn test_deno_flow_same_worker(db: Pool) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, ], same_worker: true, diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 7379428089..878c60591d 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -15185,6 +15185,12 @@ components: type: boolean raw_string: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - route_path @@ -15238,6 +15244,12 @@ components: type: boolean raw_string: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path @@ -15291,6 +15303,12 @@ components: type: boolean raw_string: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path - script_path @@ -15367,6 +15385,12 @@ components: $ref: "#/components/schemas/ScriptArgs" can_return_message: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - url @@ -15406,6 +15430,12 @@ components: $ref: "#/components/schemas/ScriptArgs" can_return_message: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path @@ -15445,6 +15475,12 @@ components: $ref: "#/components/schemas/ScriptArgs" can_return_message: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path @@ -15494,7 +15530,7 @@ components: properties: clean_start: type: boolean - topic_alias: + topic_alias_maximum: type: number session_expiry_interval: type: number @@ -15542,6 +15578,12 @@ components: type: string enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - enabled @@ -15573,6 +15615,12 @@ components: type: boolean enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path - script_path @@ -15605,6 +15653,12 @@ components: type: boolean enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path - script_path @@ -15656,6 +15710,12 @@ components: type: string enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - gcp_resource_path - topic_id @@ -15696,6 +15756,12 @@ components: type: boolean enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path - script_path @@ -15750,6 +15816,12 @@ components: type: string enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - queue_url @@ -15778,6 +15850,12 @@ components: type: boolean enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - queue_url - aws_resource_path @@ -15807,6 +15885,12 @@ components: type: boolean enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - queue_url - aws_resource_path @@ -15912,6 +15996,12 @@ components: last_server_ping: type: string format: date-time + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - enabled - postgres_resource_path @@ -15937,6 +16027,12 @@ components: type: string publication: $ref: "#/components/schemas/PublicationData" + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path - script_path @@ -15963,6 +16059,12 @@ components: type: string publication: $ref: "#/components/schemas/PublicationData" + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path - script_path @@ -15994,6 +16096,12 @@ components: type: string enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - kafka_resource_path @@ -16020,6 +16128,12 @@ components: type: string enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path @@ -16046,6 +16160,12 @@ components: type: string is_flow: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path @@ -16081,6 +16201,12 @@ components: type: string enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - nats_resource_path @@ -16111,6 +16237,12 @@ components: type: string enabled: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path @@ -16141,6 +16273,12 @@ components: type: string is_flow: type: boolean + error_handler_path: + type: string + error_handler_args: + $ref: "#/components/schemas/ScriptArgs" + retry: + $ref: "../../openflow.openapi.yaml#/components/schemas/Retry" required: - path diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index f420f2e074..aa7bf29475 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -1364,6 +1364,7 @@ mod tests { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, FlowModule { id: "b".to_string(), @@ -1396,6 +1397,7 @@ mod tests { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, FlowModule { id: "c".to_string(), @@ -1425,6 +1427,7 @@ mod tests { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }, ], failure_module: Some(Box::new(FlowModule { @@ -1453,6 +1456,7 @@ mod tests { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, })), preprocessor_module: None, same_worker: false, diff --git a/backend/windmill-api/src/gcp_triggers_oss.rs b/backend/windmill-api/src/gcp_triggers_oss.rs index 78005309bc..17363e28c0 100644 --- a/backend/windmill-api/src/gcp_triggers_oss.rs +++ b/backend/windmill-api/src/gcp_triggers_oss.rs @@ -154,6 +154,12 @@ pub struct GcpTrigger { pub server_id: Option, pub last_server_ping: Option>, pub enabled: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_args: Option>>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub retry: Option>, } #[cfg(not(feature = "private"))] impl TriggerJobArgs for GcpTrigger { diff --git a/backend/windmill-api/src/http_triggers.rs b/backend/windmill-api/src/http_triggers.rs index a42bdcac53..27607478db 100644 --- a/backend/windmill-api/src/http_triggers.rs +++ b/backend/windmill-api/src/http_triggers.rs @@ -3,15 +3,13 @@ use crate::http_trigger_args::{HttpMethod, RawHttpTriggerArgs}; #[cfg(feature = "parquet")] use crate::job_helpers_oss::get_workspace_s3_resource; use crate::resources::try_get_resource_from_db_as; -use crate::trigger_helpers::{get_runnable_format, RunnableId}; +use crate::trigger_helpers::{ + get_runnable_format, trigger_runnable, trigger_runnable_and_wait_for_result, RunnableId, +}; use crate::utils::{non_empty_str, ExpiringCacheEntry}; use crate::{ auth::{AuthCache, OptTokened}, db::{ApiAuthed, DB}, - jobs::{ - run_flow_by_path_inner, run_script_by_path_inner, run_wait_result_flow_by_path_internal, - run_wait_result_script_by_path_internal, RunJobQuery, - }, users::fetch_api_authed, }; use anyhow::anyhow; @@ -27,6 +25,7 @@ use http::header::IF_NONE_MATCH; use http::{HeaderMap, StatusCode}; use quick_cache::sync::Cache; use serde::{Deserialize, Serialize}; +use serde_json::value::RawValue; use sql_builder::{bind::Bind, SqlBuilder}; use sqlx::prelude::FromRow; use sqlx::PgConnection; @@ -37,6 +36,7 @@ use tokio::sync::{RwLock, RwLockReadGuard}; use tower_http::cors::CorsLayer; use windmill_audit::{audit_oss::audit_log, ActionKind}; use windmill_common::error::{Error, Result as WindmillResult}; +use windmill_common::flows::Retry; #[cfg(feature = "parquet")] use windmill_common::s3_helpers::build_object_store_client; use windmill_common::{ @@ -119,6 +119,9 @@ struct NewTrigger { is_static_website: bool, wrap_body: Option, raw_string: Option, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } #[derive(FromRow, Serialize)] @@ -145,6 +148,11 @@ pub struct HttpTrigger { pub workspaced_route: bool, pub wrap_body: bool, pub raw_string: bool, + pub error_handler_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_args: Option>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub retry: Option>>, } #[derive(Deserialize)] @@ -165,6 +173,9 @@ struct EditTrigger { is_static_website: bool, wrap_body: Option, raw_string: Option, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } #[derive(Deserialize)] @@ -208,6 +219,9 @@ async fn list_triggers( "static_asset_config", "is_static_website", "authentication_resource_path", + "error_handler_path", + "error_handler_args", + "retry", ]) .order_by("edited_at", true) .and_where("workspace_id = ?".bind(&w_id)) @@ -265,7 +279,10 @@ async fn get_trigger( is_static_website, authentication_resource_path, wrap_body, - raw_string + raw_string, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM http_trigger WHERE @@ -338,10 +355,13 @@ async fn create_trigger_inner( edited_by, email, edited_at, - is_static_website + is_static_website, + error_handler_path, + error_handler_args, + retry ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, now(), $19 + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, now(), $19, $20, $21, $22 ) "#, w_id, @@ -362,7 +382,10 @@ async fn create_trigger_inner( new_http_trigger.static_asset_config as _, &authed.username, &authed.email, - new_http_trigger.is_static_website + new_http_trigger.is_static_website, + new_http_trigger.error_handler_path, + new_http_trigger.error_handler_args as _, + new_http_trigger.retry as _, ) .execute(&mut *tx) .await?; @@ -616,10 +639,13 @@ async fn update_trigger( summary = $16, description = $17, edited_at = now(), - is_static_website = $18 + is_static_website = $18, + error_handler_path = $19, + error_handler_args = $20, + retry = $21 WHERE - workspace_id = $19 AND - path = $20 + workspace_id = $22 AND + path = $23 "#, route_path, &route_path_key, @@ -639,6 +665,9 @@ async fn update_trigger( ct.summary, ct.description, ct.is_static_website, + ct.error_handler_path, + ct.error_handler_args as _, + ct.retry as _, w_id, path, ) @@ -665,10 +694,13 @@ async fn update_trigger( is_async = $12, authentication_method = $13, edited_at = now(), - is_static_website = $14 + is_static_website = $14, + error_handler_path = $15, + error_handler_args = $16, + retry = $17 WHERE - workspace_id = $15 AND - path = $16 + workspace_id = $18 AND + path = $19 "#, ct.workspaced_route, ct.wrap_body, @@ -684,6 +716,9 @@ async fn update_trigger( ct.is_async, ct.authentication_method as _, ct.is_static_website, + ct.error_handler_path, + ct.error_handler_args as _, + ct.retry as _, w_id, path, ) @@ -893,6 +928,9 @@ pub struct TriggerRoute { workspaced_route: bool, wrap_body: bool, raw_string: bool, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } pub struct RoutersCache { @@ -979,7 +1017,10 @@ pub async fn refresh_routers(db: &DB) -> Result<(bool, RwLockReadGuard<'_, Route wrap_body, raw_string, workspaced_route, - is_static_website + is_static_website, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM http_trigger WHERE @@ -1386,61 +1427,37 @@ async fn route_job( ) .map_err(|e| e.into_response())?; - let run_query = RunJobQuery::default(); - - let response = if trigger.is_flow { - if trigger.is_async { - run_flow_by_path_inner( - authed, - db, - user_db, - trigger.workspace_id.clone(), - StripPath(trigger.script_path.to_owned()), - run_query, - args, - ) - .await - .into_response() - } else { - run_wait_result_flow_by_path_internal( - db, - run_query, - StripPath(trigger.script_path.to_owned()), - authed, - user_db, - args, - trigger.workspace_id.clone(), - ) - .await - .into_response() - } + if trigger.is_async { + 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), + ) + .await + .map_err(|e| e.into_response()) } else { - if trigger.is_async { - run_script_by_path_inner( - authed, - db, - user_db, - trigger.workspace_id.clone(), - StripPath(trigger.script_path.to_owned()), - run_query, - args, - ) - .await - .into_response() - } else { - run_wait_result_script_by_path_internal( - db, - run_query, - StripPath(trigger.script_path.to_owned()), - authed, - user_db, - trigger.workspace_id.clone(), - args, - ) - .await - .into_response() - } - }; - - Ok(response) + trigger_runnable_and_wait_for_result( + &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), + ) + .await + .map_err(|e| e.into_response()) + } } diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index f0421bc031..b178b20934 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -22,10 +22,13 @@ use std::ops::{Deref, DerefMut}; use std::str::FromStr; use tokio::io::AsyncReadExt; use tower::ServiceBuilder; -use windmill_common::auth::{is_super_admin_email, TOKEN_PREFIX_LEN}; +use windmill_common::auth::TOKEN_PREFIX_LEN; use windmill_common::error::JsonResult; use windmill_common::flow_status::{JobResult, RestartedFrom}; -use windmill_common::jobs::{format_completed_job_result, format_result, ENTRYPOINT_OVERRIDE}; +use windmill_common::jobs::{ + check_tag_available_for_workspace_internal, format_completed_job_result, format_result, + ENTRYPOINT_OVERRIDE, +}; use windmill_common::utils::WarnAfterExt; use windmill_common::worker::{Connection, CLOUD_HOSTED, TMP_DIR}; @@ -65,7 +68,7 @@ use tower_http::cors::{Any, CorsLayer}; use urlencoding::encode; use windmill_audit::audit_oss::{audit_log, AuditAuthor}; use windmill_audit::ActionKind; -use windmill_common::worker::{to_raw_value, CUSTOM_TAGS_PER_WORKSPACE}; +use windmill_common::worker::to_raw_value; use windmill_common::{ cache, db::UserDB, @@ -352,7 +355,9 @@ async fn cancel_job_api( username: "anonymous".to_string(), username_override: None, email: "anonymous".to_string(), - token_prefix: opt_tokened.token.map(|s| s[0..TOKEN_PREFIX_LEN].to_string()), + token_prefix: opt_tokened + .token + .map(|s| s[0..TOKEN_PREFIX_LEN].to_string()), }, }; let (mut tx, job_option) = tokio::time::timeout( @@ -568,7 +573,14 @@ async fn get_flow_job_debug_info( } } - log_job_view(&db, opt_authed.as_ref(), tokened_o.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + tokened_o.token.as_deref(), + &w_id, + &id, + ) + .await?; Ok(Json(jobs).into_response()) } else { @@ -648,7 +660,14 @@ async fn get_job( let mut job = get.fetch(&db, id, &w_id).await?; job.fetch_outstanding_wait_time(&db).await?; - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &id, + ) + .await?; Ok(Json(job).into_response()) } @@ -1134,7 +1153,14 @@ async fn get_job_logs( } let logs = record.logs.unwrap_or_default(); - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &id, + ) + .await?; #[cfg(all(feature = "enterprise", feature = "parquet"))] if let Some(r) = get_logs_from_store(record.log_offset, &logs, &record.log_file_index).await @@ -1171,7 +1197,14 @@ async fn get_job_logs( } let logs = text.logs.unwrap_or_default(); - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &id, + ) + .await?; #[cfg(all(feature = "enterprise", feature = "parquet"))] if let Some(r) = @@ -1221,7 +1254,14 @@ async fn get_args( )); } - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &id, + ) + .await?; Ok(Json(record.args.map(|x| x.0).unwrap_or_default())) } else { @@ -1242,7 +1282,14 @@ async fn get_args( )); } - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &id, + ) + .await?; Ok(Json(record.args.map(|x| x.0).unwrap_or_default())) } @@ -2112,7 +2159,9 @@ async fn resume_suspended_job_internal( email: approver.clone(), username: approver.clone(), username_override: None, - token_prefix: opt_tokened.token.map(|s| s[0..TOKEN_PREFIX_LEN].to_string()), + token_prefix: opt_tokened + .token + .map(|s| s[0..TOKEN_PREFIX_LEN].to_string()), }, }; audit_log( @@ -2289,7 +2338,16 @@ pub async fn cancel_suspended_job( QueryOrBody(value): QueryOrBody, ) -> error::Result { resume_suspended_job_internal( - value, db, w_id, job_id, resume_id, approver, secret, authed, opt_tokened, false, + value, + db, + w_id, + job_id, + resume_id, + approver, + secret, + authed, + opt_tokened, + false, ) .await } @@ -2374,7 +2432,14 @@ pub async fn get_suspended_job_flow( approvers_from_status }; - log_job_view(&db, authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &job).await?; + log_job_view( + &db, + authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &job, + ) + .await?; Ok(Json(SuspendedJobFlow { job: flow, approvers }).into_response()) } @@ -3110,62 +3175,18 @@ pub fn add_raw_string( return args; } -async fn check_tag_available_for_workspace( +pub async fn check_tag_available_for_workspace( db: &DB, w_id: &str, tag: &Option, authed: &ApiAuthed, ) -> error::Result<()> { - if let Some(tag) = tag { - if tag.is_empty() { - return Ok(()); - } - + if let Some(tag) = tag.as_deref().filter(|t| !t.is_empty()) { let tags = get_scope_tags(authed); - let mut is_tag_available_in_workspace = None; - let mut is_tag_in_workspace_custom_tags = false; - - if let Some(tags) = tags.as_ref() { - is_tag_available_in_workspace = Some(tags.contains(&tag.as_str())); - } - - let custom_tags_per_w = CUSTOM_TAGS_PER_WORKSPACE.read().await; - if custom_tags_per_w.0.contains(&tag.to_string()) { - is_tag_in_workspace_custom_tags = true; - } else if custom_tags_per_w.1.contains_key(tag) - && custom_tags_per_w - .1 - .get(tag) - .unwrap() - .contains(&w_id.to_string()) - { - is_tag_in_workspace_custom_tags = true; - } - - match is_tag_available_in_workspace { - Some(true) | None => { - if is_tag_in_workspace_custom_tags { - return Ok(()); - } - } - _ => {} - } - - if !is_super_admin_email(db, &authed.email).await? { - if tags.is_some() && is_tag_available_in_workspace.is_some() { - return Err(Error::BadRequest(format!( - "Tag {tag} is not available in your scope" - ))); - } - - return Err(error::Error::BadRequest(format!( - "Only super admins are allowed to use tags that are not included in the allowed CUSTOM_TAGS: {:?}", - custom_tags_per_w - ))); - } + check_tag_available_for_workspace_internal(&db, w_id, tag, &authed.email, tags).await + } else { + Ok(()) } - - return Ok(()); } #[cfg(feature = "enterprise")] @@ -3406,8 +3427,8 @@ async fn batch_rerun_handle_job( PushArgsOwned { extra: None, args }, ) .await; - if let Ok((_, uuid)) = result { - return Ok(uuid); + if let Ok(uuid) = result { + return Ok(uuid.to_string()); } } JobKind::Script => { @@ -3434,8 +3455,8 @@ async fn batch_rerun_handle_job( ) .await }; - if let Ok((_, uuid)) = result { - return Ok(uuid); + if let Ok((uuid, _)) = result { + return Ok(uuid.to_string()); } } _ => {} @@ -3463,7 +3484,10 @@ pub async fn run_flow_by_path( ) .await?; - run_flow_by_path_inner(authed, db, user_db, w_id, flow_path, run_query, args).await + let uuid = + run_flow_by_path_inner(authed, db, user_db, w_id, flow_path, run_query, args).await?; + + Ok((StatusCode::CREATED, uuid.to_string())) } pub async fn run_flow_by_path_inner( @@ -3474,7 +3498,7 @@ pub async fn run_flow_by_path_inner( flow_path: StripPath, run_query: RunJobQuery, args: PushArgsOwned, -) -> error::Result<(StatusCode, String)> { +) -> error::Result { #[cfg(feature = "enterprise")] check_license_key_valid().await?; let flow_path = flow_path.to_path(); @@ -3549,7 +3573,7 @@ pub async fn run_flow_by_path_inner( ) .await?; tx.commit().await?; - Ok((StatusCode::CREATED, uuid.to_string())) + Ok(uuid) } #[cfg(not(feature = "enterprise"))] @@ -3664,7 +3688,10 @@ pub async fn run_script_by_path( ) .await?; - run_script_by_path_inner(authed, db, user_db, w_id, script_path, run_query, args).await + let (uuid, _) = + run_script_by_path_inner(authed, db, user_db, w_id, script_path, run_query, args).await?; + + Ok((StatusCode::CREATED, uuid.to_string())) } pub async fn run_script_by_path_inner( @@ -3675,7 +3702,7 @@ pub async fn run_script_by_path_inner( script_path: StripPath, run_query: RunJobQuery, args: PushArgsOwned, -) -> error::Result<(StatusCode, String)> { +) -> error::Result<(Uuid, Option)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -3684,7 +3711,7 @@ pub async fn run_script_by_path_inner( check_scopes(&authed, || format!("run:script/{script_path}"))?; let mut tx = user_db.clone().begin(&authed).await?; - let (job_payload, tag, _delete_after_use, timeout, on_behalf_of) = + let (job_payload, tag, delete_after_use, timeout, on_behalf_of) = script_path_to_payload(script_path, &mut *tx, &w_id, run_query.skip_preprocessor).await?; drop(tx); let scheduled_for = run_query.get_scheduled_for(&db).await?; @@ -3736,7 +3763,8 @@ pub async fn run_script_by_path_inner( ) .await?; tx.commit().await?; - Ok((StatusCode::CREATED, uuid.to_string())) + + Ok((uuid, delete_after_use)) } #[derive(Deserialize)] @@ -4083,16 +4111,7 @@ pub async fn run_wait_result_internal( } } -pub async fn run_wait_result( - db: &DB, - uuid: Uuid, - w_id: String, - node_id_for_empty_return: Option, - username: &str, -) -> error::Result { - let (result, success) = - run_wait_result_internal(db, uuid, w_id, node_id_for_empty_return, username).await?; - +pub fn result_to_response(result: Box, success: bool) -> error::Result { let composite_result = serde_json::from_str::(result.get()); match composite_result { Ok(WindmillCompositeResult { @@ -4181,7 +4200,20 @@ pub async fn run_wait_result( } } -async fn delete_job_metadata_after_use(db: &DB, job_uuid: Uuid) -> Result<(), Error> { +pub async fn run_wait_result( + db: &DB, + uuid: Uuid, + w_id: String, + node_id_for_empty_return: Option, + username: &str, +) -> error::Result { + let (result, success) = + run_wait_result_internal(db, uuid, w_id, node_id_for_empty_return, username).await?; + + result_to_response(result, success) +} + +pub async fn delete_job_metadata_after_use(db: &DB, job_uuid: Uuid) -> Result<(), Error> { sqlx::query!( "UPDATE v2_job SET args = '{}'::jsonb WHERE id = $1", job_uuid, @@ -4291,7 +4323,7 @@ async fn log_job_view( username: "anonymous".to_string(), username_override: None, email: "anonymous".to_string(), - token_prefix: opt_token.map(|t| t[0..TOKEN_PREFIX_LEN].to_string()) + token_prefix: opt_token.map(|t| t[0..TOKEN_PREFIX_LEN].to_string()), }, }; if JOB_VIEW_CACHE @@ -5527,7 +5559,10 @@ pub async fn run_job_by_hash( ) .await?; - 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).await?; + + Ok((StatusCode::CREATED, uuid.to_string())) } pub async fn run_job_by_hash_inner( @@ -5538,7 +5573,7 @@ pub async fn run_job_by_hash_inner( script_hash: ScriptHash, run_query: RunJobQuery, args: PushArgsOwned, -) -> error::Result<(StatusCode, String)> { +) -> error::Result<(Uuid, Option)> { #[cfg(feature = "enterprise")] check_license_key_valid().await?; @@ -5558,7 +5593,8 @@ pub async fn run_job_by_hash_inner( has_preprocessor, on_behalf_of_email, created_by, - .. // delete_after_use not taken into account in async endpoints + delete_after_use, + .. } = get_script_info_for_hash(&mut *tx, &w_id, hash).await?; check_scopes(&authed, || format!("run:script/{path}"))?; if let Some(run_query_cache_ttl) = run_query.cache_ttl { @@ -5626,7 +5662,7 @@ pub async fn run_job_by_hash_inner( .await?; tx.commit().await?; - Ok((StatusCode::CREATED, uuid.to_string())) + Ok((uuid, delete_after_use)) } #[derive(Deserialize)] @@ -5757,7 +5793,14 @@ async fn get_job_update( "As a non logged in user, you can only see jobs ran by anonymous users".to_string(), )); } - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &job_id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &job_id, + ) + .await?; Ok(Json(JobUpdate { running: record.running, completed: record.completed, @@ -6071,7 +6114,14 @@ async fn get_completed_job<'a>( // .fetch_optional(db) // .await.ok().flatten().flatten(); - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &id, + ) + .await?; Ok(response) } @@ -6174,7 +6224,14 @@ async fn get_completed_job_result( raw_result.result.as_mut(), ); - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &id, + ) + .await?; Ok(Json(raw_result.result).into_response()) } @@ -6265,7 +6322,14 @@ async fn get_completed_job_result_maybe( )); } - log_job_view(&db, opt_authed.as_ref(), opt_tokened.token.as_deref(), &w_id, &id).await?; + log_job_view( + &db, + opt_authed.as_ref(), + opt_tokened.token.as_deref(), + &w_id, + &id, + ) + .await?; Ok(Json(CompletedJobResult { started: Some(true), diff --git a/backend/windmill-api/src/kafka_triggers_oss.rs b/backend/windmill-api/src/kafka_triggers_oss.rs index 9d698e2fbf..22cbf3785d 100644 --- a/backend/windmill-api/src/kafka_triggers_oss.rs +++ b/backend/windmill-api/src/kafka_triggers_oss.rs @@ -51,4 +51,10 @@ pub struct KafkaTrigger { #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, pub enabled: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_args: Option>>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub retry: Option>, } diff --git a/backend/windmill-api/src/mqtt_triggers.rs b/backend/windmill-api/src/mqtt_triggers.rs index 7cc3f3c4e0..73f40c6307 100644 --- a/backend/windmill-api/src/mqtt_triggers.rs +++ b/backend/windmill-api/src/mqtt_triggers.rs @@ -1,9 +1,8 @@ use crate::{ capture::{insert_capture_payload, MqttTriggerConfig}, db::{ApiAuthed, DB}, - jobs::{run_flow_by_path_inner, run_script_by_path_inner, RunJobQuery}, resources::try_get_resource_from_db_as, - trigger_helpers::TriggerJobArgs, + trigger_helpers::{trigger_runnable, TriggerJobArgs}, users::fetch_api_authed, }; use windmill_git_sync::{handle_deployment_metadata, DeployedObject}; @@ -106,33 +105,20 @@ async fn run_job( ) .await?; - let user_db = UserDB::new(db.clone()); - - let run_query = RunJobQuery::default(); - - if trigger.is_flow { - run_flow_by_path_inner( - authed, - db.clone(), - user_db, - trigger.workspace_id.clone(), - StripPath(trigger.script_path.to_owned()), - run_query, - args, - ) - .await?; - } else { - run_script_by_path_inner( - authed, - db.clone(), - user_db, - trigger.workspace_id.clone(), - StripPath(trigger.script_path.to_owned()), - run_query, - args, - ) - .await?; - } + trigger_runnable( + db, + None, + 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!("mqtt_trigger/{}", trigger.path), + ) + .await?; Ok(()) } @@ -227,6 +213,9 @@ pub struct NewMqttTrigger { script_path: String, is_flow: bool, enabled: bool, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } #[derive(Debug, Serialize, Deserialize)] @@ -241,6 +230,9 @@ pub struct EditMqttTrigger { path: String, script_path: String, is_flow: bool, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } #[derive(Debug, Serialize, Deserialize, FromRow)] @@ -264,6 +256,12 @@ pub struct MqttTrigger { pub server_id: Option, pub last_server_ping: Option>, pub enabled: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_args: Option>>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub retry: Option>, } #[derive(Deserialize, Serialize)] @@ -538,6 +536,9 @@ pub async fn create_mqtt_trigger( v5_config, client_version, client_id, + error_handler_path, + error_handler_args, + retry, } = new_mqtt_trigger; let mut tx = user_db.begin(&authed).await?; @@ -561,7 +562,10 @@ pub async fn create_mqtt_trigger( is_flow, email, enabled, - edited_by + edited_by, + error_handler_path, + error_handler_args, + retry ) VALUES ( $1, @@ -576,7 +580,10 @@ pub async fn create_mqtt_trigger( $10, $11, $12, - $13 + $13, + $14, + $15, + $16 )"#, mqtt_resource_path, subscribe_topics.as_slice() as &[SqlxJson], @@ -590,7 +597,10 @@ pub async fn create_mqtt_trigger( is_flow, &authed.email, enabled, - &authed.username + &authed.username, + error_handler_path, + error_handler_args as _, + retry as _ ) .execute(&mut *tx) .await?; @@ -650,6 +660,9 @@ pub async fn list_mqtt_triggers( "extra_perms", "error", "enabled", + "error_handler_path", + "error_handler_args", + "retry", ]) .order_by("edited_at", true) .and_where("workspace_id = ?".bind(&w_id)) @@ -711,7 +724,10 @@ pub async fn get_mqtt_trigger( last_server_ping, extra_perms, error, - enabled + enabled, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM mqtt_trigger WHERE @@ -748,6 +764,9 @@ pub async fn update_mqtt_trigger( v5_config, client_version, client_id, + error_handler_path, + error_handler_args, + retry, } = mqtt_trigger; let mut tx = user_db.begin(&authed).await?; @@ -775,7 +794,10 @@ pub async fn update_mqtt_trigger( path = $11, edited_at = now(), error = NULL, - server_id = NULL + server_id = NULL, + error_handler_path = $14, + error_handler_args = $15, + retry = $16 WHERE workspace_id = $12 AND path = $13 @@ -793,6 +815,9 @@ pub async fn update_mqtt_trigger( path, w_id, workspace_path, + error_handler_path, + error_handler_args as _, + retry as _ ) .execute(&mut *tx) .await?; @@ -1787,7 +1812,10 @@ async fn listen_to_unlistened_mqtt_events( last_server_ping, extra_perms, error, - enabled + enabled, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM mqtt_trigger WHERE diff --git a/backend/windmill-api/src/nats_triggers_oss.rs b/backend/windmill-api/src/nats_triggers_oss.rs index 28bf1e70e2..3e85ff45cf 100644 --- a/backend/windmill-api/src/nats_triggers_oss.rs +++ b/backend/windmill-api/src/nats_triggers_oss.rs @@ -52,4 +52,10 @@ pub struct NatsTrigger { #[serde(skip_serializing_if = "Option::is_none")] pub error: Option, pub enabled: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_args: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub retry: Option, } diff --git a/backend/windmill-api/src/postgres_triggers/handler.rs b/backend/windmill-api/src/postgres_triggers/handler.rs index 73e35c9e2f..c7f4b233ed 100644 --- a/backend/windmill-api/src/postgres_triggers/handler.rs +++ b/backend/windmill-api/src/postgres_triggers/handler.rs @@ -17,6 +17,7 @@ use pg_escape::{quote_identifier, quote_literal}; use quick_cache::sync::Cache; use rust_postgres::{types::Type, Client}; use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::value::RawValue; use sql_builder::{bind::Bind, SqlBuilder}; use sqlx::FromRow; use windmill_audit::{audit_oss::audit_log, ActionKind}; @@ -97,6 +98,9 @@ pub struct EditPostgresTrigger { is_flow: bool, postgres_resource_path: String, publication: Option, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } #[derive(Deserialize, Serialize, Debug)] @@ -109,6 +113,9 @@ pub struct NewPostgresTrigger { replication_slot_name: Option, publication_name: Option, publication: Option, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } #[derive(Serialize, Deserialize)] @@ -236,6 +243,12 @@ pub struct PostgresTrigger { #[serde(skip_serializing_if = "Option::is_none")] pub last_server_ping: Option>, pub enabled: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_args: Option>>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub retry: Option>, } #[derive(Deserialize, Serialize)] @@ -375,6 +388,9 @@ pub async fn create_postgres_trigger( publication_name, replication_slot_name, publication, + error_handler_path, + error_handler_args, + retry, } = new_postgres_trigger; if publication_name.is_none() && publication.is_none() { @@ -424,7 +440,10 @@ pub async fn create_postgres_trigger( email, enabled, postgres_resource_path, - edited_by + edited_by, + error_handler_path, + error_handler_args, + retry ) VALUES ( $1, @@ -436,7 +455,10 @@ pub async fn create_postgres_trigger( $7, $8, $9, - $10 + $10, + $11, + $12, + $13 )"#, pub_name, slot_name, @@ -447,7 +469,10 @@ pub async fn create_postgres_trigger( &authed.email, enabled, postgres_resource_path, - &authed.username + &authed.username, + error_handler_path, + error_handler_args as _, + retry as _ ) .execute(&mut *tx) .await?; @@ -504,6 +529,9 @@ pub async fn list_postgres_triggers( "postgres_resource_path", "replication_slot_name", "publication_name", + "error_handler_path", + "error_handler_args", + "retry", ]) .order_by("edited_at", true) .and_where("workspace_id = ?".bind(&w_id)) @@ -1142,7 +1170,10 @@ pub async fn get_postgres_trigger( enabled, replication_slot_name, publication_name, - postgres_resource_path + postgres_resource_path, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM postgres_trigger WHERE @@ -1178,6 +1209,9 @@ pub async fn update_postgres_trigger( is_flow, postgres_resource_path, publication, + error_handler_path, + error_handler_args, + retry, } = postgres_trigger; let mut pg_connection = get_default_pg_connection( @@ -1239,7 +1273,10 @@ pub async fn update_postgres_trigger( publication_name = $8, edited_at = now(), error = NULL, - server_id = NULL + server_id = NULL, + error_handler_path = $11, + error_handler_args = $12, + retry = $13 WHERE workspace_id = $9 AND path = $10 @@ -1254,6 +1291,9 @@ pub async fn update_postgres_trigger( publication_name, w_id, workspace_path, + error_handler_path, + error_handler_args as _, + retry as _, ) .execute(&mut *tx) .await?; diff --git a/backend/windmill-api/src/postgres_triggers/mod.rs b/backend/windmill-api/src/postgres_triggers/mod.rs index eff341436f..1e224df3dc 100644 --- a/backend/windmill-api/src/postgres_triggers/mod.rs +++ b/backend/windmill-api/src/postgres_triggers/mod.rs @@ -1,8 +1,7 @@ use crate::{ db::{ApiAuthed, DB}, - jobs::{run_flow_by_path_inner, run_script_by_path_inner, RunJobQuery}, resources::try_get_resource_from_db_as, - trigger_helpers::TriggerJobArgs, + trigger_helpers::{trigger_runnable, TriggerJobArgs}, users::fetch_api_authed, }; use chrono::Utc; @@ -32,7 +31,6 @@ use handler::{ use windmill_common::{ db::UserDB, error::{to_anyhow, Error, Result}, - utils::StripPath, }; mod bool; mod converter; @@ -395,33 +393,20 @@ async fn run_job( ) .await?; - let user_db = UserDB::new(db.clone()); - - let run_query = RunJobQuery::default(); - - if trigger.is_flow { - run_flow_by_path_inner( - authed, - db.clone(), - user_db, - trigger.workspace_id.clone(), - StripPath(trigger.script_path.to_owned()), - run_query, - args, - ) - .await?; - } else { - run_script_by_path_inner( - authed, - db.clone(), - user_db, - trigger.workspace_id.clone(), - StripPath(trigger.script_path.to_owned()), - run_query, - args, - ) - .await?; - } + trigger_runnable( + db, + None, + 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!("postgres_trigger/{}", trigger.path), + ) + .await?; Ok(()) } diff --git a/backend/windmill-api/src/postgres_triggers/trigger.rs b/backend/windmill-api/src/postgres_triggers/trigger.rs index 27313879b2..c08e917dd4 100644 --- a/backend/windmill-api/src/postgres_triggers/trigger.rs +++ b/backend/windmill-api/src/postgres_triggers/trigger.rs @@ -913,7 +913,10 @@ async fn listen_to_unlistened_database_events( extra_perms, error, enabled, - postgres_resource_path + postgres_resource_path, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM postgres_trigger WHERE diff --git a/backend/windmill-api/src/sqs_triggers_oss.rs b/backend/windmill-api/src/sqs_triggers_oss.rs index 6e01641a5e..7d993bf1cc 100644 --- a/backend/windmill-api/src/sqs_triggers_oss.rs +++ b/backend/windmill-api/src/sqs_triggers_oss.rs @@ -40,4 +40,10 @@ pub struct SqsTrigger { pub server_id: Option, pub last_server_ping: Option>, pub enabled: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_args: Option>>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub retry: Option>, } diff --git a/backend/windmill-api/src/trigger_helpers.rs b/backend/windmill-api/src/trigger_helpers.rs index 917fe48bfa..a3c2acd4a1 100644 --- a/backend/windmill-api/src/trigger_helpers.rs +++ b/backend/windmill-api/src/trigger_helpers.rs @@ -1,23 +1,38 @@ +use anyhow::Context; +use axum::response::IntoResponse; +use http::StatusCode; use serde::Deserialize; use serde_json::value::RawValue; use std::collections::HashMap; +use uuid::Uuid; use windmill_common::{ + db::UserDB, error::Result, - flows::FlowModuleValue, + flows::{FlowModuleValue, Retry}, get_latest_deployed_hash_for_path, get_latest_flow_version_info_for_path, - jobs::get_has_preprocessor_from_content_and_lang, + 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, RUNNABLE_FORMAT_VERSION_CACHE, }, + users::username_to_permissioned_as, utils::StripPath, worker::to_raw_value, FlowVersionInfo, }; -use windmill_queue::PushArgsOwned; +use windmill_queue::{push, PushArgs, PushArgsOwned, PushIsolationLevel}; -use crate::{db::DB, HTTP_CLIENT}; +#[cfg(feature = "enterprise")] +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, + }, + HTTP_CLIENT, +}; struct ScriptInfo { has_preprocessor: Option, @@ -442,3 +457,347 @@ pub trait TriggerJobArgs { (main_args, preprocessor_args) } } + +#[allow(dead_code)] +async fn trigger_runnable_inner( + db: &DB, + user_db: Option, + authed: ApiAuthed, + workspace_id: &str, + runnable_path: &str, + is_flow: bool, + args: PushArgsOwned, + retry: Option<&sqlx::types::Json>, + error_handler_path: Option<&str>, + error_handler_args: Option<&sqlx::types::Json>>>, + trigger_path: String, +) -> Result<(Uuid, Option)> { + let user_db = user_db.unwrap_or_else(|| UserDB::new(db.clone())); + let (uuid, delete_after_use) = if is_flow { + let run_query = RunJobQuery::default(); + let path = StripPath(runnable_path.to_string()); + let uuid = run_flow_by_path_inner( + authed, + db.clone(), + user_db, + workspace_id.to_string(), + path, + run_query, + args, + ) + .await?; + (uuid, None) + } else { + trigger_script_internal( + db, + user_db, + authed, + workspace_id, + runnable_path, + args, + retry, + error_handler_path, + error_handler_args, + trigger_path, + ) + .await? + }; + + Ok((uuid, delete_after_use)) +} + +#[allow(dead_code)] +pub async fn trigger_runnable( + db: &DB, + user_db: Option, + authed: ApiAuthed, + workspace_id: &str, + runnable_path: &str, + is_flow: bool, + args: PushArgsOwned, + retry: Option<&sqlx::types::Json>, + error_handler_path: Option<&str>, + error_handler_args: Option<&sqlx::types::Json>>>, + trigger_path: String, +) -> Result { + let (uuid, _) = trigger_runnable_inner( + db, + user_db, + authed, + workspace_id, + runnable_path, + is_flow, + args, + retry, + error_handler_path, + error_handler_args, + trigger_path, + ) + .await?; + Ok((StatusCode::CREATED, uuid.to_string()).into_response()) +} + +#[allow(dead_code)] +pub async fn trigger_runnable_and_wait_for_result( + db: &DB, + user_db: Option, + authed: ApiAuthed, + workspace_id: &str, + runnable_path: &str, + is_flow: bool, + args: PushArgsOwned, + retry: Option<&sqlx::types::Json>, + error_handler_path: Option<&str>, + error_handler_args: Option<&sqlx::types::Json>>>, + trigger_path: String, +) -> Result { + let username = authed.username.clone(); + let (uuid, delete_after_use) = trigger_runnable_inner( + db, + user_db, + authed, + workspace_id, + runnable_path, + is_flow, + args, + retry, + error_handler_path, + error_handler_args, + trigger_path, + ) + .await?; + let (result, success) = + run_wait_result_internal(db, uuid, workspace_id.to_string(), None, &username).await?; + + if delete_after_use.unwrap_or(false) { + delete_job_metadata_after_use(&db, uuid).await?; + } + + result_to_response(result, success) +} + +#[allow(dead_code)] +pub async fn trigger_runnable_and_wait_for_raw_result( + db: &DB, + user_db: Option, + authed: ApiAuthed, + workspace_id: &str, + runnable_path: &str, + is_flow: bool, + args: PushArgsOwned, + retry: Option<&sqlx::types::Json>, + error_handler_path: Option<&str>, + error_handler_args: Option<&sqlx::types::Json>>>, + trigger_path: String, +) -> Result> { + let username = authed.username.clone(); + let (uuid, delete_after_use) = trigger_runnable_inner( + db, + user_db, + authed, + workspace_id, + runnable_path, + is_flow, + args, + retry, + error_handler_path, + error_handler_args, + trigger_path, + ) + .await?; + + let early_return = if is_flow { + sqlx::query_scalar!( + r#"SELECT flow_version.value->>'early_return' as early_return + FROM flow + LEFT JOIN flow_version + ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] + WHERE flow.path = $1 and flow.workspace_id = $2"#, + runnable_path, + workspace_id, + ) + .fetch_optional(db) + .await? + .flatten() + } else { + None + }; + + let (result, success) = + run_wait_result_internal(db, uuid, workspace_id.to_string(), early_return, &username) + .await + .with_context(|| { + format!( + "Error fetching job result for {} {}", + if is_flow { "flow" } else { "script" }, + runnable_path + ) + })?; + + if delete_after_use.unwrap_or(false) { + delete_job_metadata_after_use(&db, uuid).await?; + } + + if !success { + Err(windmill_common::error::Error::internal_err(format!( + "{} {runnable_path} failed: {:?}", + if is_flow { "Flow" } else { "Script" }, + result + ))) + } else { + Ok(result) + } +} + +async fn trigger_script_internal( + db: &DB, + user_db: UserDB, + authed: ApiAuthed, + workspace_id: &str, + script_path: &str, + args: PushArgsOwned, + retry: Option<&sqlx::types::Json>, + error_handler_path: Option<&str>, + error_handler_args: Option<&sqlx::types::Json>>>, + trigger_path: String, +) -> Result<(Uuid, Option)> { + if retry.is_none() && error_handler_path.is_none() { + let run_query = RunJobQuery::default(); + let path = StripPath(script_path.to_string()); + run_script_by_path_inner( + authed, + db.clone(), + user_db, + workspace_id.to_string(), + path, + run_query, + args, + ) + .await + } else { + trigger_script_with_retry_and_error_handler( + db, + user_db, + authed, + workspace_id, + script_path, + args, + retry, + error_handler_path, + error_handler_args, + trigger_path, + ) + .await + } +} + +async fn trigger_script_with_retry_and_error_handler( + db: &DB, + user_db: UserDB, + authed: ApiAuthed, + workspace_id: &str, + script_path: &str, + args: PushArgsOwned, + retry: Option<&sqlx::types::Json>, + error_handler_path: Option<&str>, + error_handler_args: Option<&sqlx::types::Json>>>, + trigger_path: String, +) -> Result<(Uuid, Option)> { + let retry = retry.map(|r| r.0.clone()); + let error_handler_path = error_handler_path.map(|p| p.to_string()); + let error_handler_args = error_handler_args.map(|args| args.0.clone()); + + #[cfg(feature = "enterprise")] + check_license_key_valid().await?; + + let (job_payload, tag, delete_after_use, timeout, on_behalf_of) = { + let mut tx = user_db.clone().begin(&authed).await?; + script_path_to_payload(script_path, &mut *tx, &workspace_id, Some(false)).await? + }; + + 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 push_args = PushArgs { args: &args.args, extra: args.extra }; + + let retryable_job_payload = match job_payload { + JobPayload::ScriptHash { + hash, + path, + custom_concurrency_key, + concurrent_limit, + concurrency_time_window_s, + cache_ttl, + priority, + apply_preprocessor, + .. + } => JobPayload::SingleScriptFlow { + path, + hash, + args: HashMap::from(&push_args), + retry, + error_handler_path, + error_handler_args, + custom_concurrency_key, + concurrent_limit, + concurrency_time_window_s, + cache_ttl, + priority, + tag_override: tag.clone(), + apply_preprocessor, + trigger_path: Some(trigger_path), + }, + _ => { + return Err(windmill_common::error::Error::internal_err(format!( + "Unsupported job payload: {:?}", + job_payload + ))) + } + }; + + let (uuid, tx) = push( + &db, + tx, + &workspace_id, + retryable_job_payload, + push_args, + authed.display_username(), + email, + permissioned_as, + authed.token_prefix.as_deref(), + None, + None, + None, + None, + None, + false, + false, + None, + true, + tag, + timeout, + None, + None, + push_authed.as_ref(), + ) + .await?; + tx.commit().await?; + + Ok((uuid, delete_after_use)) +} diff --git a/backend/windmill-api/src/websocket_triggers.rs b/backend/windmill-api/src/websocket_triggers.rs index 8c263b1f62..ead48f8d98 100644 --- a/backend/windmill-api/src/websocket_triggers.rs +++ b/backend/windmill-api/src/websocket_triggers.rs @@ -19,7 +19,6 @@ use sqlx::types::Json as SqlxJson; use std::{collections::HashMap, fmt}; use tokio::net::TcpStream; use tokio_tungstenite::{connect_async, tungstenite::Message, MaybeTlsStream, WebSocketStream}; -use uuid::Uuid; use windmill_audit::{audit_oss::audit_log, ActionKind}; use windmill_common::{ db::UserDB, @@ -35,10 +34,7 @@ use windmill_queue::PushArgsOwned; use crate::{ capture::{insert_capture_payload, WebsocketTriggerConfig}, db::{ApiAuthed, DB}, - jobs::{ - run_flow_by_path_inner, run_script_by_path_inner, run_wait_result_internal, RunJobQuery, - }, - trigger_helpers::TriggerJobArgs, + trigger_helpers::{trigger_runnable, trigger_runnable_and_wait_for_raw_result, TriggerJobArgs}, users::fetch_api_authed, }; @@ -67,6 +63,9 @@ struct NewWebsocketTrigger { initial_messages: Option>>, url_runnable_args: Option>, can_return_message: bool, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } #[derive(Deserialize)] @@ -112,6 +111,12 @@ pub struct WebsocketTrigger { #[serde(skip_serializing_if = "Option::is_none")] pub url_runnable_args: Option>>, pub can_return_message: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_path: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub error_handler_args: Option>>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub retry: Option>, } #[derive(Deserialize)] @@ -124,6 +129,9 @@ struct EditWebsocketTrigger { initial_messages: Option>>, url_runnable_args: Option>, can_return_message: bool, + error_handler_path: Option, + error_handler_args: Option>>>, + retry: Option>, } #[derive(Deserialize)] @@ -215,22 +223,47 @@ async fn create_websocket_trigger( .into_iter() .map(SqlxJson) .collect_vec(); - sqlx::query_as::<_, WebsocketTrigger>( - "INSERT INTO websocket_trigger (workspace_id, path, url, script_path, is_flow, enabled, filters, initial_messages, url_runnable_args, edited_by, can_return_message, email, edited_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now()) RETURNING *", + sqlx::query!( + r#" + INSERT INTO websocket_trigger ( + workspace_id, + path, + url, + script_path, + is_flow, + enabled, + filters, + initial_messages, + url_runnable_args, + edited_by, + can_return_message, + email, + edited_at, + error_handler_path, + error_handler_args, + retry + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, now(), $13, $14, $15 + ) + "#, + w_id, + ct.path, + ct.url, + ct.script_path, + ct.is_flow, + ct.enabled.unwrap_or(true), + &filters as _, + &initial_messages as _, + ct.url_runnable_args.map(SqlxJson) as _, + authed.username, + ct.can_return_message, + authed.email, + ct.error_handler_path, + ct.error_handler_args as _, + ct.retry as _ ) - .bind(&w_id) - .bind(&ct.path) - .bind(ct.url) - .bind(ct.script_path) - .bind(ct.is_flow) - .bind(ct.enabled.unwrap_or(true)) - .bind(filters.as_slice()) - .bind(initial_messages.as_slice()) - .bind(ct.url_runnable_args.map(SqlxJson)) - .bind(&authed.username) - .bind(ct.can_return_message) - .bind(&authed.email) - .fetch_one(&mut *tx).await?; + .execute(&mut *tx) + .await?; audit_log( &mut *tx, @@ -279,7 +312,7 @@ async fn update_websocket_trigger( // important to update server_id to NULL to stop current websocket listener sqlx::query!( - "UPDATE websocket_trigger SET url = $1, script_path = $2, path = $3, is_flow = $4, filters = $5, initial_messages = $6, url_runnable_args = $7, edited_by = $8, email = $9, can_return_message = $10, edited_at = now(), server_id = NULL, error = NULL + "UPDATE websocket_trigger SET url = $1, script_path = $2, path = $3, is_flow = $4, filters = $5, initial_messages = $6, url_runnable_args = $7, edited_by = $8, email = $9, can_return_message = $10, edited_at = now(), server_id = NULL, error = NULL, error_handler_path = $13, error_handler_args = $14, retry = $15 WHERE workspace_id = $11 AND path = $12", ct.url, ct.script_path, @@ -293,6 +326,9 @@ async fn update_websocket_trigger( ct.can_return_message, w_id, path, + ct.error_handler_path, + ct.error_handler_args as _, + ct.retry as _, ) .execute(&mut *tx).await?; @@ -636,87 +672,6 @@ fn raw_value_to_args_hashmap( Ok(args) } -async fn wait_runnable_result( - path: String, - is_flow: bool, - args: PushArgsOwned, - authed: ApiAuthed, - db: &DB, - workspace_id: &str, -) -> error::Result { - let user_db = UserDB::new(db.clone()); - - let username = authed.display_username().to_owned(); - - let (job_id, early_return) = if is_flow { - let (_, job_id) = run_flow_by_path_inner( - authed, - db.clone(), - user_db, - workspace_id.to_string(), - StripPath(path.clone()), - RunJobQuery::default(), - args, - ) - .await?; - - let early_return = sqlx::query_scalar!( - r#"SELECT flow_version.value->>'early_return' as early_return - FROM flow - LEFT JOIN flow_version - ON flow_version.id = flow.versions[array_upper(flow.versions, 1)] - WHERE flow.path = $1 and flow.workspace_id = $2"#, - path, - workspace_id, - ) - .fetch_optional(db) - .await? - .flatten(); - - (job_id, early_return) - } else { - let (_, job_id) = run_script_by_path_inner( - authed, - db.clone(), - user_db, - workspace_id.to_string(), - StripPath(path.clone()), - RunJobQuery::default(), - args, - ) - .await?; - - (job_id, None) - }; - - let (result, success) = run_wait_result_internal( - db, - Uuid::parse_str(&job_id).unwrap(), - workspace_id.to_string(), - early_return, - &username, - ) - .await - .with_context(|| { - format!( - "Error fetching job result for {} {}", - if is_flow { "flow" } else { "script" }, - path - ) - })?; - - if !success { - Err(anyhow::anyhow!( - "{} {path} failed: {:?}", - if is_flow { "Flow" } else { "Script" }, - result - ) - .into()) - } else { - Ok(result.get().to_owned()) - } -} - async fn loop_ping(db: &DB, ws: &WebsocketEnum, error: Option<&str>) -> () { loop { if let None = ws.update_ping(db, error).await { @@ -742,17 +697,22 @@ async fn get_url_from_runnable( let args = raw_value_to_args_hashmap(args)?; - let result = wait_runnable_result( - path.to_string(), + let result = trigger_runnable_and_wait_for_raw_result( + db, + None, + authed, + workspace_id, + path, is_flow, PushArgsOwned { args, extra: None }, - authed, - db, - workspace_id, + None, + None, + None, + "".to_string(), // doesn't matter as no retry/error handler ) .await?; - serde_json::from_str::(result.as_str()).map_err(|_| { + serde_json::from_str::(result.get()).map_err(|_| { error::Error::BadConfig(format!( "{} {} did not return a string", if is_flow { "Flow" } else { "Script" }, @@ -866,6 +826,7 @@ impl WebsocketTrigger { .filter_map(|m| serde_json::from_str(m.get()).ok()) .collect_vec(); + let mut authed_o = None; for start_message in initial_messages { match start_message { InitialMessage::RawMessage(msg) => { @@ -895,15 +856,26 @@ impl WebsocketTrigger { let args = raw_value_to_args_hashmap(Some(&args))?; - let result = wait_runnable_result( - path.clone(), + if authed_o.is_none() { + authed_o = Some(self.fetch_authed(db).await?); + } + let authed = authed_o.clone().unwrap(); + + let result = trigger_runnable_and_wait_for_raw_result( + db, + None, + authed.clone(), + &self.workspace_id, + &path, is_flow, PushArgsOwned { args, extra: None }, - self.fetch_authed(db).await?, - db, - &self.workspace_id, + None, + None, + None, + "".to_string(), // doesn't matter as no retry/error handler ) - .await?; + .await + .map(|r| r.get().to_owned())?; tracing::info!( "Sending {} {} result to WebSocket {}", @@ -1398,20 +1370,29 @@ async fn run_job( let script_path = trigger.script_path.clone(); let is_flow = trigger.is_flow; let w_id = trigger.workspace_id.clone(); + let retry = trigger.retry.clone(); + let error_handler_path = trigger.error_handler_path.clone(); + let error_handler_args = trigger.error_handler_args.clone(); + let trigger_path = trigger.path.clone(); let handle_response_f = async move { tokio::select! { _ = killpill_rx.recv() => { return; }, - result = wait_runnable_result( - script_path, + result = trigger_runnable_and_wait_for_raw_result( + &db_, + None, + authed, + &w_id, + &script_path, is_flow, args, - authed, - &db_, - &w_id, + retry.as_ref(), + error_handler_path.as_deref(), + error_handler_args.as_ref(), + format!("websocket_trigger/{}", trigger_path), ) => { - if let Ok(result) = result { + if let Ok(result) = result.map(|r| r.get().to_owned()) { // only send the result if it's not null if result != "null" { tracing::info!("Sending job result to WebSocket {}", url); @@ -1429,32 +1410,20 @@ async fn run_job( tokio::spawn(handle_response_f); } else { - let user_db = UserDB::new(db.clone()); - let run_query = RunJobQuery::default(); - let runnable_path = StripPath(trigger.script_path.to_owned()); - if trigger.is_flow { - run_flow_by_path_inner( - authed, - db.clone(), - user_db, - trigger.workspace_id.clone(), - runnable_path, - run_query, - args, - ) - .await?; - } else { - run_script_by_path_inner( - authed, - db.clone(), - user_db, - trigger.workspace_id.clone(), - runnable_path, - run_query, - args, - ) - .await?; - } + trigger_runnable( + db, + None, + 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!("websocket_trigger/{}", trigger.path), + ) + .await?; } Ok(()) diff --git a/backend/windmill-api/src/workspaces_export.rs b/backend/windmill-api/src/workspaces_export.rs index 5673958ab8..88475dd129 100644 --- a/backend/windmill-api/src/workspaces_export.rs +++ b/backend/windmill-api/src/workspaces_export.rs @@ -560,7 +560,10 @@ pub(crate) async fn tarball_workspace( static_asset_config AS "static_asset_config: _", is_static_website, wrap_body, - raw_string + raw_string, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM http_trigger WHERE workspace_id = $1 "#, @@ -599,7 +602,10 @@ pub(crate) async fn tarball_workspace( filters AS "filters: _", initial_messages AS "initial_messages: _", url_runnable_args AS "url_runnable_args: _", - can_return_message + can_return_message, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM websocket_trigger WHERE @@ -625,8 +631,27 @@ pub(crate) async fn tarball_workspace( { let kafka_triggers = sqlx::query_as!( crate::kafka_triggers_oss::KafkaTrigger, - "SELECT * FROM kafka_trigger - WHERE workspace_id = $1", + r#"SELECT + workspace_id, + path, + kafka_resource_path, + group_id, + topics, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" + FROM kafka_trigger + WHERE workspace_id = $1"#, &w_id ) .fetch_all(&mut *tx) @@ -664,7 +689,10 @@ pub(crate) async fn tarball_workspace( last_server_ping, extra_perms, error, - enabled + enabled, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM sqs_trigger WHERE @@ -706,7 +734,10 @@ pub(crate) async fn tarball_workspace( last_server_ping, extra_perms, error, - enabled + enabled, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM gcp_trigger WHERE @@ -729,8 +760,29 @@ pub(crate) async fn tarball_workspace( { let nats_triggers = sqlx::query_as!( crate::nats_triggers_oss::NatsTrigger, - "SELECT * FROM nats_trigger - WHERE workspace_id = $1", + r#"SELECT + workspace_id, + path, + nats_resource_path, + subjects, + stream_name, + consumer_name, + use_jetstream, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" + FROM nats_trigger + WHERE workspace_id = $1"#, &w_id ) .fetch_all(&mut *tx) @@ -749,8 +801,27 @@ pub(crate) async fn tarball_workspace( { let postgres_triggers = sqlx::query_as!( crate::postgres_triggers::PostgresTrigger, - "SELECT * FROM postgres_trigger - WHERE workspace_id = $1", + r#"SELECT + workspace_id, + path, + script_path, + is_flow, + edited_by, + email, + edited_at, + server_id, + last_server_ping, + extra_perms, + error, + enabled, + replication_slot_name, + publication_name, + postgres_resource_path, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" + FROM postgres_trigger + WHERE workspace_id = $1"#, &w_id ) .fetch_all(&mut *tx) @@ -790,7 +861,10 @@ pub(crate) async fn tarball_workspace( last_server_ping, extra_perms, error, - enabled + enabled, + error_handler_path, + error_handler_args as "error_handler_args: _", + retry as "retry: _" FROM mqtt_trigger "#, diff --git a/backend/windmill-common/src/flows.rs b/backend/windmill-common/src/flows.rs index 377653ff36..b6d8f422e2 100644 --- a/backend/windmill-common/src/flows.rs +++ b/backend/windmill-common/src/flows.rs @@ -253,7 +253,7 @@ pub struct Mock { pub return_value: Option, } -#[derive(Deserialize, Serialize, Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone, Default)] pub struct FlowModule { #[serde(default = "default_id")] pub id: String, @@ -285,6 +285,8 @@ pub struct FlowModule { pub continue_on_error: Option, #[serde(skip_serializing_if = "Option::is_none")] pub skip_if: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub apply_preprocessor: Option, } #[derive(Deserialize, Serialize, Debug, Clone)] @@ -715,6 +717,7 @@ pub fn add_virtual_items_if_necessary(modules: &mut Vec) { delete_after_use: None, continue_on_error: None, skip_if: None, + apply_preprocessor: None, }); } } diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index 1e28d373be..51083f860b 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -14,6 +14,7 @@ pub const LARGE_LOG_THRESHOLD_SIZE: usize = 9000; use crate::{ apps::AppScriptId, + auth::is_super_admin_email, error::{self, to_anyhow, Error}, flow_status::{FlowStatus, RestartedFrom}, flows::{FlowNodeId, FlowValue, Retry}, @@ -21,7 +22,7 @@ use crate::{ scripts::{get_full_hub_script_by_path, ScriptHash, ScriptLang}, users::username_to_permissioned_as, utils::{StripPath, HTTP_CLIENT}, - worker::{to_raw_value, TMP_DIR}, + worker::{to_raw_value, CUSTOM_TAGS_PER_WORKSPACE, TMP_DIR}, FlowVersionInfo, ScriptHashInfo, }; @@ -350,13 +351,17 @@ pub enum JobPayload { path: String, hash: ScriptHash, args: HashMap>, - retry: Retry, // for now only used to retry the script, so retry is necessarily present + retry: Option, + error_handler_path: Option, + error_handler_args: Option>>, custom_concurrency_key: Option, concurrent_limit: Option, concurrency_time_window_s: Option, cache_ttl: Option, priority: Option, tag_override: Option, + trigger_path: Option, + apply_preprocessor: bool, }, DeploymentCallback { path: String, @@ -637,3 +642,55 @@ pub async fn get_logs_from_store( } return None; } + +pub async fn check_tag_available_for_workspace_internal( + db: &DB, + w_id: &str, + tag: &str, + email: &str, + scope_tags: Option>, +) -> error::Result<()> { + let mut is_tag_in_scope_tags = None; + let mut is_tag_in_workspace_custom_tags = false; + + if let Some(scope_tags) = scope_tags.as_ref() { + is_tag_in_scope_tags = Some(scope_tags.contains(&tag)); + } + + let custom_tags_per_w = CUSTOM_TAGS_PER_WORKSPACE.read().await; + if custom_tags_per_w.0.contains(&tag.to_string()) { + is_tag_in_workspace_custom_tags = true; + } else if custom_tags_per_w.1.contains_key(tag) + && custom_tags_per_w + .1 + .get(tag) + .unwrap() + .contains(&w_id.to_string()) + { + is_tag_in_workspace_custom_tags = true; + } + + match is_tag_in_scope_tags { + Some(true) | None => { + if is_tag_in_workspace_custom_tags { + return Ok(()); + } + } + _ => {} + } + + if !is_super_admin_email(db, email).await? { + if scope_tags.is_some() && is_tag_in_scope_tags.is_some() { + return Err(Error::BadRequest(format!( + "Tag {tag} is not available in your scope" + ))); + } + + return Err(error::Error::BadRequest(format!( + "Only super admins are allowed to use tags that are not included in the allowed CUSTOM_TAGS: {:?}", + custom_tags_per_w + ))); + } + + return Ok(()); +} diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index fc51a3c870..c901167c63 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -3233,6 +3233,17 @@ impl<'c> Serialize for PushArgs<'c> { } } +impl<'c> From<&PushArgs<'c>> for HashMap> { + fn from(args: &PushArgs<'c>) -> Self { + let mut map = HashMap::new(); + map.extend(args.args.clone().into_iter()); + if let Some(ref extra) = args.extra { + map.extend(extra.clone().into_iter()); + } + map + } +} + impl PushArgsOwned { pub fn empty() -> Self { PushArgsOwned { extra: None, args: HashMap::new() } @@ -3789,6 +3800,8 @@ pub async fn push<'c, 'd>( path, hash, retry, + error_handler_path, + error_handler_args, args, custom_concurrency_key, concurrent_limit, @@ -3796,11 +3809,46 @@ pub async fn push<'c, 'd>( cache_ttl, priority, tag_override, + trigger_path, + apply_preprocessor, } => { let mut input_transforms = HashMap::::new(); for (arg_name, arg_value) in args { input_transforms.insert(arg_name, InputTransform::Static { value: arg_value }); } + let failure_module = if let Some(error_handler_path) = error_handler_path { + let mut input_transforms = HashMap::::new(); + input_transforms.insert("error".to_string(), InputTransform::Javascript { expr: "error".to_string() }); + input_transforms.insert("path".to_string(), InputTransform::Static { value: to_raw_value(&path) }); + input_transforms.insert("is_flow".to_string(), InputTransform::Static { value: to_raw_value(&false) }); + input_transforms.insert("trigger_path".to_string(), InputTransform::Static { value: to_raw_value(&trigger_path) }); + input_transforms.insert("workspace_id".to_string(), InputTransform::Static { value: to_raw_value(&workspace_id) }); + input_transforms.insert("email".to_string(), InputTransform::Static { value: to_raw_value(&email) }); + // for the below transforms to work, make sure that flow_job_id and started_at are added to the eval context when pusing the error handler job + input_transforms.insert("job_id".to_string(), InputTransform::Javascript { expr: "flow_job_id".to_string() }); + input_transforms.insert("started_at".to_string(), InputTransform::Javascript { expr: "started_at".to_string() }); + + if let Some(error_handler_args) = error_handler_args { + for (arg_name, arg_value) in error_handler_args { + input_transforms.insert(arg_name, InputTransform::Static { value: arg_value }); + } + } + + Some(Box::new(FlowModule { + id: "failure".to_string(), + value: to_raw_value(&FlowModuleValue::Script { + input_transforms, + path: error_handler_path, + hash: None, + tag_override: None, + is_trigger: None, + }), + ..Default::default() + })) + } else { + None + }; + let flow_value = FlowValue { modules: vec![FlowModule { id: "a".to_string(), @@ -3811,29 +3859,19 @@ pub async fn push<'c, 'd>( tag_override, is_trigger: None, }), - stop_after_if: None, - stop_after_all_iters_if: None, - summary: None, - suspend: None, - mock: None, - retry: Some(retry), - sleep: None, - cache_ttl: None, - timeout: None, - priority: None, - delete_after_use: None, - continue_on_error: None, - skip_if: None, + retry, + apply_preprocessor: Some(apply_preprocessor), + ..Default::default() }], - same_worker: false, - failure_module: None, + failure_module, concurrency_time_window_s, concurrent_limit, - skip_expr: None, - cache_ttl: cache_ttl.map(|val| val as u32), - early_return: None, - concurrency_key: custom_concurrency_key.clone(), priority, + cache_ttl: cache_ttl.map(|val| val as u32), + concurrency_key: custom_concurrency_key.clone(), + same_worker: false, + early_return: None, + skip_expr: None, preprocessor_module: None, }; // this is a new flow being pushed, flow_status is set to flow_value: diff --git a/backend/windmill-queue/src/schedule.rs b/backend/windmill-queue/src/schedule.rs index f539ac7818..0c31568968 100644 --- a/backend/windmill-queue/src/schedule.rs +++ b/backend/windmill-queue/src/schedule.rs @@ -16,6 +16,7 @@ use windmill_common::db::Authed; use windmill_common::ee_oss::LICENSE_KEY_VALID; use windmill_common::flows::Retry; use windmill_common::get_latest_flow_version_info_for_path; +use windmill_common::jobs::check_tag_available_for_workspace_internal; use windmill_common::jobs::JobPayload; use windmill_common::schedule::schedule_to_user; use windmill_common::FlowVersionInfo; @@ -175,7 +176,9 @@ pub async fn push_scheduled_job<'c>( JobPayload::SingleScriptFlow { path: schedule.script_path.clone(), hash: hash, - retry: parsed_retry, + retry: Some(parsed_retry), + error_handler_path: None, + error_handler_args: None, args: static_args, custom_concurrency_key: None, concurrent_limit: None, @@ -183,6 +186,8 @@ pub async fn push_scheduled_job<'c>( cache_ttl: cache_ttl, priority: priority, tag_override: schedule.tag.clone(), + trigger_path: None, + apply_preprocessor: false, }, if schedule.tag.as_ref().is_some_and(|x| x != "") { schedule.tag.clone() @@ -261,6 +266,17 @@ pub async fn push_scheduled_job<'c>( ) }; + if let Some(tag) = tag.as_deref().filter(|t| !t.is_empty()) { + check_tag_available_for_workspace_internal( + &db, + &schedule.workspace_id, + &tag, + email, + None, // no token for schedules so no scopes so no scope_tags + ) + .await?; + } + let tx = PushIsolationLevel::Transaction(tx); let (_, mut tx) = push( &db, diff --git a/backend/windmill-worker/src/bun_executor.rs b/backend/windmill-worker/src/bun_executor.rs index 20f42b468d..f724f564df 100644 --- a/backend/windmill-worker/src/bun_executor.rs +++ b/backend/windmill-worker/src/bun_executor.rs @@ -894,7 +894,8 @@ pub async fn handle_bun_job( annotation.nodejs = true } let main_override = job.script_entrypoint_override.as_deref(); - let apply_preprocessor = !job.is_flow_step() && job.preprocessed == Some(false); + let apply_preprocessor = + job.flow_step_id.as_deref() != Some("preprocessor") && job.preprocessed == Some(false); if has_bundle_cache { let target; diff --git a/backend/windmill-worker/src/deno_executor.rs b/backend/windmill-worker/src/deno_executor.rs index 59bb6d8808..e2bf579b53 100644 --- a/backend/windmill-worker/src/deno_executor.rs +++ b/backend/windmill-worker/src/deno_executor.rs @@ -199,7 +199,8 @@ pub async fn handle_deno_job( append_logs(&job.id, &job.workspace_id, logs1, conn).await; let main_override = job.script_entrypoint_override.as_deref(); - let apply_preprocessor = !job.is_flow_step() && job.preprocessed == Some(false); + let apply_preprocessor = + job.flow_step_id.as_deref() != Some("preprocessor") && job.preprocessed == Some(false); write_file(job_dir, "main.ts", inner_content)?; diff --git a/backend/windmill-worker/src/python_executor.rs b/backend/windmill-worker/src/python_executor.rs index 07c7877d4f..fb760fb168 100644 --- a/backend/windmill-worker/src/python_executor.rs +++ b/backend/windmill-worker/src/python_executor.rs @@ -610,7 +610,7 @@ pub async fn handle_python_job( pre_spread, ) = prepare_wrapper( job_dir, - job.is_flow_step(), + job.flow_step_id.as_deref(), job.preprocessed, job.script_entrypoint_override.as_deref(), inner_content, @@ -897,7 +897,7 @@ mount {{ async fn prepare_wrapper( job_dir: &str, - job_is_flow_step: bool, + job_flow_step_id: Option<&str>, job_preprocessed: Option, job_script_entrypoint_override: Option<&str>, inner_content: &str, @@ -915,7 +915,8 @@ async fn prepare_wrapper( Option, )> { let main_override = job_script_entrypoint_override.as_deref(); - let apply_preprocessor = !job_is_flow_step && job_preprocessed == Some(false); + let apply_preprocessor = + job_flow_step_id != Some("preprocessor") && job_preprocessed == Some(false); let relative_imports = RELATIVE_IMPORT_REGEX.is_match(&inner_content); @@ -2174,7 +2175,7 @@ pub async fn start_worker( spread, _, _, - ) = prepare_wrapper(job_dir, false, None, None, inner_content, script_path).await?; + ) = prepare_wrapper(job_dir, None, None, None, inner_content, script_path).await?; { let postprocessor = get_result_postprocessor(annotations.skip_result_postprocessing); diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index 98c292b97a..5fba884500 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -1528,6 +1528,11 @@ async fn compute_bool_from_expr( } } +struct FailureContext { + started_at: Arc>, + flow_job_id: Uuid, +} + /// resumes should be in order of timestamp ascending, so that more recent are at the end #[instrument(level = "trace", skip_all)] async fn transform_input( @@ -1537,6 +1542,7 @@ async fn transform_input( resumes: Arc>, resume: Arc>, approvers: Arc>, + failure_context: Option, by_id: &IdContext, client: &AuthedClient, ) -> windmill_common::error::Result>> { @@ -1553,6 +1559,13 @@ async fn transform_input( env.insert("resume".to_string(), resume); env.insert("resumes".to_string(), resumes); env.insert("approvers".to_string(), approvers); + if let Some(FailureContext { started_at, flow_job_id }) = failure_context { + env.insert("started_at".to_string(), started_at); + env.insert( + "flow_job_id".to_string(), + Arc::new(to_raw_value(&flow_job_id)), + ); + } } for (key, val) in input_transforms.into_iter() { @@ -2388,90 +2401,101 @@ async fn push_next_flow_job( false }; - let args: windmill_common::error::Result<_> = - if module.mock.is_some() && module.mock.as_ref().unwrap().enabled { - let mut hm = HashMap::new(); - hm.insert( - "previous_result".to_string(), - to_raw_value( - &module - .mock - .as_ref() - .unwrap() - .return_value - .clone() - .unwrap_or_else(|| serde_json::from_str("null").unwrap()), - ), - ); - Ok(Marc::new(hm)) - } else if let Some(id) = get_args_from_id { - let args = sqlx::query_scalar!( - "SELECT args AS \"args: Json>>\" + let args: windmill_common::error::Result<_> = if module.mock.is_some() + && module.mock.as_ref().unwrap().enabled + { + let mut hm = HashMap::new(); + hm.insert( + "previous_result".to_string(), + to_raw_value( + &module + .mock + .as_ref() + .unwrap() + .return_value + .clone() + .unwrap_or_else(|| serde_json::from_str("null").unwrap()), + ), + ); + Ok(Marc::new(hm)) + } else if let Some(id) = get_args_from_id { + let args = sqlx::query_scalar!( + "SELECT args AS \"args: Json>>\" FROM v2_job WHERE id = $1 AND workspace_id = $2", - id, - &flow_job.workspace_id - ) - .fetch_optional(db) - .warn_after_seconds(3) - .await?; - if let Some(args) = args { - Ok(Marc::new(args.map(|x| x.0).unwrap_or_else(HashMap::new))) - } else { - Ok(Marc::new(HashMap::new())) - } - } else if matches!(step, Step::PreprocessorStep) { - let mut hm = (*arc_flow_job_args).clone(); - hm.insert( - ENTRYPOINT_OVERRIDE.to_string(), - to_raw_value(&"preprocessor"), - ); - Ok(Marc::new(hm)) + id, + &flow_job.workspace_id + ) + .fetch_optional(db) + .warn_after_seconds(3) + .await?; + if let Some(args) = args { + Ok(Marc::new(args.map(|x| x.0).unwrap_or_else(HashMap::new))) } else { - let value = module.get_value(); - match &value { - Ok(_) if matches!(value, Ok(FlowModuleValue::Identity)) || is_skipped => { - serde_json::from_str( - &serde_json::to_string(&PreviousResult { - previous_result: Some(&arc_last_job_result), - }) - .unwrap(), - ) - .map(Marc::new) - .map_err(|e| error::Error::internal_err(format!("identity: {e:#}"))) - } - Ok( - FlowModuleValue::Script { input_transforms, .. } - | FlowModuleValue::RawScript { input_transforms, .. } - | FlowModuleValue::FlowScript { input_transforms, .. } - | FlowModuleValue::Flow { input_transforms, .. }, - ) => { - let ctx = get_transform_context(&flow_job, &previous_id, &status) - .warn_after_seconds(3) - .await?; - transform_context = Some(ctx); - let by_id = transform_context.as_ref().unwrap(); - transform_input( - arc_flow_job_args.clone(), - arc_last_job_result.clone(), - input_transforms, - resumes.clone(), - resume.clone(), - approvers.clone(), - by_id, - client, - ) - .warn_after_seconds(3) - .await - .map(Marc::new) - } - Ok(_) => Ok(arc_flow_job_args.clone()), - Err(e) => { - return Err(error::Error::internal_err(format!( - "module was not convertible to acceptable value {e:?}" - ))) - } + Ok(Marc::new(HashMap::new())) + } + } else if matches!(step, Step::PreprocessorStep) { + let mut hm = (*arc_flow_job_args).clone(); + hm.insert( + ENTRYPOINT_OVERRIDE.to_string(), + to_raw_value(&"preprocessor"), + ); + Ok(Marc::new(hm)) + } else { + let value = module.get_value(); + match &value { + Ok(_) if matches!(value, Ok(FlowModuleValue::Identity)) || is_skipped => { + serde_json::from_str( + &serde_json::to_string(&PreviousResult { + previous_result: Some(&arc_last_job_result), + }) + .unwrap(), + ) + .map(Marc::new) + .map_err(|e| error::Error::internal_err(format!("identity: {e:#}"))) } - }; + Ok( + FlowModuleValue::Script { input_transforms, .. } + | FlowModuleValue::RawScript { input_transforms, .. } + | FlowModuleValue::FlowScript { input_transforms, .. } + | FlowModuleValue::Flow { input_transforms, .. }, + ) => { + let ctx = get_transform_context(&flow_job, &previous_id, &status) + .warn_after_seconds(3) + .await?; + transform_context = Some(ctx); + let by_id = transform_context.as_ref().unwrap(); + // if a failure step, we add flow job id and started_at to the context. This is for error handling of triggers where we wrap scripts into single step flows + // It's to make its arguments consistent with global/workspace/schedule error handlers. + let failure_context = match step { + Step::FailureStep if flow_job.started_at.is_some() => Some(FailureContext { + started_at: Arc::new(to_raw_value(&flow_job.started_at.unwrap())), + flow_job_id: flow_job.id, + }), + _ => None, + }; + transform_input( + arc_flow_job_args.clone(), + arc_last_job_result.clone(), + input_transforms, + resumes.clone(), + resume.clone(), + approvers.clone(), + failure_context, + by_id, + client, + ) + .warn_after_seconds(3) + .await + .map(Marc::new) + } + Ok(_) => Ok(arc_flow_job_args.clone()), + Err(e) => { + return Err(error::Error::internal_err(format!( + "module was not convertible to acceptable value {e:?}" + ))) + } + } + }; tracing::debug!(id = %flow_job.id, root_id = %job_root, "flow job args computed"); let next_flow_transform = compute_next_flow_transform( @@ -2615,6 +2639,7 @@ async fn push_next_flow_job( resumes.clone(), resume.clone(), approvers.clone(), + None, &ctx, client, ) @@ -2664,6 +2689,7 @@ async fn push_next_flow_job( resumes.clone(), resume.clone(), approvers.clone(), + None, &ctx, client, ) @@ -2774,7 +2800,10 @@ async fn push_next_flow_job( &flow_job.created_by, email, permissioned_as, - Some(&format!("job-span-{}", flow_job.flow_innermost_root_job.unwrap_or(flow_job.id))), + Some(&format!( + "job-span-{}", + flow_job.flow_innermost_root_job.unwrap_or(flow_job.id) + )), scheduled_for_o, flow_job.schedule_path(), Some(flow_job.id), @@ -3294,9 +3323,16 @@ async fn compute_next_flow_transform( )) } FlowModuleValue::Script { path: script_path, hash: script_hash, tag_override, .. } => { - let payload = - script_to_payload(script_hash, script_path, db, flow_job, module, tag_override) - .await?; + let payload = script_to_payload( + script_hash, + script_path, + db, + flow_job, + module, + tag_override, + module.apply_preprocessor, + ) + .await?; Ok(NextFlowTransform::Continue( ContinuePayload::SingleJob(payload), NextStatus::NextStep, @@ -3922,7 +3958,16 @@ async fn payload_from_simple_module( flow_to_payload(path, delete_after_use, &flow_job.workspace_id, db).await? } FlowModuleValue::Script { path: script_path, hash: script_hash, tag_override, .. } => { - script_to_payload(script_hash, script_path, db, flow_job, module, tag_override).await? + script_to_payload( + script_hash, + script_path, + db, + flow_job, + module, + tag_override, + module.apply_preprocessor, + ) + .await? } FlowModuleValue::RawScript { path, @@ -4031,6 +4076,7 @@ async fn script_to_payload( flow_job: &MiniPulledJob, module: &FlowModule, tag_override: Option, + apply_preprocessor: Option, ) -> Result { let tag_override = if tag_override.as_ref().is_some_and(|x| x.trim().is_empty()) { None @@ -4071,6 +4117,10 @@ async fn script_to_payload( None }; ( + // We only apply the preprocessor if it's explicitly set to true in the module, + // which can only happen if the the flow is a SingleScriptFlow triggered by a trigger with retries or error handling. + // In that case, apply_preprocessor is still only set to true if the script has a preprocesor. + // We only check for script hash because SingleScriptFlow triggers specifies the script hash JobPayload::ScriptHash { hash, path: script_path, @@ -4081,7 +4131,7 @@ async fn script_to_payload( language, dedicated_worker, priority, - apply_preprocessor: false, + apply_preprocessor: apply_preprocessor.unwrap_or(false), }, tag_override.to_owned().or(tag), delete_after_use, diff --git a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte index f4c0cfb65f..9f4463018b 100644 --- a/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte +++ b/frontend/src/lib/components/ErrorOrRecoveryHandler.svelte @@ -33,30 +33,44 @@ const teamsHandlerScriptPath = hubPaths.teamsErrorHandler const teamsSuccessHandler = hubPaths.teamsSuccessHandler - export let errorOrRecovery: 'error' | 'recovery' | 'success' - export let isEditable: boolean - export let toggleText: string = 'Enable' - export let showScriptHelpText: boolean = false - export let handlerSelected: 'custom' | 'slack' | 'teams' + interface Props { + errorOrRecovery: 'error' | 'recovery' | 'success' + isEditable: boolean + toggleText?: string + showScriptHelpText?: boolean + handlerSelected: 'custom' | 'slack' | 'teams' + handlerPath: string | undefined + handlerExtraArgs: Record + customScriptTemplate: string + customHandlerKind?: 'flow' | 'script' + customTabTooltip?: import('svelte').Snippet + } - export let handlerPath: string | undefined - export let handlerExtraArgs: Record + let { + errorOrRecovery, + isEditable, + toggleText = 'Enable', + showScriptHelpText = false, + handlerSelected = $bindable(), + handlerPath = $bindable(), + handlerExtraArgs = $bindable(), + customScriptTemplate, + customHandlerKind = $bindable('script'), + customTabTooltip + }: Props = $props() - export let customInitialScriptPath: string | undefined - export let customScriptTemplate: string - export let customHandlerKind: 'flow' | 'script' = 'script' + let customHandlerSchema: Schema | undefined = $state() + let slackHandlerSchema: Schema | undefined = $state() + let isFetching: boolean = $state(false) - let customHandlerSchema: Schema | undefined - let slackHandlerSchema: Schema | undefined - let isFetching: boolean = false + let teams_channels: ListAvailableTeamsChannelsResponse = $state([]) + let teams_team_name: string | undefined = $state(undefined) - let teams_channels: ListAvailableTeamsChannelsResponse = [] - let teams_team_name: string | undefined = undefined + let workspaceConnectedToSlack: boolean | undefined = $state(undefined) + let workspaceConnectedToTeams: boolean | undefined = $state(undefined) - let workspaceConnectedToSlack: boolean | undefined = undefined - let workspaceConnectedToTeams: boolean | undefined = undefined - - let connectionTestJob: { uuid: string; is_success: boolean; in_progress: boolean } | undefined + let connectionTestJob: { uuid: string; is_success: boolean; in_progress: boolean } | undefined = + $state() async function loadSlackResources() { const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! }) @@ -173,7 +187,7 @@ } function isSlackHandler(scriptPath: string | undefined) { - if (scriptPath === undefined) { + if (!scriptPath) { return false } if (errorOrRecovery == 'error') { @@ -191,7 +205,7 @@ } function isTeamsHandler(scriptPath: string | undefined) { - if (scriptPath === undefined) { + if (!scriptPath) { return false } if (errorOrRecovery == 'error') { @@ -208,19 +222,27 @@ } } - $: { + $effect(() => { if ($workspaceStore) { loadSlackResources() loadTeamsResources() } - } + }) - let lastHandlerSelected: 'slack' | 'teams' | 'custom' | undefined = undefined - let channelCache = { + $effect(() => { + if (handlerSelected === 'slack' && isSlackHandler(handlerPath)) { + handlerExtraArgs['slack'] = '$res:f/slack_bot/bot_token' + } else { + handlerExtraArgs['slack'] = undefined + } + }) + + let lastHandlerSelected: 'slack' | 'teams' | 'custom' | undefined = $state(undefined) + let channelCache = $state({ slack: undefined as string | undefined, teams: undefined as string | undefined - } - $: { + }) + $effect(() => { if (lastHandlerSelected !== handlerSelected && lastHandlerSelected !== undefined) { if (lastHandlerSelected === 'teams' || lastHandlerSelected === 'slack') { channelCache[lastHandlerSelected] = handlerExtraArgs['channel'] @@ -228,51 +250,58 @@ if (handlerSelected === 'custom') { handlerExtraArgs['channel'] = '' + handlerPath = undefined } else { handlerExtraArgs['channel'] = channelCache[handlerSelected] ?? '' } } lastHandlerSelected = handlerSelected - } + }) - $: handlerPath && - !isSlackHandler(handlerPath) && - !isTeamsHandler(handlerPath) && - loadHandlerScriptArgs(handlerPath, [ - 'path', - 'workspace_id', - 'job_id', - 'is_flow', - 'schedule_path', - 'error', - 'error_started_at', - 'failed_times', - 'started_at', - 'success_times', - 'success_result', - 'success_started_at', - 'email' - ]).then((schema) => (customHandlerSchema = schema)) + $effect(() => { + handlerPath && + !isSlackHandler(handlerPath) && + !isTeamsHandler(handlerPath) && + loadHandlerScriptArgs(handlerPath, [ + 'path', + 'workspace_id', + 'job_id', + 'is_flow', + 'schedule_path', + 'error', + 'error_started_at', + 'failed_times', + 'started_at', + 'success_times', + 'success_result', + 'success_started_at', + 'email', + 'trigger_path' + ]).then((schema) => (customHandlerSchema = schema)) + }) - $: handlerPath && - isSlackHandler(handlerPath) && - loadHandlerScriptArgs(handlerPath, [ - 'path', - 'workspace_id', - 'job_id', - 'is_flow', - 'schedule_path', - 'error', - 'error_started_at', - 'failed_times', - 'started_at', - 'success_times', - 'success_result', - 'success_started_at', - 'email', - 'slack' - ]).then((schema) => (slackHandlerSchema = schema)) + $effect(() => { + handlerPath && + isSlackHandler(handlerPath) && + loadHandlerScriptArgs(handlerPath, [ + 'path', + 'workspace_id', + 'job_id', + 'is_flow', + 'schedule_path', + 'error', + 'error_started_at', + 'failed_times', + 'started_at', + 'success_times', + 'success_result', + 'success_started_at', + 'email', + 'trigger_path', + 'slack' + ]).then((schema) => (slackHandlerSchema = schema)) + })
@@ -281,7 +310,7 @@ Teams Custom - + {@render customTabTooltip?.()}
@@ -290,15 +319,15 @@
- {#if handlerPath === undefined} + {#if !handlerPath}
{/if} {/snippet} diff --git a/frontend/src/lib/components/triggers/gcp/utils.ts b/frontend/src/lib/components/triggers/gcp/utils.ts index e2bebce6cd..af51bc9c09 100644 --- a/frontend/src/lib/components/triggers/gcp/utils.ts +++ b/frontend/src/lib/components/triggers/gcp/utils.ts @@ -1,4 +1,4 @@ -import { GcpTriggerService } from '$lib/gen' +import { GcpTriggerService, type GcpTriggerData } from '$lib/gen' import { sendUserToast } from '$lib/toast' import { get, type Writable } from 'svelte/store' @@ -10,7 +10,15 @@ export async function saveGcpTriggerFromCfg( usedTriggerKinds: Writable ): Promise { try { - const requestBody = { + const errorHandlerAndRetries = !cfg.is_flow + ? { + error_handler_path: cfg.error_handler_path, + error_handler_args: cfg.error_handler_path ? cfg.error_handler_args : undefined, + retry: cfg.retry + } + : {} + + const requestBody: GcpTriggerData = { gcp_resource_path: cfg.gcp_resource_path, subscription_mode: cfg.subscription_mode, subscription_id: cfg.subscription_id, @@ -21,7 +29,8 @@ export async function saveGcpTriggerFromCfg( path: cfg.path, script_path: cfg.script_path, enabled: cfg.enabled, - is_flow: cfg.is_flow + is_flow: cfg.is_flow, + ...errorHandlerAndRetries } if (edit) { await GcpTriggerService.updateGcpTrigger({ diff --git a/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte b/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte index 31d927bb03..b4c3005322 100644 --- a/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte +++ b/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte @@ -5,7 +5,14 @@ import Path from '$lib/components/Path.svelte' import Required from '$lib/components/Required.svelte' import ScriptPicker from '$lib/components/ScriptPicker.svelte' - import { HttpTriggerService, VariableService, type AuthenticationMethod } from '$lib/gen' + import { + HttpTriggerService, + VariableService, + type AuthenticationMethod, + type HttpTrigger, + type NewHttpTrigger, + type Retry + } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptyString, sendUserToast } from '$lib/utils' import Section from '$lib/components/Section.svelte' @@ -31,9 +38,12 @@ import RouteBodyTransformerOption from './RouteBodyTransformerOption.svelte' import TestingBadge from '../testingBadge.svelte' import TriggerEditorToolbar from '../TriggerEditorToolbar.svelte' - import { handleConfigChange } from '../utils' + import { getHandlerType, handleConfigChange } from '../utils' import autosize from '$lib/autosize' import { untrack } from 'svelte' + import Tabs from '$lib/components/common/tabs/Tabs.svelte' + import Tab from '$lib/components/common/tabs/Tab.svelte' + import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' let { useDrawer = true, @@ -83,17 +93,22 @@ let variable_path = $state('') let signature_options_type = $state<'custom_script' | 'custom_signature'>('custom_signature') let can_write = $state(true) - let extraPerms = $state | undefined>(undefined) + let extraPerms = $state | undefined>(undefined) let summary: string | undefined = $state() let routeDescription: string | undefined = $state() + let error_handler_path: string | undefined = $state() + let error_handler_args: Record = $state({}) + let retry: Retry | undefined = $state() // Component references let s3FilePicker = $state(null) let s3Editor = $state(null) let variablePicker = $state(null) let variableEditor = $state(null) let drawer = $state(null) - let initialConfig: Record | undefined = undefined + let initialConfig: NewHttpTrigger | undefined = undefined let deploymentLoading = $state(false) + let optionTabSelected: 'request_options' | 'error_handler' | 'retries' = $state('request_options') + let errorHandlerSelected: 'slack' | 'teams' | 'custom' = $state('slack') const isAdmin = $derived($userStore?.is_admin || $userStore?.is_super_admin) const routeConfig = $derived.by(getRouteConfig) @@ -163,7 +178,7 @@ export async function openEdit( ePath: string, isFlow: boolean, - defaultConfig?: Record + defaultConfig?: Partial ) { drawerLoading = true let loader = setTimeout(() => { @@ -194,7 +209,10 @@ export async function openNew( nis_flow: boolean, fixedScriptPath_?: string, - defaultValues?: Record + defaultValues?: Partial & { + s3FileUploadRawMode?: boolean + signature_options_type?: 'custom_script' | 'custom_signature' + } ) { drawerLoading = true let loader = setTimeout(() => { @@ -227,6 +245,10 @@ wrap_body = defaultValues?.wrap_body ?? false summary = defaultValues?.summary ?? '' routeDescription = defaultValues?.description ?? '' + error_handler_path = defaultValues?.error_handler_path ?? undefined + error_handler_args = defaultValues?.error_handler_args ?? {} + retry = defaultValues?.retry ?? undefined + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } finally { clearTimeout(loader) drawerLoading = false @@ -234,37 +256,42 @@ } } - function loadTriggerConfig(cfg?: Record): void { - script_path = cfg?.script_path - initialScriptPath = cfg?.script_path - is_flow = cfg?.is_flow - path = cfg?.path - route_path = cfg?.route_path + function loadTriggerConfig(cfg?: Partial): void { + script_path = cfg?.script_path ?? '' + initialScriptPath = cfg?.script_path ?? '' + is_flow = cfg?.is_flow ?? false + path = cfg?.path ?? '' + route_path = cfg?.route_path ?? '' http_method = cfg?.http_method ?? 'post' - is_async = cfg?.is_async - workspaced_route = cfg?.workspaced_route - wrap_body = cfg?.wrap_body - raw_string = cfg?.raw_string - summary = cfg?.summary - routeDescription = cfg?.description + is_async = cfg?.is_async ?? false + workspaced_route = cfg?.workspaced_route ?? false + wrap_body = cfg?.wrap_body ?? false + raw_string = cfg?.raw_string ?? false + summary = cfg?.summary ?? '' + routeDescription = cfg?.description ?? '' authentication_resource_path = cfg?.authentication_resource_path ?? '' if (cfg?.authentication_method === 'custom_script') { authentication_method = 'signature' signature_options_type = 'custom_script' } else { - authentication_method = cfg?.authentication_method + authentication_method = cfg?.authentication_method ?? 'none' signature_options_type = 'custom_signature' } if (!isCloudHosted()) { static_asset_config = cfg?.static_asset_config s3FileUploadRawMode = !!cfg?.static_asset_config - is_static_website = cfg?.is_static_website + is_static_website = cfg?.is_static_website ?? false } - extraPerms = cfg?.extra_perms - can_write = canWrite(path, cfg?.extra_perms, $userStore) + extraPerms = cfg?.extra_perms ?? undefined + can_write = canWrite(path, cfg?.extra_perms ?? {}, $userStore) + + error_handler_path = cfg?.error_handler_path + error_handler_args = cfg?.error_handler_args ?? {} + retry = cfg?.retry + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } - async function loadTrigger(defaultConfig?: Record): Promise { + async function loadTrigger(defaultConfig?: Partial): Promise { if (defaultConfig) { loadTriggerConfig(defaultConfig) return @@ -301,7 +328,7 @@ } } - function getRouteConfig(): Record { + function getRouteConfig(): NewHttpTrigger { // If the user selects "signature" with the "custom_script" option, // we explicitly set the authentication method to "custom_script" // (which is a valid enum on its own in the backend) @@ -326,7 +353,10 @@ is_static_website, extra_perms: extraPerms, summary, - description: routeDescription + description: routeDescription, + error_handler_path, + error_handler_args, + retry } return nCfg @@ -574,175 +604,201 @@ {#if !is_static_website}
- {#if !static_asset_config} -
- -
- {/if} -
{/if} diff --git a/frontend/src/lib/components/triggers/http/utils.ts b/frontend/src/lib/components/triggers/http/utils.ts index 0b18186c9f..7591ccc4bd 100644 --- a/frontend/src/lib/components/triggers/http/utils.ts +++ b/frontend/src/lib/components/triggers/http/utils.ts @@ -42,13 +42,13 @@ export async function saveHttpRouteFromCfg( isAdmin: boolean, usedTriggerKinds: Writable ): Promise { - const requestBody = { + const requestBody: NewHttpTrigger = { path: routeCfg.path, script_path: routeCfg.script_path, is_flow: routeCfg.is_flow, is_async: routeCfg.is_async, authentication_method: routeCfg.authentication_method, - route_path: isAdmin || !edit ? routeCfg.route_path : undefined, + route_path: routeCfg.route_path, http_method: routeCfg.http_method, is_static_website: routeCfg.is_static_website, static_asset_config: routeCfg.static_asset_config, @@ -57,14 +57,20 @@ export async function saveHttpRouteFromCfg( wrap_body: routeCfg.wrap_body, raw_string: routeCfg.raw_string, description: routeCfg.description, - summary: routeCfg.summary + summary: routeCfg.summary, + error_handler_path: routeCfg.error_handler_path, + error_handler_args: routeCfg.error_handler_path ? routeCfg.error_handler_args : undefined, + retry: routeCfg.retry } try { if (edit) { await HttpTriggerService.updateHttpTrigger({ workspace: workspace, path: initialPath, - requestBody: requestBody + requestBody: { + ...requestBody, + route_path: isAdmin || !edit ? routeCfg.route_path : undefined + } }) sendUserToast(`Route ${routeCfg.path} updated`) } else { diff --git a/frontend/src/lib/components/triggers/kafka/KafkaTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/kafka/KafkaTriggerEditorInner.svelte index f677059d9c..adf8ff88da 100644 --- a/frontend/src/lib/components/triggers/kafka/KafkaTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/kafka/KafkaTriggerEditorInner.svelte @@ -5,7 +5,7 @@ import Path from '$lib/components/Path.svelte' import Required from '$lib/components/Required.svelte' import ScriptPicker from '$lib/components/ScriptPicker.svelte' - import { KafkaTriggerService } from '$lib/gen' + import { KafkaTriggerService, type Retry } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptyString, sendUserToast } from '$lib/utils' import Section from '$lib/components/Section.svelte' @@ -15,7 +15,10 @@ import { untrack, type Snippet } from 'svelte' import TriggerEditorToolbar from '../TriggerEditorToolbar.svelte' import { saveKafkaTriggerFromCfg } from './utils' - import { handleConfigChange, type Trigger } from '../utils' + import { getHandlerType, handleConfigChange, type Trigger } from '../utils' + import Tabs from '$lib/components/common/tabs/Tabs.svelte' + import Tab from '$lib/components/common/tabs/Tab.svelte' + import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' interface Props { useDrawer?: boolean @@ -74,6 +77,11 @@ let kafkaResourcePath = $state('') let kafkaCfg: Record = $state({}) let deploymentLoading = $state(false) + let optionTabSelected: 'error_handler' | 'retries' = $state('error_handler') + let errorHandlerSelected: 'slack' | 'teams' | 'custom' = $state('slack') + let error_handler_path: string | undefined = $state() + let error_handler_args: Record = $state({}) + let retry: Retry | undefined = $state() const isValid = $derived( !!kafkaResourcePath && @@ -152,6 +160,10 @@ path = nDefaultValues?.path ?? '' initialPath = '' dirtyPath = false + error_handler_path = nDefaultValues?.error_handler_path ?? undefined + error_handler_args = nDefaultValues?.error_handler_args ?? {} + retry = nDefaultValues?.retry ?? undefined + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } finally { clearTimeout(loadingTimeout) drawerLoading = false @@ -172,6 +184,10 @@ enabled = cfg?.enabled extra_perms = cfg?.extra_perms can_write = canWrite(path, cfg?.extra_perms, $userStore) + error_handler_path = cfg?.error_handler_path + error_handler_args = cfg?.error_handler_args ?? {} + retry = cfg?.retry + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } async function loadTrigger(defaultConfig?: Record): Promise { @@ -196,7 +212,10 @@ group_id: kafkaCfg.group_id, topics: kafkaCfg.topics, enabled, - extra_perms: extra_perms + extra_perms: extra_perms, + error_handler_path, + error_handler_args, + retry } } @@ -373,6 +392,28 @@ {can_write} showTestingBadge={isEditor} /> + +
+
+
+ + Error Handler + Retries + +
+ +
+
+
+
{/if} {/snippet} diff --git a/frontend/src/lib/components/triggers/kafka/utils.ts b/frontend/src/lib/components/triggers/kafka/utils.ts index d87a730d1b..08e89cc951 100644 --- a/frontend/src/lib/components/triggers/kafka/utils.ts +++ b/frontend/src/lib/components/triggers/kafka/utils.ts @@ -1,4 +1,4 @@ -import { KafkaTriggerService } from '$lib/gen' +import { KafkaTriggerService, type EditKafkaTrigger } from '$lib/gen' import { sendUserToast } from '$lib/toast' import { get, type Writable } from 'svelte/store' @@ -9,13 +9,21 @@ export async function saveKafkaTriggerFromCfg( workspace: string, usedTriggerKinds: Writable ): Promise { - const requestBody = { + const errorHandlerAndRetries = !cfg.is_flow + ? { + error_handler_path: cfg.error_handler_path, + error_handler_args: cfg.error_handler_path ? cfg.error_handler_args : undefined, + retry: cfg.retry + } + : {} + const requestBody: EditKafkaTrigger = { path: cfg.path, script_path: cfg.script_path, is_flow: cfg.is_flow, kafka_resource_path: cfg.kafka_resource_path, group_id: cfg.group_id, - topics: cfg.topics + topics: cfg.topics, + ...errorHandlerAndRetries } try { if (edit) { diff --git a/frontend/src/lib/components/triggers/mqtt/MqttEditorConfigSection.svelte b/frontend/src/lib/components/triggers/mqtt/MqttEditorConfigSection.svelte index 15ab7db886..cdfdaed056 100644 --- a/frontend/src/lib/components/triggers/mqtt/MqttEditorConfigSection.svelte +++ b/frontend/src/lib/components/triggers/mqtt/MqttEditorConfigSection.svelte @@ -6,14 +6,12 @@ import { Plus, X } from 'lucide-svelte' import Subsection from '$lib/components/Subsection.svelte' import ResourcePicker from '$lib/components/ResourcePicker.svelte' - import type { MqttClientVersion, MqttV3Config, MqttV5Config, MqttSubscribeTopic } from '$lib/gen' + import type { MqttClientVersion, MqttSubscribeTopic } from '$lib/gen' import Button from '$lib/components/common/button/Button.svelte' import { fade } from 'svelte/transition' import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte' - import Toggle from '$lib/components/Toggle.svelte' import { emptyStringTrimmed } from '$lib/utils' - import { DEFAULT_V3_CONFIG, DEFAULT_V5_CONFIG } from './constant' import TestTriggerConnection from '../TestTriggerConnection.svelte' import Tooltip from '$lib/components/Tooltip.svelte' import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte' @@ -21,11 +19,8 @@ interface Props { can_write?: boolean - headless?: boolean mqtt_resource_path?: string subscribe_topics?: MqttSubscribeTopic[] - v3_config?: MqttV3Config - v5_config?: MqttV5Config client_version?: MqttClientVersion isValid?: boolean client_id?: string @@ -34,22 +29,14 @@ let { can_write = false, - headless = false, mqtt_resource_path = $bindable(''), subscribe_topics = $bindable([]), - v3_config = $bindable(DEFAULT_V3_CONFIG), - v5_config = $bindable(DEFAULT_V5_CONFIG), client_version = $bindable('v5'), isValid = $bindable(false), client_id = $bindable(''), showTestingBadge = false }: Props = $props() - const activateV5Options = $state({ - topic_alias: Boolean(v5_config.topic_alias), - session_expiry_interval: Boolean(v5_config.session_expiry_interval) - }) - const isValidSubscribeTopics = (subscribe_topics: MqttSubscribeTopic[]): boolean => { if ( subscribe_topics.length === 0 || @@ -65,14 +52,14 @@ }) -
-
+
+
{#snippet header()} {#if showTestingBadge} {/if} {/snippet} -
+
{#if !emptyStringTrimmed(mqtt_resource_path)} @@ -196,128 +183,6 @@
- - -
- - {#snippet children({ item })} - - - {/snippet} - - - - - {#if client_version === 'v5'} - { - v5_config.clean_start = !v5_config.clean_start - }} - options={{ - right: 'Clean start', - rightTooltip: - 'Start a new session without any stored messages or subscriptions if enabled. Otherwise, resume the previous session with stored subscriptions and undelivered messages. The default setting is 0.', - rightDocumentationLink: - 'https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901039' - }} - class="py-1" - /> - -
- { - activateV5Options.session_expiry_interval = - !activateV5Options.session_expiry_interval - if (!activateV5Options.session_expiry_interval) { - v5_config.session_expiry_interval = undefined - } - }} - options={{ - right: 'Session expiry interval', - rightTooltip: - 'Defines the time in seconds that the broker will retain the session after disconnection. If set to 0, the session ends immediately. If set to 4,294,967,295, the session will be retained indefinitely. Otherwise, subscriptions and undelivered messages are stored until the interval expires.', - rightDocumentationLink: '' - }} - class="py-1" - /> - - {#if activateV5Options.session_expiry_interval} - - {/if} -
- -
- { - activateV5Options.topic_alias = !activateV5Options.topic_alias - if (!activateV5Options.topic_alias) { - v5_config.topic_alias = undefined - } - }} - options={{ - right: 'Topic alias maximum', - rightTooltip: - 'Defines the maximum topic alias value the client will accept from the broker. A value of 0 indicates that topic aliases are not supported. The default value is 65536, which is the maximum allowed topic alias.', - rightDocumentationLink: - 'https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901051' - }} - class="py-1" - /> - - {#if activateV5Options.topic_alias} - - {/if} -
- {:else if client_version === 'v3'} - { - v3_config.clean_session = !v3_config.clean_session - }} - options={{ - right: 'Clean session', - rightTooltip: - 'Starts a new session without any stored messages or subscriptions if enabled. Otherwise, it resumes the previous session with stored subscriptions and undelivered messages. The default value is 0', - rightDocumentationLink: '' - }} - class="py-1" - /> - {/if} -
-
diff --git a/frontend/src/lib/components/triggers/mqtt/MqttTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/mqtt/MqttTriggerEditorInner.svelte index 1a18df0bf3..66a3c93a41 100644 --- a/frontend/src/lib/components/triggers/mqtt/MqttTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/mqtt/MqttTriggerEditorInner.svelte @@ -15,13 +15,21 @@ type MqttClientVersion, type MqttV3Config, type MqttV5Config, - type MqttSubscribeTopic + type MqttSubscribeTopic, + type Retry } from '$lib/gen' import MqttEditorConfigSection from './MqttEditorConfigSection.svelte' import type { Snippet } from 'svelte' import TriggerEditorToolbar from '../TriggerEditorToolbar.svelte' import { saveMqttTriggerFromCfg } from './utils' - import { handleConfigChange, type Trigger } from '../utils' + import { getHandlerType, handleConfigChange, type Trigger } from '../utils' + import Tabs from '$lib/components/common/tabs/Tabs.svelte' + import Tab from '$lib/components/common/tabs/Tab.svelte' + import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' + import Toggle from '$lib/components/Toggle.svelte' + import ToggleButtonGroup from '$lib/components/common/toggleButton-v2/ToggleButtonGroup.svelte' + import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte' + import { DEFAULT_V3_CONFIG, DEFAULT_V5_CONFIG } from './constant' interface Props { useDrawer?: boolean @@ -76,19 +84,30 @@ let drawerLoading = $state(true) let showLoading = $state(false) let subscribe_topics: MqttSubscribeTopic[] = $state([]) - let v3_config: MqttV3Config | undefined = $state({}) - let v5_config: MqttV5Config | undefined = $state({}) + let v3_config: MqttV3Config = $state(DEFAULT_V3_CONFIG) + let v5_config: MqttV5Config = $state(DEFAULT_V5_CONFIG) let client_version: MqttClientVersion | undefined = $state() let client_id: string | undefined = $state('') let isValid: boolean = $state(false) let initialConfig: Record | undefined = {} let deploymentLoading = $state(false) + let errorHandlerSelected: 'slack' | 'teams' | 'custom' = $state('slack') + let error_handler_path: string | undefined = $state() + let error_handler_args: Record = $state({}) + let retry: Retry | undefined = $state() + + let optionTabSelected: 'connection_options' | 'error_handler' | 'retries' = + $state('connection_options') const mqttConfig = $derived.by(getSaveCfg) const captureConfig = $derived.by(isEditor ? getCaptureConfig : () => ({})) const saveDisabled = $derived( pathError != '' || emptyString(script_path) || !can_write || !isValid ) + const activateV5Options = $state({ + topic_alias_maximum: Boolean(DEFAULT_V5_CONFIG.topic_alias_maximum), + session_expiry_interval: Boolean(DEFAULT_V5_CONFIG.session_expiry_interval) + }) $effect(() => { is_flow = itemKind === 'flow' @@ -147,6 +166,16 @@ client_version = defaultValues?.client_version ?? 'v5' client_id = defaultValues?.client_id ?? '' enabled = defaultValues?.enabled ?? false + error_handler_path = defaultValues?.error_handler_path ?? undefined + error_handler_args = defaultValues?.error_handler_args ?? {} + retry = defaultValues?.retry ?? undefined + errorHandlerSelected = getHandlerType(error_handler_path ?? '') + v3_config = defaultValues?.v3_config ?? DEFAULT_V3_CONFIG + v5_config = defaultValues?.v5_config ?? DEFAULT_V5_CONFIG + activateV5Options.topic_alias_maximum = Boolean(defaultValues?.v5_config?.topic_alias_maximum) + activateV5Options.session_expiry_interval = Boolean( + defaultValues?.v5_config?.session_expiry_interval + ) } finally { clearTimeout(loadingTimeout) drawerLoading = false @@ -164,10 +193,16 @@ path = cfg?.path enabled = cfg?.enabled client_version = cfg?.client_version - v3_config = cfg?.v3_config - v5_config = cfg?.v5_config + v3_config = cfg?.v3_config ?? DEFAULT_V3_CONFIG + v5_config = cfg?.v5_config ?? DEFAULT_V5_CONFIG client_id = cfg?.client_id ?? '' can_write = canWrite(cfg?.path, cfg?.extra_perms, $userStore) + error_handler_path = cfg?.error_handler_path + error_handler_args = cfg?.error_handler_args ?? {} + retry = cfg?.retry + errorHandlerSelected = getHandlerType(error_handler_path ?? '') + activateV5Options.topic_alias_maximum = Boolean(v5_config.topic_alias_maximum) + activateV5Options.session_expiry_interval = Boolean(v5_config.session_expiry_interval) } catch (error) { sendUserToast(`Could not load mqtt trigger config: ${error.body}`, true) } @@ -201,7 +236,10 @@ path, script_path, enabled, - is_flow + is_flow, + error_handler_path, + error_handler_args, + retry } } @@ -383,13 +421,149 @@ bind:subscribe_topics {can_write} bind:client_version - bind:v3_config - bind:v5_config bind:isValid bind:client_id - headless={true} showTestingBadge={isEditor} /> + +
+
+
+ + Connection Options + Error Handler + Retries + +
+ {#if optionTabSelected === 'connection_options'} +
+ + {#snippet children({ item })} + + + {/snippet} + + + + + {#if client_version === 'v5'} + + +
+ { + if (!ev.detail) { + v5_config.session_expiry_interval = undefined + } + }} + options={{ + right: 'Session expiry interval', + rightTooltip: + 'Defines the time in seconds that the broker will retain the session after disconnection. If set to 0, the session ends immediately. If set to 4,294,967,295, the session will be retained indefinitely. Otherwise, subscriptions and undelivered messages are stored until the interval expires.', + rightDocumentationLink: '' + }} + class="py-1" + /> + + {#if activateV5Options.session_expiry_interval} + + {/if} +
+ +
+ { + if (!ev.detail) { + v5_config.topic_alias_maximum = undefined + } + }} + options={{ + right: 'Topic alias maximum', + rightTooltip: + 'Defines the maximum topic alias value the client will accept from the broker. A value of 0 indicates that topic aliases are not supported. The default value is 65536, which is the maximum allowed topic alias.', + rightDocumentationLink: + 'https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901051' + }} + class="py-1" + /> + + {#if activateV5Options.topic_alias_maximum} + + {/if} +
+ {:else if client_version === 'v3'} + { + v3_config.clean_session = !v3_config.clean_session + }} + options={{ + right: 'Clean session', + rightTooltip: + 'Starts a new session without any stored messages or subscriptions if enabled. Otherwise, it resumes the previous session with stored subscriptions and undelivered messages. The default value is 0', + rightDocumentationLink: '' + }} + class="py-1" + /> + {/if} +
+ {:else} + + {/if} +
+
+
+
{/if} {/snippet} diff --git a/frontend/src/lib/components/triggers/mqtt/utils.ts b/frontend/src/lib/components/triggers/mqtt/utils.ts index 14e264a088..2b9f978743 100644 --- a/frontend/src/lib/components/triggers/mqtt/utils.ts +++ b/frontend/src/lib/components/triggers/mqtt/utils.ts @@ -1,4 +1,4 @@ -import { MqttTriggerService } from '$lib/gen' +import { MqttTriggerService, type EditMqttTrigger } from '$lib/gen' import { sendUserToast } from '$lib/toast' import { get, type Writable } from 'svelte/store' @@ -9,7 +9,14 @@ export async function saveMqttTriggerFromCfg( workspace: string, usedTriggerKinds: Writable ): Promise { - const requestBody = { + const errorHandlerAndRetries = !cfg.is_flow + ? { + error_handler_path: cfg.error_handler_path, + error_handler_args: cfg.error_handler_path ? cfg.error_handler_args : undefined, + retry: cfg.retry + } + : {} + const requestBody: EditMqttTrigger = { client_id: cfg.client_id, client_version: cfg.client_version, v3_config: cfg.v3_config, @@ -19,7 +26,8 @@ export async function saveMqttTriggerFromCfg( path: cfg.path, script_path: cfg.script_path, enabled: cfg.enabled, - is_flow: cfg.is_flow + is_flow: cfg.is_flow, + ...errorHandlerAndRetries } try { if (edit) { diff --git a/frontend/src/lib/components/triggers/nats/NatsTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/nats/NatsTriggerEditorInner.svelte index 3746b87d97..2e780de59e 100644 --- a/frontend/src/lib/components/triggers/nats/NatsTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/nats/NatsTriggerEditorInner.svelte @@ -5,7 +5,7 @@ import Path from '$lib/components/Path.svelte' import Required from '$lib/components/Required.svelte' import ScriptPicker from '$lib/components/ScriptPicker.svelte' - import { NatsTriggerService } from '$lib/gen' + import { NatsTriggerService, type Retry } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptyString, sendUserToast } from '$lib/utils' import Section from '$lib/components/Section.svelte' @@ -15,7 +15,10 @@ import { untrack, type Snippet } from 'svelte' import TriggerEditorToolbar from '../TriggerEditorToolbar.svelte' import { saveNatsTriggerFromCfg } from './utils' - import { handleConfigChange, type Trigger } from '../utils' + import { getHandlerType, handleConfigChange, type Trigger } from '../utils' + import Tabs from '$lib/components/common/tabs/Tabs.svelte' + import Tab from '$lib/components/common/tabs/Tab.svelte' + import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' interface Props { useDrawer?: boolean @@ -78,6 +81,11 @@ let initialConfig: Record | undefined = undefined let deploymentLoading = $state(false) let isValid = $state(false) + let optionTabSelected: 'error_handler' | 'retries' = $state('error_handler') + let errorHandlerSelected: 'slack' | 'teams' | 'custom' = $state('slack') + let error_handler_path: string | undefined = $state() + let error_handler_args: Record = $state({}) + let retry: Retry | undefined = $state() const saveDisabled = $derived( pathError != '' || emptyString(script_path) || !can_write || !isValid @@ -144,6 +152,10 @@ dirtyPath = false defaultValues = nDefaultValues enabled = nDefaultValues?.enabled ?? false + error_handler_path = nDefaultValues?.error_handler_path ?? undefined + error_handler_args = nDefaultValues?.error_handler_args ?? {} + retry = nDefaultValues?.retry ?? undefined + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } finally { clearTimeout(loadingTimeout) drawerLoading = false @@ -163,6 +175,10 @@ useJetstream = cfg?.use_jetstream || false enabled = cfg?.enabled can_write = canWrite(cfg?.path, cfg?.extra_perms, $userStore) + error_handler_path = cfg?.error_handler_path + error_handler_args = cfg?.error_handler_args ?? {} + retry = cfg?.retry + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } async function loadTrigger(defaultConfig?: Record): Promise { @@ -188,7 +204,10 @@ stream_name: streamName, consumer_name: consumerName, subjects, - use_jetstream: useJetstream + use_jetstream: useJetstream, + error_handler_path, + error_handler_args, + retry } } @@ -382,6 +401,28 @@ {can_write} showTestingBadge={isEditor} /> + +
+
+
+ + Error Handler + Retries + +
+ +
+
+
+
{/if} {/snippet} diff --git a/frontend/src/lib/components/triggers/nats/utils.ts b/frontend/src/lib/components/triggers/nats/utils.ts index 926956314c..0d6706bebc 100644 --- a/frontend/src/lib/components/triggers/nats/utils.ts +++ b/frontend/src/lib/components/triggers/nats/utils.ts @@ -1,4 +1,4 @@ -import { NatsTriggerService } from '$lib/gen' +import { NatsTriggerService, type EditNatsTrigger } from '$lib/gen' import { sendUserToast } from '$lib/toast' import { get, type Writable } from 'svelte/store' @@ -9,7 +9,14 @@ export async function saveNatsTriggerFromCfg( workspace: string, usedTriggerKinds: Writable ): Promise { - const requestBody = { + const errorHandlerAndRetries = !cfg.is_flow + ? { + error_handler_path: cfg.error_handler_path, + error_handler_args: cfg.error_handler_path ? cfg.error_handler_args : undefined, + retry: cfg.retry + } + : {} + const requestBody: EditNatsTrigger = { path: cfg.path, script_path: cfg.script_path, is_flow: cfg.is_flow, @@ -17,7 +24,8 @@ export async function saveNatsTriggerFromCfg( stream_name: cfg.stream_name, consumer_name: cfg.consumer_name, subjects: cfg.subjects, - use_jetstream: cfg.use_jetstream + use_jetstream: cfg.use_jetstream, + ...errorHandlerAndRetries } try { if (edit) { diff --git a/frontend/src/lib/components/triggers/postgres/PostgresTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/postgres/PostgresTriggerEditorInner.svelte index 32a31fd75b..6d0d2c5f85 100644 --- a/frontend/src/lib/components/triggers/postgres/PostgresTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/postgres/PostgresTriggerEditorInner.svelte @@ -5,7 +5,7 @@ import Path from '$lib/components/Path.svelte' import Required from '$lib/components/Required.svelte' import ScriptPicker from '$lib/components/ScriptPicker.svelte' - import { PostgresTriggerService, type Language, type Relations } from '$lib/gen' + import { PostgresTriggerService, type Language, type Relations, type Retry } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptyString, emptyStringTrimmed, sendUserToast } from '$lib/utils' import Section from '$lib/components/Section.svelte' @@ -27,7 +27,8 @@ import { untrack, type Snippet } from 'svelte' import TriggerEditorToolbar from '../TriggerEditorToolbar.svelte' import TestingBadge from '../testingBadge.svelte' - import { handleConfigChange, type Trigger } from '../utils' + import { getHandlerType, handleConfigChange, type Trigger } from '../utils' + import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' import { fade } from 'svelte/transition' import MultiSelect from '$lib/components/select/MultiSelect.svelte' import { safeSelectItems } from '$lib/components/select/utils.svelte' @@ -104,6 +105,11 @@ let creatingSlot: boolean = $state(false) let creatingPublication: boolean = $state(false) let pg14: boolean = $derived(postgresVersion.startsWith('14')) + let optionTabSelected: 'error_handler' | 'retries' = $state('error_handler') + let errorHandlerSelected: 'slack' | 'teams' | 'custom' = $state('slack') + let error_handler_path: string | undefined = $state() + let error_handler_args: Record = $state({}) + let retry: Retry | undefined = $state() const errorMessage = $derived.by(() => { if (relations && relations.length > 0) { @@ -258,6 +264,10 @@ table_to_track: [] } ] + error_handler_path = defaultValues?.error_handler_path ?? undefined + error_handler_args = defaultValues?.error_handler_args ?? {} + retry = defaultValues?.retry ?? undefined + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } finally { clearTimeout(loadingTimeout) drawerLoading = false @@ -280,7 +290,10 @@ transaction_to_track: transaction_to_track, table_to_track: relations } - : undefined + : undefined, + error_handler_path, + error_handler_args, + retry } return cfg } @@ -297,6 +310,10 @@ can_write = canWrite(path, cfg?.extra_perms, $userStore) transaction_to_track = [...cfg?.publication?.transaction_to_track] relations = cfg?.publication?.table_to_track ?? [] + error_handler_path = cfg?.error_handler_path + error_handler_args = cfg?.error_handler_args ?? {} + retry = cfg?.retry + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } async function loadTrigger(defaultConfig?: Record): Promise { @@ -780,6 +797,28 @@ {/if} + +
+
+
+ + Error Handler + Retries + +
+ +
+
+
+
{/if} {/snippet} diff --git a/frontend/src/lib/components/triggers/postgres/utils.ts b/frontend/src/lib/components/triggers/postgres/utils.ts index 15045e00e0..f7da464367 100644 --- a/frontend/src/lib/components/triggers/postgres/utils.ts +++ b/frontend/src/lib/components/triggers/postgres/utils.ts @@ -1,4 +1,4 @@ -import { PostgresTriggerService, type Relations } from '$lib/gen' +import { PostgresTriggerService, type EditPostgresTrigger, type Relations } from '$lib/gen' import { sendUserToast } from '$lib/toast' import { emptyString } from '$lib/utils' import type { Writable } from 'svelte/store' @@ -110,7 +110,14 @@ export async function savePostgresTriggerFromCfg( usedTriggerKinds: Writable ): Promise { try { - const requestBody = { + const errorHandlerAndRetries = !config.is_flow + ? { + error_handler_path: config.error_handler_path, + error_handler_args: config.error_handler_path ? config.error_handler_args : undefined, + retry: config.retry + } + : {} + const requestBody: EditPostgresTrigger = { path: config.path, script_path: config.script_path, is_flow: config.is_flow, @@ -118,7 +125,8 @@ export async function savePostgresTriggerFromCfg( replication_slot_name: config.replication_slot_name, publication_name: config.publication_name, publication: config.publication, - enabled: config.enabled + enabled: config.enabled, + ...errorHandlerAndRetries } if (edit) { await PostgresTriggerService.updatePostgresTrigger({ diff --git a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte index efe399aa0b..0751ed13b9 100644 --- a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte +++ b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte @@ -52,7 +52,6 @@ let optionTabSelected: 'error_handler' | 'recovery_handler' | 'success_handler' | 'retries' = $state('error_handler') - let is_flow: boolean = $state(false) let initialPath = $state('') let edit = $state(true) let schedule: string = $state('0 0 12 * *') @@ -63,19 +62,17 @@ let timezone: string = $state(Intl.DateTimeFormat().resolvedOptions().timeZone) let paused_until: string | undefined = $state(undefined) let itemKind: 'flow' | 'script' = $state('script') + let is_flow: boolean = $derived.by(() => itemKind === 'flow') let errorHandleritemKind: 'flow' | 'script' = $state('script') let wsErrorHandlerMuted: boolean = $state(false) let errorHandlerPath: string | undefined = $state(undefined) - let errorHandlerCustomInitialPath: string | undefined = $state(undefined) let errorHandlerSelected: 'custom' | 'slack' | 'teams' = $state('slack') let errorHandlerExtraArgs: Record = $state({}) let recoveryHandlerPath: string | undefined = $state(undefined) - let recoveryHandlerCustomInitialPath: string | undefined = $state(undefined) let recoveryHandlerSelected: 'custom' | 'slack' | 'teams' = $state('slack') let recoveryHandlerItemKind: 'flow' | 'script' = $state('script') let recoveryHandlerExtraArgs: Record = $state({}) let successHandlerPath: string | undefined = $state(undefined) - let successHandlerCustomInitialPath: string | undefined = $state(undefined) let successHandlerSelected: 'custom' | 'slack' | 'teams' = $state('slack') let successHandlerItemKind: 'flow' | 'script' = $state('script') let successHandlerExtraArgs: Record = $state({}) @@ -124,9 +121,8 @@ drawerLoading = true try { drawer?.openDrawer() - is_flow = isFlow initialPath = ePath - itemKind = is_flow ? 'flow' : 'script' + itemKind = isFlow ? 'flow' : 'script' path = defaultCfg?.path ?? ePath await loadSchedule(defaultCfg) edit = true @@ -146,7 +142,6 @@ let splitted = s.on_failure.split('/') errorHandleritemKind = splitted[0] as 'flow' | 'script' errorHandlerPath = splitted.slice(1)?.join('/') - errorHandlerCustomInitialPath = errorHandlerPath failedTimes = s.on_failure_times ?? 1 failedExact = s.on_failure_exact ?? false errorHandlerExtraArgs = s.on_failure_extra_args ?? {} @@ -154,7 +149,6 @@ } else { errorHandlerPath = undefined errorHandleritemKind = 'script' - errorHandlerCustomInitialPath = undefined errorHandlerExtraArgs = {} failedExact = false failedTimes = 1 @@ -164,14 +158,12 @@ let splitted = s.on_recovery.split('/') recoveryHandlerItemKind = splitted[0] as 'flow' | 'script' recoveryHandlerPath = splitted.slice(1)?.join('/') - recoveryHandlerCustomInitialPath = recoveryHandlerPath recoveredTimes = s.on_recovery_times ?? 1 recoveryHandlerExtraArgs = s.on_recovery_extra_args ?? {} recoveryHandlerSelected = getHandlerType('recovery', recoveryHandlerPath) } else { recoveryHandlerPath = undefined recoveryHandlerItemKind = 'script' - recoveryHandlerCustomInitialPath = undefined recoveredTimes = 1 recoveryHandlerSelected = 'slack' recoveryHandlerExtraArgs = {} @@ -180,13 +172,11 @@ let splitted = s.on_success.split('/') successHandlerItemKind = splitted[0] as 'flow' | 'script' successHandlerPath = splitted.slice(1)?.join('/') - successHandlerCustomInitialPath = successHandlerPath successHandlerExtraArgs = s.on_success_extra_args ?? {} successHandlerSelected = getHandlerType('success', successHandlerPath) } else { successHandlerPath = undefined successHandlerItemKind = 'script' - successHandlerCustomInitialPath = undefined successHandlerSelected = 'slack' successHandlerExtraArgs = {} } @@ -212,7 +202,6 @@ errorHandleritemKind = splitted[0] as 'flow' | 'script' errorHandlerPath = splitted.slice(1)?.join('/') errorHandlerExtraArgs = defaultErrorHandlerMaybe['errorHandlerExtraArgs'] - errorHandlerCustomInitialPath = errorHandlerPath errorHandlerSelected = getHandlerType('error', errorHandlerPath) failedTimes = defaultErrorHandlerMaybe['failedTimes'] failedExact = defaultErrorHandlerMaybe['failedExact'] @@ -221,7 +210,6 @@ errorHandlerPath = undefined errorHandleritemKind = 'script' errorHandlerExtraArgs = {} - errorHandlerCustomInitialPath = undefined errorHandlerSelected = 'slack' failedTimes = 1 failedExact = false @@ -231,14 +219,12 @@ recoveryHandlerItemKind = splitted[0] as 'flow' | 'script' recoveryHandlerPath = splitted.slice(1)?.join('/') recoveryHandlerExtraArgs = defaultRecoveryHandlerMaybe['recoveryHandlerExtraArgs'] - recoveryHandlerCustomInitialPath = recoveryHandlerPath recoveryHandlerSelected = getHandlerType('recovery', recoveryHandlerPath) recoveredTimes = defaultRecoveryHandlerMaybe['recoveredTimes'] } else { recoveryHandlerPath = undefined recoveryHandlerItemKind = 'script' recoveryHandlerExtraArgs = {} - recoveryHandlerCustomInitialPath = undefined recoveryHandlerSelected = 'slack' recoveredTimes = 1 } @@ -247,14 +233,12 @@ successHandlerItemKind = splitted[0] as 'flow' | 'script' successHandlerPath = splitted.slice(1)?.join('/') successHandlerExtraArgs = defaultSuccessHandlerMaybe['successHandlerExtraArgs'] - successHandlerCustomInitialPath = successHandlerPath successHandlerSelected = getHandlerType('success', successHandlerPath) recoveredTimes = defaultSuccessHandlerMaybe['recoveredTimes'] } else { successHandlerPath = undefined successHandlerItemKind = 'script' successHandlerExtraArgs = {} - successHandlerCustomInitialPath = undefined successHandlerSelected = 'slack' } } @@ -283,9 +267,8 @@ } drawer?.openDrawer() runnable = undefined - is_flow = s?.is_flow ?? nis_flow edit = false - itemKind = is_flow ? 'flow' : 'script' + itemKind = (s?.is_flow ?? nis_flow) ? 'flow' : 'script' initialScriptPath = initial_script_path ?? '' path = initNewPath ? '' @@ -319,16 +302,6 @@ } } - async function resetRetries() { - if (itemKind === 'flow') { - retry = undefined - } - } - - $effect(() => { - ;(is_flow = itemKind == 'flow') && resetRetries() - }) - // set isValid to true when a script/flow without any properties is selected $effect(() => { setDefaultValid(draftSchema ?? runnable?.schema) @@ -474,7 +447,7 @@ script_path = cfg.script_path ?? '' await loadScript(script_path) - is_flow = cfg.is_flow + itemKind = cfg.is_flow ? 'flow' : 'script' no_flow_overlap = cfg.no_flow_overlap ?? false wsErrorHandlerMuted = cfg.ws_error_handler_muted ?? false retry = cfg.retry @@ -482,7 +455,6 @@ let splitted = cfg.on_failure.split('/') errorHandleritemKind = splitted[0] as 'flow' | 'script' errorHandlerPath = splitted.slice(1)?.join('/') - errorHandlerCustomInitialPath = errorHandlerPath failedTimes = cfg.on_failure_times ?? 1 failedExact = cfg.on_failure_exact ?? false errorHandlerExtraArgs = cfg.on_failure_extra_args ?? {} @@ -490,7 +462,6 @@ } else { errorHandlerPath = undefined errorHandleritemKind = 'script' - errorHandlerCustomInitialPath = undefined errorHandlerExtraArgs = {} failedExact = false failedTimes = 1 @@ -500,14 +471,12 @@ let splitted = cfg.on_recovery.split('/') recoveryHandlerItemKind = splitted[0] as 'flow' | 'script' recoveryHandlerPath = splitted.slice(1)?.join('/') - recoveryHandlerCustomInitialPath = recoveryHandlerPath recoveredTimes = cfg.on_recovery_times ?? 1 recoveryHandlerExtraArgs = cfg.on_recovery_extra_args ?? {} recoveryHandlerSelected = getHandlerType('recovery', recoveryHandlerPath ?? '') } else { recoveryHandlerPath = undefined recoveryHandlerItemKind = 'script' - recoveryHandlerCustomInitialPath = undefined recoveredTimes = 1 recoveryHandlerSelected = 'slack' recoveryHandlerExtraArgs = {} @@ -516,13 +485,11 @@ let splitted = cfg.on_success.split('/') successHandlerItemKind = splitted[0] as 'flow' | 'script' successHandlerPath = splitted.slice(1)?.join('/') - successHandlerCustomInitialPath = successHandlerPath successHandlerExtraArgs = cfg.on_success_extra_args ?? {} successHandlerSelected = getHandlerType('success', successHandlerPath ?? '') } else { successHandlerPath = undefined successHandlerItemKind = 'script' - successHandlerCustomInitialPath = undefined successHandlerSelected = 'slack' successHandlerExtraArgs = {} } @@ -572,21 +539,6 @@ return 'custom' } - function isSlackHandler(isSlackHandler: 'error' | 'recovery' | 'success', scriptPath: string) { - if (isSlackHandler == 'error') { - return ( - scriptPath.startsWith('hub/') && - scriptPath.endsWith('/workspace-or-schedule-error-handler-slack') - ) - } else if (isSlackHandler == 'recovery') { - return ( - scriptPath.startsWith('hub/') && scriptPath.endsWith('/schedule-recovery-handler-slack') - ) - } else { - return scriptPath.startsWith('hub/') && scriptPath.endsWith('/schedule-success-handler-slack') - } - } - let drawer: Drawer | undefined = $state() let pathC: Path | undefined = $state() @@ -613,26 +565,6 @@ } function getScheduleCfg(): Record { - let errorHadlerExtraArgsDerived = structuredClone($state.snapshot(errorHandlerExtraArgs)) - if (errorHandlerPath !== undefined && isSlackHandler('error', errorHandlerPath)) { - errorHadlerExtraArgsDerived['slack'] = '$res:f/slack_bot/bot_token' - } else { - errorHadlerExtraArgsDerived['slack'] = undefined - } - - let recoveryHandlerExtraArgsDerived = structuredClone($state.snapshot(recoveryHandlerExtraArgs)) - if (recoveryHandlerPath !== undefined && isSlackHandler('recovery', recoveryHandlerPath)) { - recoveryHandlerExtraArgsDerived['slack'] = '$res:f/slack_bot/bot_token' - } else { - recoveryHandlerExtraArgsDerived['slack'] = undefined - } - - let successHandlerExtraArgsDerived = structuredClone($state.snapshot(successHandlerExtraArgs)) - if (successHandlerPath !== undefined && isSlackHandler('success', successHandlerPath)) { - successHandlerExtraArgsDerived['slack'] = '$res:f/slack_bot/bot_token' - } else { - successHandlerExtraArgsDerived['slack'] = undefined - } return { path: path, schedule: formatCron(schedule), @@ -644,16 +576,16 @@ on_failure: errorHandlerPath ? `${errorHandleritemKind}/${errorHandlerPath}` : undefined, on_failure_times: failedTimes, on_failure_exact: failedExact, - on_failure_extra_args: errorHandlerPath ? errorHadlerExtraArgsDerived : undefined, + on_failure_extra_args: errorHandlerPath ? errorHandlerExtraArgs : undefined, on_recovery: recoveryHandlerPath ? `${recoveryHandlerItemKind}/${recoveryHandlerPath}` : undefined, on_recovery_times: recoveredTimes, - on_recovery_extra_args: recoveryHandlerPath ? recoveryHandlerExtraArgsDerived : {}, + on_recovery_extra_args: recoveryHandlerPath ? recoveryHandlerExtraArgs : {}, on_success: successHandlerPath ? `${successHandlerItemKind}/${successHandlerPath}` : undefined, - on_success_extra_args: successHandlerPath ? successHandlerExtraArgsDerived : {}, + on_success_extra_args: successHandlerPath ? successHandlerExtraArgs : {}, ws_error_handler_muted: wsErrorHandlerMuted, retry: retry, summary: summary != '' ? summary : undefined, @@ -960,14 +892,14 @@ {/snippet} {#snippet errorHandler()} -
+
{#if !loading} Error Handler Recovery Handler Success Handler + Retries {#if itemKind === 'script'} - Retries Custom tag {/if} @@ -1017,19 +949,21 @@ showScriptHelpText={true} bind:handlerSelected={errorHandlerSelected} bind:handlerPath={errorHandlerPath} - customInitialScriptPath={errorHandlerCustomInitialPath} toggleText="Alert channel on error" - customScriptTemplate="/scripts/add?hub=hub%2F9081%2Fwindmill%2Fschedule_error_handler_template" + customScriptTemplate="/scripts/add?hub=hub%2F19743%2Fwindmill%2Fschedule_error_handler_template" bind:customHandlerKind={errorHandleritemKind} bind:handlerExtraArgs={errorHandlerExtraArgs} > - - + {#snippet customTabTooltip()}
The following args will be passed to the error handler:
    +
  • workspace_id: The ID of the workspace that the schedule belongs to.
  • +
  • job_id: The UUID of the job that errored.
  • path: The path of the script or flow that failed.
  • is_flow: Whether the runnable is a flow.
  • schedule_path: The path of the schedule.
  • @@ -1043,7 +977,7 @@
-
+ {/snippet}
@@ -1108,14 +1042,12 @@ errorOrRecovery="recovery" bind:handlerSelected={recoveryHandlerSelected} bind:handlerPath={recoveryHandlerPath} - customInitialScriptPath={recoveryHandlerCustomInitialPath} toggleText="Alert channel when error recovered" customScriptTemplate="/scripts/add?hub=hub%2F9082%2Fwindmill%2Fschedule_recovery_handler_template" bind:customHandlerKind={recoveryHandlerItemKind} bind:handlerExtraArgs={recoveryHandlerExtraArgs} > - - + {#snippet customTabTooltip()}
- + {/snippet}
- - + {#snippet customTabTooltip()}
- + {/snippet} {:else if optionTabSelected === 'retries'} @@ -1239,7 +1169,20 @@ step in the flow editor. {/snippet} - + {#if itemKind !== 'script'} + + Error Handler and Retries are only available for scripts. For flows, use the built-in error handler + and retries. + + {:else} + + {/if} {:else if optionTabSelected === 'tag'}
| undefined = undefined let deploymentLoading = $state(false) + let optionTabSelected: 'error_handler' | 'retries' = $state('error_handler') + let errorHandlerSelected: 'slack' | 'teams' | 'custom' = $state('slack') + let error_handler_path: string | undefined = $state() + let error_handler_args: Record = $state({}) + let retry: Retry | undefined = $state() const sqsConfig = $derived.by(getSaveCfg) const captureConfig = $derived.by(getCaptureConfig) @@ -135,6 +143,10 @@ edit = false dirtyPath = false enabled = defaultValues?.enabled ?? false + error_handler_path = defaultValues?.error_handler_path ?? undefined + error_handler_args = defaultValues?.error_handler_args ?? {} + retry = defaultValues?.retry ?? undefined + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } finally { initialConfig = structuredClone($state.snapshot(getSaveCfg())) clearTimeout(loadingTimeout) @@ -155,6 +167,10 @@ enabled = cfg?.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 + error_handler_args = cfg?.error_handler_args ?? {} + retry = cfg?.retry + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } catch (error) { sendUserToast(`Could not load SQS trigger config: ${error.body}`, true) } @@ -186,7 +202,10 @@ queue_url, message_attributes, aws_auth_resource_type, - enabled + enabled, + error_handler_path, + error_handler_args, + retry } } @@ -371,6 +390,28 @@ headless={true} showTestingBadge={isEditor} /> + +
+
+
+ + Error Handler + Retries + +
+ +
+
+
+
{/if} {/snippet} diff --git a/frontend/src/lib/components/triggers/sqs/utils.ts b/frontend/src/lib/components/triggers/sqs/utils.ts index 7ef1afa60b..0ae7fb796c 100644 --- a/frontend/src/lib/components/triggers/sqs/utils.ts +++ b/frontend/src/lib/components/triggers/sqs/utils.ts @@ -1,4 +1,4 @@ -import { SqsTriggerService } from '$lib/gen' +import { SqsTriggerService, type EditSqsTrigger } from '$lib/gen' import { sendUserToast } from '$lib/toast' import { get, type Writable } from 'svelte/store' @@ -9,7 +9,14 @@ export async function saveSqsTriggerFromCfg( workspace: string, usedTriggerKinds: Writable ): Promise { - const requestBody = { + const errorHandlerAndRetries = !cfg.is_flow + ? { + error_handler_path: cfg.error_handler_path, + error_handler_args: cfg.error_handler_path ? cfg.error_handler_args : undefined, + retry: cfg.retry + } + : {} + const requestBody: EditSqsTrigger = { path: cfg.path, script_path: cfg.script_path, is_flow: cfg.is_flow, @@ -17,7 +24,8 @@ 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 + enabled: cfg.enabled, + ...errorHandlerAndRetries } try { if (edit) { diff --git a/frontend/src/lib/components/triggers/utils.ts b/frontend/src/lib/components/triggers/utils.ts index 62aeba7acd..6e2a866b48 100644 --- a/frontend/src/lib/components/triggers/utils.ts +++ b/frontend/src/lib/components/triggers/utils.ts @@ -542,3 +542,16 @@ export function filterDraftTriggers( return newSavedValue } + +export function getHandlerType(scriptPath: string): 'custom' | 'slack' | 'teams' { + const handlerMap = { + teams: '/workspace-or-schedule-error-handler-teams', + slack: '/workspace-or-schedule-error-handler-slack' + } + for (const [type, suffix] of Object.entries(handlerMap)) { + if (scriptPath.startsWith('hub/') && scriptPath.endsWith(suffix)) { + return type as 'slack' | 'teams' + } + } + return 'custom' +} diff --git a/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte index 8ece9a99fa..0fce91b6f8 100644 --- a/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte @@ -12,7 +12,8 @@ type Flow, type Script, type ScriptArgs, - type WebsocketTriggerInitialMessage + type WebsocketTriggerInitialMessage, + type Retry } from '$lib/gen' import { usedTriggerKinds, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptySchema, emptyString, sendUserToast } from '$lib/utils' @@ -28,7 +29,10 @@ import TriggerEditorToolbar from '../TriggerEditorToolbar.svelte' import { saveWebsocketTriggerFromCfg } from './utils' - import { handleConfigChange, type Trigger } from '../utils' + import { getHandlerType, handleConfigChange, type Trigger } from '../utils' + import Tabs from '$lib/components/common/tabs/Tabs.svelte' + import Tab from '$lib/components/common/tabs/Tab.svelte' + import TriggerRetriesAndErrorHandler from '../TriggerRetriesAndErrorHandler.svelte' interface Props { useDrawer?: boolean @@ -94,6 +98,11 @@ let initialConfig: Record | undefined = undefined let deploymentLoading = $state(false) let isValid = $state(false) + let optionTabSelected: 'error_handler' | 'retries' = $state('error_handler') + let errorHandlerSelected: 'slack' | 'teams' | 'custom' = $state('slack') + let error_handler_path: string | undefined = $state() + let error_handler_args: Record = $state({}) + let retry: Retry | undefined = $state() const websocketCfg = $derived.by(getSaveCfg) const captureConfig = $derived.by(isEditor ? getCaptureConfig : () => ({})) @@ -173,6 +182,10 @@ url_runnable_args = defaultValues?.url_runnable_args ?? {} dirtyPath = false can_return_message = false + error_handler_path = defaultValues?.error_handler_path ?? undefined + error_handler_args = defaultValues?.error_handler_args ?? {} + retry = defaultValues?.retry ?? undefined + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } finally { clearTimeout(loadingTimeout) drawerLoading = false @@ -192,6 +205,10 @@ url_runnable_args = cfg?.url_runnable_args can_return_message = cfg?.can_return_message can_write = canWrite(path, cfg?.extra_perms, $userStore) + error_handler_path = cfg?.error_handler_path + error_handler_args = cfg?.error_handler_args ?? {} + retry = cfg?.retry + errorHandlerSelected = getHandlerType(error_handler_path ?? '') } function getSaveCfg() { @@ -205,7 +222,10 @@ initial_messages, url_runnable_args, can_return_message, - enabled + enabled, + error_handler_path, + error_handler_args, + retry } } @@ -677,6 +697,28 @@
+ +
+
+
+ + Error Handler + Retries + +
+ +
+
+
+
{/if} {/snippet} diff --git a/frontend/src/lib/components/triggers/websocket/utils.ts b/frontend/src/lib/components/triggers/websocket/utils.ts index c112cb0f1a..edea9611eb 100644 --- a/frontend/src/lib/components/triggers/websocket/utils.ts +++ b/frontend/src/lib/components/triggers/websocket/utils.ts @@ -1,4 +1,4 @@ -import { WebsocketTriggerService } from '$lib/gen' +import { WebsocketTriggerService, type EditWebsocketTrigger } from '$lib/gen' import { sendUserToast } from '$lib/toast' import type { Writable } from 'svelte/store' import { get } from 'svelte/store' @@ -10,7 +10,16 @@ export async function saveWebsocketTriggerFromCfg( workspace: string, usedTriggerKinds: Writable ): Promise { - const requestBody = { + const errorHandlerAndRetries = !triggerCfg.is_flow + ? { + error_handler_path: triggerCfg.error_handler_path, + error_handler_args: triggerCfg.error_handler_path + ? triggerCfg.error_handler_args + : undefined, + retry: triggerCfg.retry + } + : {} + const requestBody: EditWebsocketTrigger = { path: triggerCfg.path, script_path: triggerCfg.script_path, is_flow: triggerCfg.is_flow, @@ -18,7 +27,8 @@ export async function saveWebsocketTriggerFromCfg( filters: triggerCfg.filters, initial_messages: triggerCfg.initial_messages, url_runnable_args: triggerCfg.url_runnable_args, - can_return_message: triggerCfg.can_return_message + can_return_message: triggerCfg.can_return_message, + ...errorHandlerAndRetries } try { if (edit) { diff --git a/frontend/src/lib/components/workspaceSettings/AISettings.svelte b/frontend/src/lib/components/workspaceSettings/AISettings.svelte index dbeae226b2..282b8cc0c2 100644 --- a/frontend/src/lib/components/workspaceSettings/AISettings.svelte +++ b/frontend/src/lib/components/workspaceSettings/AISettings.svelte @@ -197,8 +197,6 @@ {#if aiProviders[provider]}
- - {#snippet text()} -
Connected to sqs{!server_id ? ' (shutting down...)' : ''}
+
Connected to SQS{!server_id ? ' (shutting down...)' : ''}
{/snippet} {/if} diff --git a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte index 073f1281e0..840eec52c0 100644 --- a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte @@ -60,6 +60,7 @@ import ConnectionSection from '$lib/components/ConnectionSection.svelte' import AISettings from '$lib/components/workspaceSettings/AISettings.svelte' import StorageSettings from '$lib/components/workspaceSettings/StorageSettings.svelte' + import { untrack } from 'svelte' type GitSyncTypeMap = { scripts: boolean @@ -88,36 +89,54 @@ | 'user' | 'group' | 'trigger' - let slackInitialPath: string - let slackScriptPath: string - let teamsInitialPath: string - let teamsScriptPath: string - let slack_team_name: string | undefined - let teams_team_id: string | undefined - let teams_team_name: string | undefined - let itemKind: 'flow' | 'script' = 'flow' - let plan: string | undefined = undefined - let customer_id: string | undefined = undefined - let webhook: string | undefined = undefined - let workspaceToDeployTo: string | undefined = undefined - let errorHandlerSelected: 'custom' | 'slack' | 'teams' = 'slack' - let errorHandlerInitialScriptPath: string - let errorHandlerScriptPath: string - let errorHandlerItemKind: 'flow' | 'script' = 'script' - let errorHandlerExtraArgs: Record = {} - let errorHandlerMutedOnCancel: boolean | undefined = undefined - let criticalAlertUIMuted: boolean | undefined = undefined - let initialCriticalAlertUIMuted: boolean | undefined = undefined + let slackInitialPath: string = $state('') + let slackScriptPath: string = $state('') + let teamsInitialPath: string = $state('') + let teamsScriptPath: string = $state('') + let slack_team_name: string | undefined = $state() + let teams_team_id: string | undefined = $state() + let teams_team_name: string | undefined = $state() + let itemKind: 'flow' | 'script' = $state('flow') + let plan: string | undefined = $state(undefined) + let customer_id: string | undefined = $state(undefined) + let webhook: string | undefined = $state(undefined) + let workspaceToDeployTo: string | undefined = $state(undefined) + let errorHandlerSelected: 'custom' | 'slack' | 'teams' = $state('slack') + let errorHandlerScriptPath: string | undefined = $state(undefined) + let errorHandlerItemKind: 'flow' | 'script' = $state('script') + let errorHandlerExtraArgs: Record = $state({}) + let errorHandlerMutedOnCancel: boolean | undefined = $state(undefined) + let criticalAlertUIMuted: boolean | undefined = $state(undefined) + let initialCriticalAlertUIMuted: boolean | undefined = $state(undefined) - let aiProviders: Exclude = {} - let codeCompletionModel: string | undefined = undefined - let defaultModel: string | undefined = undefined + let aiProviders: Exclude = $state({}) + let codeCompletionModel: string | undefined = $state(undefined) + let defaultModel: string | undefined = $state(undefined) - let s3ResourceSettings: S3ResourceSettings = { + let s3ResourceSettings: S3ResourceSettings = $state({ resourceType: 's3', resourcePath: undefined, publicResource: undefined, secondaryStorage: undefined + }) + + const DEFAULT_GIT_SYNC_SETTINGS = { + include_path: ['f/**'], + repositories: [], + include_type: { + scripts: true, + flows: true, + apps: true, + folders: true, + resourceTypes: false, + resources: false, + variables: false, + secrets: false, + schedules: false, + users: false, + groups: false, + triggers: false + } } let gitSyncSettings: { include_path: string[] @@ -129,18 +148,18 @@ group_by_folder: boolean }[] include_type: GitSyncTypeMap - } + } = $state(DEFAULT_GIT_SYNC_SETTINGS) let gitSyncTestJobs: { jobId: string | undefined status: 'running' | 'success' | 'failure' | undefined - }[] - let workspaceDefaultAppPath: string | undefined = undefined - let workspaceEncryptionKey: string | undefined = undefined - let editedWorkspaceEncryptionKey: string | undefined = undefined - let workspaceReencryptionInProgress: boolean = false + }[] = $state([]) + let workspaceDefaultAppPath: string | undefined = $state(undefined) + let workspaceEncryptionKey: string | undefined = $state(undefined) + let editedWorkspaceEncryptionKey: string | undefined = $state(undefined) + let workspaceReencryptionInProgress: boolean = $state(false) let encryptionKeyRegex = /^[a-zA-Z0-9]{64}$/ - let slack_tabs: 'slack_commands' | 'teams_commands' = 'slack_commands' - let tab = + let slack_tabs: 'slack_commands' | 'teams_commands' = $state('slack_commands') + let tab = $state( ($page.url.searchParams.get('tab') as | 'users' | 'slack' @@ -149,7 +168,8 @@ | 'webhook' | 'deploy_to' | 'error_handler') ?? 'users' - let usingOpenaiClientCredentialsOauth = false + ) + let usingOpenaiClientCredentialsOauth = $state(false) const latestGitSyncHubScript = hubPaths.gitSync @@ -357,7 +377,7 @@ ) } - let loadedSettings = false + let loadedSettings = $state(false) async function loadSettings(): Promise { const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! }) slack_team_name = settings.slack_name @@ -382,9 +402,10 @@ defaultModel = settings.ai_config?.default_model?.model codeCompletionModel = settings.ai_config?.code_completion_model?.model - errorHandlerItemKind = settings.error_handler?.split('/')[0] as 'flow' | 'script' + errorHandlerItemKind = settings.error_handler + ? (settings.error_handler.split('/')[0] as 'flow' | 'script') + : 'script' errorHandlerScriptPath = (settings.error_handler ?? '').split('/').slice(1).join('/') - errorHandlerInitialScriptPath = errorHandlerScriptPath errorHandlerMutedOnCancel = settings.error_handler_muted_on_cancel criticalAlertUIMuted = settings.mute_critical_alerts initialCriticalAlertUIMuted = settings.mute_critical_alerts @@ -454,24 +475,7 @@ } } } else { - gitSyncSettings = { - include_path: ['f/**'], - repositories: [], - include_type: { - scripts: true, - flows: true, - apps: true, - folders: true, - resourceTypes: false, - resources: false, - variables: false, - secrets: false, - schedules: false, - users: false, - groups: false, - triggers: false - } - } + gitSyncSettings = DEFAULT_GIT_SYNC_SETTINGS gitSyncTestJobs = [] } if (settings.deploy_ui != undefined && settings.deploy_ui != null) { @@ -501,28 +505,27 @@ loadedSettings = true } - let deployUiSettings: { - include_path: string[] - include_type: { - scripts: boolean - flows: boolean - apps: boolean - resources: boolean - variables: boolean - secrets: boolean - triggers: boolean - } - } + let deployUiSettings: + | { + include_path: string[] + include_type: { + scripts: boolean + flows: boolean + apps: boolean + resources: boolean + variables: boolean + secrets: boolean + triggers: boolean + } + } + | undefined = $state() - $: $workspaceStore && loadSettings() + $effect(() => { + $workspaceStore && untrack(() => loadSettings()) + }) async function editErrorHandler() { if (errorHandlerScriptPath) { - if (errorHandlerScriptPath !== undefined && isSlackHandler(errorHandlerScriptPath)) { - errorHandlerExtraArgs['slack'] = '$res:f/slack_bot/bot_token' - } else { - errorHandlerExtraArgs['slack'] = undefined - } await WorkspaceService.editErrorHandler({ workspace: $workspaceStore!, requestBody: { @@ -545,13 +548,6 @@ } } - function isSlackHandler(scriptPath: string) { - return ( - scriptPath.startsWith('hub/') && - scriptPath.endsWith('/workspace-or-schedule-error-handler-slack') - ) - } - async function runGitSyncTestJob(settingsIdx: number) { let gitSyncRepository = gitSyncSettings.repositories[settingsIdx] if (emptyString(gitSyncRepository.script_path)) { @@ -611,13 +607,18 @@ }, 3000) } - function updateFromSearchTab(searchTab: string | null) { - if (searchTab && searchTab !== tab) { + function updateFromSearchTab(searchTab: string | null, currentTab: string) { + if (searchTab && searchTab !== currentTab) { tab = searchTab as typeof tab } } - $: updateFromSearchTab($page.url.searchParams.get('tab')) + $effect(() => { + updateFromSearchTab( + $page.url.searchParams.get('tab'), + untrack(() => tab) + ) + }) @@ -974,14 +975,13 @@ isEditable={true} errorOrRecovery="error" showScriptHelpText={true} - customInitialScriptPath={errorHandlerInitialScriptPath} bind:handlerSelected={errorHandlerSelected} bind:handlerPath={errorHandlerScriptPath} customScriptTemplate="/scripts/add?hub=hub%2F9083%2Fwindmill%2Fworkspace_error_handler_template" bind:customHandlerKind={errorHandlerItemKind} bind:handlerExtraArgs={errorHandlerExtraArgs} > - + {#snippet customTabTooltip()}
@@ -1002,7 +1002,7 @@
-
+ {/snippet}
@@ -1117,14 +1117,18 @@
By default everything in folders will be synced. - {#each gitSyncSettings.include_path ?? [] as gitSyncRegexpPath, idx} + {#each gitSyncSettings.include_path ?? [] as _, idx}
- +