mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 00:01:55 +00:00
feat: add workspace setting to disable error handler for u/ scripts/flows (#7634)
* feat: add workspace setting to disable error handler for u/ scripts/flows Add a new workspace setting `error_handler_muted_on_user_path` that allows disabling the error handler for jobs related to scripts/flows that start with the "u/" prefix (user scripts/flows). Changes: - Add migration to add `error_handler_muted_on_user_path` column to workspace_settings - Update WorkspaceSettings struct and get_settings query - Update EditErrorHandler struct and edit_error_handler API endpoint - Update error handler cache to include the new setting - Add skip logic in send_error_to_workspace_handler for u/ paths - Add toggle in workspace settings UI The implementation uses the existing cached settings to avoid additional database queries, making it efficient. Closes #7633 Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> * all * update ee private * combine migrations * sqlx * fix: migration fixes for auto_invite JSONB column - Fix TEXT[] to JSONB conversion using to_jsonb() - Add GIN index on auto_invite for efficient ? operator queries - Add index cleanup to down migration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * sqlx * all * all * all --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: windmill-internal-app[bot] <windmill-internal-app[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET auto_invite = COALESCE(auto_invite, '{}'::jsonb)\n || jsonb_build_object('instance_groups', $2::jsonb, 'instance_groups_roles', $3::jsonb)\n WHERE workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Jsonb",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "03caaec9f41be87eac32e163d7a829e58ae993932e9bbad5aebab4770cc44d61"
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT success_handler_extra_args FROM workspace_settings WHERE workspace_id = 'test-workspace'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "success_handler_extra_args",
|
||||
"type_info": "Json"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "085bbcfcebc9e56984295046c48722f3510548eaf525c499ad0e24cdf8332b22"
|
||||
}
|
||||
+32
-74
@@ -30,163 +30,128 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "auto_invite_domain",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "auto_invite_operator",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "customer_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 6,
|
||||
"name": "plan",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 7,
|
||||
"name": "webhook",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 8,
|
||||
"name": "deploy_to",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "error_handler",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 9,
|
||||
"name": "ai_config",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "error_handler_extra_args",
|
||||
"type_info": "Json"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "error_handler_muted_on_cancel",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 10,
|
||||
"name": "large_file_storage",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 11,
|
||||
"name": "git_sync",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 12,
|
||||
"name": "default_app",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "auto_add",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 13,
|
||||
"name": "default_scripts",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 14,
|
||||
"name": "deploy_ui",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 15,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 16,
|
||||
"name": "color",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 17,
|
||||
"name": "operator_settings",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 18,
|
||||
"name": "teams_command_script",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 19,
|
||||
"name": "teams_team_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 20,
|
||||
"name": "teams_team_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 21,
|
||||
"name": "git_app_installations",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 22,
|
||||
"name": "ducklake",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"name": "auto_add_instance_groups",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"name": "auto_add_instance_groups_roles",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"ordinal": 23,
|
||||
"name": "slack_oauth_client_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"ordinal": 24,
|
||||
"name": "slack_oauth_client_secret",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"ordinal": 25,
|
||||
"name": "datatable",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"ordinal": 26,
|
||||
"name": "teams_team_guid",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"name": "success_handler",
|
||||
"type_info": "Text"
|
||||
"ordinal": 27,
|
||||
"name": "auto_invite",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"name": "success_handler_extra_args",
|
||||
"type_info": "Json"
|
||||
"ordinal": 28,
|
||||
"name": "error_handler",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"name": "success_handler",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -209,12 +174,6 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -230,7 +189,6 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET auto_invite = COALESCE(auto_invite, '{}'::jsonb) - 'instance_groups' - 'instance_groups_roles'\n WHERE workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0984e469bbd5f97cf9a6cac4bf5700bb6b1a99f6abfb3390a54a8a42c6768903"
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET auto_invite_domain = NULL, auto_invite_operator = NULL, auto_add = NULL WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "0c9ad812013ff476a79ca8d6bb8b7a73d9492e07680732af9af09e223ade1f37"
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT success_handler FROM workspace_settings WHERE workspace_id = 'test-workspace'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "success_handler",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "10a40902f5aba38aea23f2b0776b54752362c9cc7deb179f75c7f1795866d13a"
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT COALESCE(auto_add_instance_groups, '{}') FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "coalesce",
|
||||
"type_info": "TextArray"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "18e550f4ec23d465632449b88c4b25931f145f771b93828e8e6dfcc1f906443d"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET auto_invite = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "255ba68caa78d0e814ea817693a319f769907025f9c7d1150d45e8ecb1bff4ab"
|
||||
}
|
||||
+72
-114
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT * FROM workspace_settings WHERE workspace_id = $1",
|
||||
"query": "\n SELECT\n workspace_id,\n slack_team_id,\n teams_team_id,\n teams_team_name,\n teams_team_guid,\n slack_name,\n slack_command_script,\n teams_command_script,\n slack_email,\n slack_oauth_client_id,\n slack_oauth_client_secret,\n customer_id,\n plan,\n webhook,\n deploy_to,\n ai_config,\n large_file_storage,\n datatable,\n ducklake,\n git_sync,\n deploy_ui,\n default_app,\n default_scripts,\n mute_critical_alerts,\n color,\n operator_settings,\n git_app_installations,\n auto_invite,\n error_handler,\n success_handler\n FROM\n workspace_settings\n WHERE\n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -15,92 +15,92 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "teams_team_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "teams_team_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "teams_team_guid",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "slack_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 6,
|
||||
"name": "slack_command_script",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 7,
|
||||
"name": "teams_command_script",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "slack_email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "auto_invite_domain",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "auto_invite_operator",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "customer_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "plan",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "webhook",
|
||||
"type_info": "Text"
|
||||
"name": "slack_oauth_client_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "deploy_to",
|
||||
"name": "slack_oauth_client_secret",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "error_handler",
|
||||
"name": "customer_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "plan",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "webhook",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "deploy_to",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "ai_config",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "error_handler_extra_args",
|
||||
"type_info": "Json"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "error_handler_muted_on_cancel",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "large_file_storage",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "git_sync",
|
||||
"ordinal": 17,
|
||||
"name": "datatable",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "default_app",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "auto_add",
|
||||
"type_info": "Bool"
|
||||
"name": "ducklake",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "default_scripts",
|
||||
"name": "git_sync",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
@@ -110,83 +110,48 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"name": "default_app",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "default_scripts",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 24,
|
||||
"name": "color",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 25,
|
||||
"name": "operator_settings",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"name": "teams_command_script",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"name": "teams_team_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"name": "teams_team_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"name": "git_app_installations",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"name": "auto_invite",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"name": "ducklake",
|
||||
"name": "error_handler",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"name": "auto_add_instance_groups",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"name": "auto_add_instance_groups_roles",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"name": "slack_oauth_client_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"name": "slack_oauth_client_secret",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"name": "datatable",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"name": "teams_team_guid",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"name": "success_handler",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"name": "success_handler_extra_args",
|
||||
"type_info": "Json"
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -199,6 +164,10 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
@@ -209,11 +178,6 @@
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -225,14 +189,8 @@
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "1730f39fd1793d45fbb41b21389c61296a3ff7489ae12f52a19f9543173ac597"
|
||||
"hash": "289919809e16aee33c81951b05a4795de710421bcd3e4c06588e56092677bd05"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET success_handler = '{\"path\": \"script/f/test/success_handler\", \"extra_args\": {\"key\": \"value\"}}'::jsonb\n WHERE workspace_id = 'test-workspace'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "28f3dfb70aed943ad3d64da608168c027c645afb73dfc2360245b68ba593cab4"
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET auto_add_instance_groups = NULL, auto_add_instance_groups_roles = NULL WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "31db242523c534b2abbb5f4985f56dd4493d19611ed988d694d9f4289dc29c48"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT error_handler->>'path' FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "397f5bef6454770910189db812ad549d50af537c43b4b6ded63b6e83070243be"
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET auto_add_instance_groups = $2, auto_add_instance_groups_roles = $3 WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"TextArray",
|
||||
"Jsonb"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "404233e74aaafd987879c6c87d1fd80928e6fade09e60a9a2d35121c81885cca"
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n workspace_settings\n SET\n success_handler = NULL,\n success_handler_extra_args = NULL\n WHERE\n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4977b4027861ca234e95559a8b550bc8849f7ed60a471d46f3be3d39664a48ca"
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET\n auto_add_instance_groups = array_remove(auto_add_instance_groups, $1),\n auto_add_instance_groups_roles = auto_add_instance_groups_roles - $1\n WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4a446b97cf813226d25ca40cefca2999cdcf938f817327e9168b3edb0fce7fb7"
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "5a219a2532517869578c4504ff3153c43903f929ae5d62fbba12610f89c36d55"
|
||||
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n workspace_settings\n SET\n error_handler = NULL,\n error_handler_extra_args = NULL,\n error_handler_muted_on_cancel = false\n WHERE\n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "5ad2c883d26f39f3c141806428a329951ef19a1cb3e1b429fcd1abe0e2db45b5"
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id,\n (auto_invite->>'operator')::boolean as \"is_operator: bool\",\n (auto_invite->>'mode') = 'add' as \"is_auto_add: bool\"\n FROM workspace_settings ws\n WHERE (auto_invite->>'domain' = $1 OR auto_invite->>'domain' = '*')\n AND (auto_invite->>'enabled')::boolean = true\n AND NOT EXISTS (SELECT 1 FROM usr WHERE workspace_id = ws.workspace_id AND email = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "is_operator: bool",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "is_auto_add: bool",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "5dafddc81419883beda4cc9acb3679b3d7c97f6c137b189d552182899782e99f"
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n auto_invite,\n webhook,\n deploy_to,\n error_handler,\n ai_config,\n large_file_storage,\n git_sync,\n default_app,\n default_scripts,\n workspace.name as \"name!\",\n mute_critical_alerts,\n color,\n operator_settings\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "auto_invite",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "webhook",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "deploy_to",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "error_handler",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "ai_config",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "large_file_storage",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "git_sync",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "default_app",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "default_scripts",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "name!",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "color",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "operator_settings",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "631ddef13d95a171b2807638485177e9d543b8e59a15f7e35c9b5d06dddd3142"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET success_handler = NULL WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "63ad0e3cca16987dba0be7b3efb522c6e652cfa86f10642ade6aaadd6174fb52"
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET auto_invite_domain = $1, auto_invite_operator = $2, auto_add = $4 WHERE workspace_id = $3",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Bool",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "64c5ca0bd4a2c42d6f5415fae1de21295673c45e4caae1bdbb47517d4608dc8d"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET success_handler = '{\"path\": \"script/f/test/success_handler\"}'::jsonb\n WHERE workspace_id = 'test-workspace'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "65016ea993fb40d4ab6b0a6f15c2082e8dfbaada01243bae07065c089bc42fa9"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET success_handler = 'script/f/test/success_handler',\n success_handler_extra_args = '{\"key\": \"value\"}'::json\n WHERE workspace_id = 'test-workspace'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "6eaa0af9c5e0a8d64c3202753c9c3cef39361b0987a22b7d2b2346652f81177e"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT auto_invite->'instance_groups' FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "75e740531bf794a8568350348253612cfbdecb9fb9cf768f431d4dbd4cd56cfb"
|
||||
}
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n workspace_id,\n auto_add_instance_groups_roles\n FROM workspace_settings\n WHERE\n auto_add_instance_groups IS NOT NULL\n AND $1 = ANY(auto_add_instance_groups)\n ",
|
||||
"query": "\n SELECT\n workspace_id,\n auto_invite->'instance_groups_roles' as instance_groups_roles\n FROM workspace_settings\n WHERE\n auto_invite->'instance_groups' IS NOT NULL\n AND auto_invite->'instance_groups' ? $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "auto_add_instance_groups_roles",
|
||||
"name": "instance_groups_roles",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
@@ -21,8 +21,8 @@
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "3ebc91867be0a4830ef4dbbe833aed348f78e3cb68fb1f3c855a491bdcda5017"
|
||||
"hash": "7e01ef5799168c0fc2779d42ce352827e2fda6711c0a1b104ca6435ddb14b47d"
|
||||
}
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n eig.igroup as group_name,\n ws.auto_add_instance_groups_roles\n FROM email_to_igroup eig\n INNER JOIN workspace_settings ws ON ws.workspace_id = $1\n WHERE eig.email = $2\n AND eig.igroup = ANY(ws.auto_add_instance_groups)\n ",
|
||||
"query": "\n SELECT\n eig.igroup as group_name,\n ws.auto_invite->'instance_groups_roles' as instance_groups_roles\n FROM email_to_igroup eig\n INNER JOIN workspace_settings ws ON ws.workspace_id = $1\n WHERE eig.email = $2\n AND ws.auto_invite->'instance_groups' ? eig.igroup\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "auto_add_instance_groups_roles",
|
||||
"name": "instance_groups_roles",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
@@ -22,8 +22,8 @@
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "1ff185d8b8b897a72180cd0002e0f7e9858eef249577ce23969522793c6b5608"
|
||||
"hash": "7e5913aa98624836a9bd7b5cc5cb10cc79a1249f48de45ea1c15235d4b05576b"
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n error_handler->>'path'\n FROM\n workspace_settings\n WHERE\n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "825ee06c5f88436ff7bb45e389552836ae8496d75d7cf7fee497b14943b1b6f6"
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT success_handler->'extra_args' FROM workspace_settings WHERE workspace_id = 'test-workspace'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "87044e1466cd114cbef336620e96dc29f3f930149fedc240d44c614dfc794ff7"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n ig.name as group_name,\n ws.workspace_id,\n w.name as workspace_name,\n ws.auto_add_instance_groups_roles->ig.name as role\n FROM instance_group ig\n INNER JOIN workspace_settings ws ON ws.auto_add_instance_groups IS NOT NULL\n AND ig.name = ANY(ws.auto_add_instance_groups)\n INNER JOIN workspace w ON w.id = ws.workspace_id AND w.deleted = false\n ORDER BY ig.name, ws.workspace_id\n ",
|
||||
"query": "\n SELECT\n ig.name as group_name,\n ws.workspace_id,\n w.name as workspace_name,\n ws.auto_invite->'instance_groups_roles'->ig.name as role\n FROM instance_group ig\n INNER JOIN workspace_settings ws ON ws.auto_invite->'instance_groups' IS NOT NULL\n AND ws.auto_invite->'instance_groups' ? ig.name\n INNER JOIN workspace w ON w.id = ws.workspace_id AND w.deleted = false\n ORDER BY ig.name, ws.workspace_id\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -34,5 +34,5 @@
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "1e6c125c884002a1565b11a2c308ce89f9726d21bbb3cc712ad0b6450cbb44e6"
|
||||
"hash": "87cb0e9503776dbaea70652612cf43ecedc55b2e6319aefcf9797e7dfad0812c"
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n workspace_settings\n SET\n success_handler = $1,\n success_handler_extra_args = $2\n WHERE\n workspace_id = $3\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Json",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "9044c71dc8aab7f5d17bf27c4702e67f3eee46d7dbccb09cadeaafa0534952d6"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET success_handler = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "908ab9eda5ed0f3e76ea5a0e8a1714942358e2620f3bdfb080504ec1da332dbd"
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE\n workspace_settings\n SET\n error_handler = $1,\n error_handler_extra_args = $2,\n error_handler_muted_on_cancel = $3\n WHERE\n workspace_id = $4\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Json",
|
||||
"Bool",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "97b6e5779661343587c297b275df9e75fd22924ad2dbe124bb361f3e7fd8bfe0"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET success_handler = 'flow/f/test/success_flow'\n WHERE workspace_id = 'test-workspace'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a2bc410e1066ddc89ca4557ca0e29339e41cbc7a7ef6ad97b3f0eb93de04c359"
|
||||
}
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, auto_add_instance_groups_roles FROM workspace_settings WHERE $1 = ANY(COALESCE(auto_add_instance_groups, '{}'))",
|
||||
"query": "\n SELECT workspace_id, auto_invite->'instance_groups_roles' as instance_groups_roles\n FROM workspace_settings\n WHERE auto_invite->'instance_groups' ? $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "auto_add_instance_groups_roles",
|
||||
"name": "instance_groups_roles",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
@@ -21,8 +21,8 @@
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "53210d19693ba46d97352b0ee321af59db453243c0cd17928ee942723d9cdc5c"
|
||||
"hash": "acc9be5aa111985db14025ac0e0845031f65f214bbf1c599c3ee1d1e7ac0b50f"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET error_handler = NULL WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c0635f65d561c1a6b183b8fea45320482375d0bb5e617a9dd236a631a7b9ff05"
|
||||
}
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT\n -- slack_team_id,\n -- slack_name,\n -- slack_command_script,\n -- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,\n auto_invite_domain IS NOT NULL AS \"auto_invite_enabled!\",\n CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS \"auto_invite_as!\",\n CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS \"auto_invite_mode!\",\n webhook,\n deploy_to,\n error_handler,\n ai_config,\n error_handler_extra_args,\n error_handler_muted_on_cancel,\n large_file_storage,\n git_sync,\n default_app,\n default_scripts,\n workspace.name,\n mute_critical_alerts,\n color,\n operator_settings\n FROM workspace_settings\n LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id\n WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "auto_invite_enabled!",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "auto_invite_as!",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "auto_invite_mode!",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "webhook",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "deploy_to",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "error_handler",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "ai_config",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "error_handler_extra_args",
|
||||
"type_info": "Json"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "error_handler_muted_on_cancel",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "large_file_storage",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "git_sync",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "default_app",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "default_scripts",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "color",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "operator_settings",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "c12a0b0d423577afbb2772f971bffe2635785b53d31d0ceb3d72226a0c316e8a"
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET success_handler = '{\"path\": \"flow/f/test/success_flow\"}'::jsonb\n WHERE workspace_id = 'test-workspace'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c2f271bcea1f11b2979152cc743559236fd875192b7b7f4824c63675fdc6a551"
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id FROM workspace_settings WHERE auto_add_instance_groups IS NOT NULL AND $1 = ANY(auto_add_instance_groups)",
|
||||
"query": "SELECT workspace_id FROM workspace_settings WHERE auto_invite->'instance_groups' IS NOT NULL AND auto_invite->'instance_groups' ? $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -18,5 +18,5 @@
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "711c72a72298bb656882b35071e910dab721cda070f9c39bba3729bdc1467496"
|
||||
"hash": "c7c0b7f760f9616ec4a18a8916226b65990f5055c1bc25eedefd303be75f553f"
|
||||
}
|
||||
-238
@@ -1,238 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n workspace_id,\n slack_team_id,\n teams_team_id,\n teams_team_name,\n teams_team_guid,\n slack_name,\n slack_command_script,\n teams_command_script,\n slack_email,\n slack_oauth_client_id,\n slack_oauth_client_secret,\n auto_invite_domain,\n auto_invite_operator,\n auto_add,\n customer_id,\n plan,\n webhook,\n deploy_to,\n ai_config,\n error_handler,\n error_handler_extra_args,\n error_handler_muted_on_cancel,\n success_handler,\n success_handler_extra_args,\n large_file_storage,\n datatable,\n ducklake,\n git_sync,\n deploy_ui,\n default_app,\n default_scripts,\n mute_critical_alerts,\n color,\n operator_settings,\n git_app_installations,\n auto_add_instance_groups,\n auto_add_instance_groups_roles\n FROM\n workspace_settings\n WHERE\n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "slack_team_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "teams_team_id",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "teams_team_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "teams_team_guid",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "slack_name",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "slack_command_script",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "teams_command_script",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "slack_email",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "slack_oauth_client_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"name": "slack_oauth_client_secret",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"name": "auto_invite_domain",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"name": "auto_invite_operator",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"name": "auto_add",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"name": "customer_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"name": "plan",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"name": "webhook",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"name": "deploy_to",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"name": "ai_config",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"name": "error_handler",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"name": "error_handler_extra_args",
|
||||
"type_info": "Json"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"name": "error_handler_muted_on_cancel",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"name": "success_handler",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"name": "success_handler_extra_args",
|
||||
"type_info": "Json"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"name": "large_file_storage",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"name": "datatable",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"name": "ducklake",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"name": "git_sync",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"name": "deploy_ui",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"name": "default_app",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"name": "default_scripts",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"name": "mute_critical_alerts",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"name": "color",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"name": "operator_settings",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"name": "git_app_installations",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"name": "auto_add_instance_groups",
|
||||
"type_info": "TextArray"
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"name": "auto_add_instance_groups_roles",
|
||||
"type_info": "Jsonb"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "d0dfd217a4f53aaee7b83d5b5267f74e5ad617830dd835a45e17fd378bcbeada"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET\n auto_invite = jsonb_set(\n jsonb_set(\n COALESCE(auto_invite, '{}'::jsonb),\n '{instance_groups}',\n (SELECT COALESCE(jsonb_agg(elem), '[]'::jsonb) FROM jsonb_array_elements(COALESCE(auto_invite->'instance_groups', '[]'::jsonb)) elem WHERE elem #>> '{}' != $1)\n ),\n '{instance_groups_roles}',\n COALESCE(auto_invite->'instance_groups_roles', '{}'::jsonb) - $1\n )\n WHERE auto_invite->'instance_groups' IS NOT NULL AND auto_invite->'instance_groups' ? $1",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "dab323eda1fcaff0435e98d77e544ed5d63dd6023b1dab77d5f188b299f03b9d"
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n error_handler \n FROM \n workspace_settings \n WHERE \n workspace_id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "error_handler",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "e03cfb65fc9b19c7e68e43d14a6e5041db783f1e6d545533a2d95f54e616b9fb"
|
||||
}
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE workspace_settings\n SET success_handler = 'script/f/test/success_handler'\n WHERE workspace_id = 'test-workspace'\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e3f2459d9ac3534531d5724bb2d67be6f33f8ca02ef4768bccd32a33a70a4300"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE workspace_settings SET error_handler = $1 WHERE workspace_id = $2",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Jsonb",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "e844b9ee75a1b5438ffe743185fc78f649db89c78b2b8cd7e778bf18a6b41e67"
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT success_handler->>'path' FROM workspace_settings WHERE workspace_id = 'test-workspace'",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "?column?",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "e9808cc152e1ce4717260ebb42f203ca16321a4550073258734a6208b83c00c2"
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT error_handler FROM workspace_settings WHERE workspace_id = $1",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "error_handler",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "f8b34e09453d51d3df5be20652938a890ee353fec64ee73c312a3352da7f7515"
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT workspace_id, auto_invite_operator, auto_add FROM workspace_settings ws WHERE (auto_invite_domain = $1 OR auto_invite_domain = '*') AND NOT EXISTS (SELECT 1 FROM usr WHERE workspace_id = ws.workspace_id AND email = $2)",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "workspace_id",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "auto_invite_operator",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "auto_add",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "fc8fa909ef7a15c7ad30dd938be3c13864e855219c7f366c86f0605ec25d1538"
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
1549849fadc4e5634334a384bfe52343eb1e93f0
|
||||
13825341581c1b9eb7a415f0ae25dbc9ba84059f
|
||||
@@ -0,0 +1,46 @@
|
||||
-- Step 0: Drop the GIN index on auto_invite
|
||||
DROP INDEX IF EXISTS idx_workspace_settings_auto_invite;
|
||||
|
||||
-- Step 1: Rename current JSONB columns to temporary names to avoid collision
|
||||
ALTER TABLE workspace_settings
|
||||
RENAME COLUMN error_handler TO error_handler_jsonb;
|
||||
ALTER TABLE workspace_settings
|
||||
RENAME COLUMN success_handler TO success_handler_jsonb;
|
||||
|
||||
-- Step 2: Restore old columns with original names and types
|
||||
ALTER TABLE workspace_settings
|
||||
ADD COLUMN IF NOT EXISTS auto_invite_domain VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS auto_invite_operator BOOLEAN,
|
||||
ADD COLUMN IF NOT EXISTS auto_add BOOLEAN,
|
||||
ADD COLUMN IF NOT EXISTS auto_add_instance_groups TEXT[],
|
||||
ADD COLUMN IF NOT EXISTS auto_add_instance_groups_roles JSONB,
|
||||
ADD COLUMN IF NOT EXISTS error_handler VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS error_handler_extra_args JSON,
|
||||
ADD COLUMN IF NOT EXISTS error_handler_muted_on_cancel BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS error_handler_muted_on_user_path BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS success_handler TEXT,
|
||||
ADD COLUMN IF NOT EXISTS success_handler_extra_args JSON;
|
||||
|
||||
-- Step 3: Migrate data back from JSONB columns to flat columns
|
||||
UPDATE workspace_settings SET
|
||||
auto_invite_domain = auto_invite->>'domain',
|
||||
auto_invite_operator = COALESCE((auto_invite->>'operator')::boolean, false),
|
||||
auto_add = (auto_invite->>'mode') = 'add',
|
||||
auto_add_instance_groups = CASE
|
||||
WHEN auto_invite->'instance_groups' IS NOT NULL AND auto_invite->'instance_groups' != 'null'::jsonb
|
||||
THEN ARRAY(SELECT jsonb_array_elements_text(auto_invite->'instance_groups'))
|
||||
ELSE NULL
|
||||
END,
|
||||
auto_add_instance_groups_roles = auto_invite->'instance_groups_roles',
|
||||
error_handler = error_handler_jsonb->>'path',
|
||||
error_handler_extra_args = (error_handler_jsonb->'extra_args')::json,
|
||||
error_handler_muted_on_cancel = COALESCE((error_handler_jsonb->>'muted_on_cancel')::boolean, false),
|
||||
error_handler_muted_on_user_path = COALESCE((error_handler_jsonb->>'muted_on_user_path')::boolean, false),
|
||||
success_handler = success_handler_jsonb->>'path',
|
||||
success_handler_extra_args = (success_handler_jsonb->'extra_args')::json;
|
||||
|
||||
-- Step 4: Drop the JSONB columns
|
||||
ALTER TABLE workspace_settings
|
||||
DROP COLUMN IF EXISTS auto_invite,
|
||||
DROP COLUMN IF EXISTS error_handler_jsonb,
|
||||
DROP COLUMN IF EXISTS success_handler_jsonb;
|
||||
@@ -0,0 +1,59 @@
|
||||
-- Step 1: Add error_handler_muted_on_user_path column if it doesn't exist (was previously a separate migration)
|
||||
ALTER TABLE workspace_settings ADD COLUMN IF NOT EXISTS error_handler_muted_on_user_path BOOL NOT NULL DEFAULT false;
|
||||
|
||||
-- Step 2: Add new JSONB columns with temporary names (to avoid collision with existing TEXT columns)
|
||||
ALTER TABLE workspace_settings
|
||||
ADD COLUMN IF NOT EXISTS auto_invite JSONB,
|
||||
ADD COLUMN IF NOT EXISTS error_handler_new JSONB,
|
||||
ADD COLUMN IF NOT EXISTS success_handler_new JSONB;
|
||||
|
||||
-- Step 3: Migrate existing data to new columns (use jsonb_strip_nulls to omit null values)
|
||||
UPDATE workspace_settings SET
|
||||
auto_invite = jsonb_strip_nulls(jsonb_build_object(
|
||||
'enabled', auto_invite_domain IS NOT NULL,
|
||||
'domain', auto_invite_domain,
|
||||
'operator', COALESCE(auto_invite_operator, false),
|
||||
'mode', CASE WHEN auto_add THEN 'add' ELSE 'invite' END,
|
||||
'instance_groups', CASE WHEN auto_add_instance_groups IS NOT NULL THEN to_jsonb(auto_add_instance_groups) ELSE NULL END,
|
||||
'instance_groups_roles', auto_add_instance_groups_roles
|
||||
)),
|
||||
error_handler_new = CASE
|
||||
WHEN error_handler IS NOT NULL THEN jsonb_strip_nulls(jsonb_build_object(
|
||||
'path', error_handler,
|
||||
'extra_args', error_handler_extra_args::jsonb,
|
||||
'muted_on_cancel', error_handler_muted_on_cancel,
|
||||
'muted_on_user_path', error_handler_muted_on_user_path
|
||||
))
|
||||
ELSE NULL
|
||||
END,
|
||||
success_handler_new = CASE
|
||||
WHEN success_handler IS NOT NULL THEN jsonb_strip_nulls(jsonb_build_object(
|
||||
'path', success_handler,
|
||||
'extra_args', success_handler_extra_args::jsonb
|
||||
))
|
||||
ELSE NULL
|
||||
END;
|
||||
|
||||
-- Step 4: Drop old columns that are now consolidated
|
||||
ALTER TABLE workspace_settings
|
||||
DROP COLUMN IF EXISTS auto_invite_domain,
|
||||
DROP COLUMN IF EXISTS auto_invite_operator,
|
||||
DROP COLUMN IF EXISTS auto_add,
|
||||
DROP COLUMN IF EXISTS auto_add_instance_groups,
|
||||
DROP COLUMN IF EXISTS auto_add_instance_groups_roles,
|
||||
DROP COLUMN IF EXISTS error_handler,
|
||||
DROP COLUMN IF EXISTS error_handler_extra_args,
|
||||
DROP COLUMN IF EXISTS error_handler_muted_on_cancel,
|
||||
DROP COLUMN IF EXISTS error_handler_muted_on_user_path,
|
||||
DROP COLUMN IF EXISTS success_handler,
|
||||
DROP COLUMN IF EXISTS success_handler_extra_args;
|
||||
|
||||
-- Step 5: Rename new columns to their final names
|
||||
ALTER TABLE workspace_settings
|
||||
RENAME COLUMN error_handler_new TO error_handler;
|
||||
ALTER TABLE workspace_settings
|
||||
RENAME COLUMN success_handler_new TO success_handler;
|
||||
|
||||
-- Step 6: Add GIN index on auto_invite for efficient JSONB queries (e.g., ? operator for instance_groups)
|
||||
-- Using default GIN opclass (not jsonb_path_ops) to support the ? operator
|
||||
CREATE INDEX IF NOT EXISTS idx_workspace_settings_auto_invite ON workspace_settings USING GIN (auto_invite);
|
||||
@@ -12,12 +12,11 @@ async fn test_success_handler_cache(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
// First, create a success handler script
|
||||
let _server = ApiServer::start(db.clone()).await?;
|
||||
|
||||
// Set up a success handler in workspace_settings
|
||||
// Set up a success handler in workspace_settings using new JSONB column
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE workspace_settings
|
||||
SET success_handler = 'script/f/test/success_handler',
|
||||
success_handler_extra_args = '{"key": "value"}'::json
|
||||
SET success_handler = '{"path": "script/f/test/success_handler", "extra_args": {"key": "value"}}'::jsonb
|
||||
WHERE workspace_id = 'test-workspace'
|
||||
"#
|
||||
)
|
||||
@@ -26,7 +25,7 @@ async fn test_success_handler_cache(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
|
||||
// Verify the success handler was set
|
||||
let result = sqlx::query_scalar!(
|
||||
r#"SELECT success_handler FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
r#"SELECT success_handler->>'path' FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?;
|
||||
@@ -35,7 +34,7 @@ async fn test_success_handler_cache(db: Pool<Postgres>) -> anyhow::Result<()> {
|
||||
|
||||
// Verify extra args were set
|
||||
let extra_args = sqlx::query_scalar!(
|
||||
r#"SELECT success_handler_extra_args FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
r#"SELECT success_handler->'extra_args' FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?;
|
||||
@@ -55,7 +54,7 @@ async fn test_success_handler_settings(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
|
||||
// Initially success_handler should be NULL
|
||||
let initial = sqlx::query_scalar!(
|
||||
r#"SELECT success_handler FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
r#"SELECT success_handler->>'path' FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?;
|
||||
@@ -65,7 +64,7 @@ async fn test_success_handler_settings(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE workspace_settings
|
||||
SET success_handler = 'flow/f/test/success_flow'
|
||||
SET success_handler = '{"path": "flow/f/test/success_flow"}'::jsonb
|
||||
WHERE workspace_id = 'test-workspace'
|
||||
"#
|
||||
)
|
||||
@@ -73,7 +72,7 @@ async fn test_success_handler_settings(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
.await?;
|
||||
|
||||
let after_set = sqlx::query_scalar!(
|
||||
r#"SELECT success_handler FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
r#"SELECT success_handler->>'path' FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?;
|
||||
@@ -91,7 +90,7 @@ async fn test_success_handler_settings(db: Pool<Postgres>) -> anyhow::Result<()>
|
||||
.await?;
|
||||
|
||||
let after_remove = sqlx::query_scalar!(
|
||||
r#"SELECT success_handler FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
r#"SELECT success_handler->>'path' FROM workspace_settings WHERE workspace_id = 'test-workspace'"#
|
||||
)
|
||||
.fetch_one(&db)
|
||||
.await?;
|
||||
@@ -152,7 +151,7 @@ export async function main(path: string, email: string, job_id: string, is_flow:
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE workspace_settings
|
||||
SET success_handler = 'script/f/test/success_handler'
|
||||
SET success_handler = '{"path": "script/f/test/success_handler"}'::jsonb
|
||||
WHERE workspace_id = 'test-workspace'
|
||||
"#
|
||||
)
|
||||
|
||||
@@ -2337,20 +2337,8 @@ paths:
|
||||
type: string
|
||||
teams_team_guid:
|
||||
type: string
|
||||
auto_invite_domain:
|
||||
type: string
|
||||
auto_invite_operator:
|
||||
type: boolean
|
||||
auto_add:
|
||||
type: boolean
|
||||
auto_add_instance_groups:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
auto_add_instance_groups_roles:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
auto_invite:
|
||||
$ref: "#/components/schemas/AutoInviteConfig"
|
||||
plan:
|
||||
type: string
|
||||
customer_id:
|
||||
@@ -2362,15 +2350,9 @@ paths:
|
||||
ai_config:
|
||||
$ref: "#/components/schemas/AIConfig"
|
||||
error_handler:
|
||||
type: string
|
||||
error_handler_extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
error_handler_muted_on_cancel:
|
||||
type: boolean
|
||||
$ref: "#/components/schemas/ErrorHandlerConfig"
|
||||
success_handler:
|
||||
type: string
|
||||
success_handler_extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
$ref: "#/components/schemas/SuccessHandlerConfig"
|
||||
large_file_storage:
|
||||
$ref: "#/components/schemas/LargeFileStorage"
|
||||
ducklake:
|
||||
@@ -2391,8 +2373,6 @@ paths:
|
||||
type: string
|
||||
operator_settings:
|
||||
$ref: "#/components/schemas/OperatorSettings"
|
||||
required:
|
||||
- error_handler_muted_on_cancel
|
||||
|
||||
/w/{workspace}/workspaces/get_deploy_to:
|
||||
get:
|
||||
@@ -3071,14 +3051,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
error_handler:
|
||||
type: string
|
||||
error_handler_extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
error_handler_muted_on_cancel:
|
||||
type: boolean
|
||||
$ref: "#/components/schemas/EditErrorHandler"
|
||||
responses:
|
||||
"200":
|
||||
description: status
|
||||
@@ -3101,12 +3074,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success_handler:
|
||||
type: string
|
||||
success_handler_extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
$ref: "#/components/schemas/EditSuccessHandler"
|
||||
responses:
|
||||
"200":
|
||||
description: status
|
||||
@@ -16703,6 +16671,119 @@ components:
|
||||
# -- INLINE END --
|
||||
# Do not change line above
|
||||
|
||||
AutoInviteConfig:
|
||||
type: object
|
||||
description: Configuration for auto-inviting users to the workspace
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
default: false
|
||||
domain:
|
||||
type: string
|
||||
operator:
|
||||
type: boolean
|
||||
default: false
|
||||
description: If true, auto-invited users are added as operators. If false, they are added as developers.
|
||||
mode:
|
||||
type: string
|
||||
enum: ["invite", "add"]
|
||||
default: "invite"
|
||||
instance_groups:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
instance_groups_roles:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
|
||||
ErrorHandlerConfig:
|
||||
type: object
|
||||
description: Configuration for the workspace error handler
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
description: Path to the error handler script or flow
|
||||
extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
muted_on_cancel:
|
||||
type: boolean
|
||||
default: false
|
||||
muted_on_user_path:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
SuccessHandlerConfig:
|
||||
type: object
|
||||
description: Configuration for the workspace success handler
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
description: Path to the success handler script or flow
|
||||
extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
|
||||
EditErrorHandler:
|
||||
description: Request body for editing the workspace error handler. Accepts both new grouped format and legacy flat format for backward compatibility.
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/EditErrorHandlerNew"
|
||||
- $ref: "#/components/schemas/EditErrorHandlerLegacy"
|
||||
|
||||
EditErrorHandlerNew:
|
||||
type: object
|
||||
description: New grouped format for editing error handler
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
description: Path to the error handler script or flow
|
||||
extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
muted_on_cancel:
|
||||
type: boolean
|
||||
default: false
|
||||
muted_on_user_path:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
EditErrorHandlerLegacy:
|
||||
type: object
|
||||
description: Legacy flat format for editing error handler (deprecated, use new format)
|
||||
properties:
|
||||
error_handler:
|
||||
type: string
|
||||
description: Path to the error handler script or flow
|
||||
error_handler_extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
error_handler_muted_on_cancel:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
EditSuccessHandler:
|
||||
description: Request body for editing the workspace success handler. Accepts both new grouped format and legacy flat format for backward compatibility.
|
||||
oneOf:
|
||||
- $ref: "#/components/schemas/EditSuccessHandlerNew"
|
||||
- $ref: "#/components/schemas/EditSuccessHandlerLegacy"
|
||||
|
||||
EditSuccessHandlerNew:
|
||||
type: object
|
||||
description: New grouped format for editing success handler
|
||||
properties:
|
||||
path:
|
||||
type: string
|
||||
description: Path to the success handler script or flow
|
||||
extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
|
||||
EditSuccessHandlerLegacy:
|
||||
type: object
|
||||
description: Legacy flat format for editing success handler (deprecated, use new format)
|
||||
properties:
|
||||
success_handler:
|
||||
type: string
|
||||
description: Path to the success handler script or flow
|
||||
success_handler_extra_args:
|
||||
$ref: "#/components/schemas/ScriptArgs"
|
||||
|
||||
VaultSettings:
|
||||
type: object
|
||||
required:
|
||||
|
||||
@@ -289,10 +289,10 @@ async fn toggle_workspace_error_handler(
|
||||
let error_handler_maybe: Option<String> = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT
|
||||
error_handler
|
||||
FROM
|
||||
workspace_settings
|
||||
WHERE
|
||||
error_handler->>'path'
|
||||
FROM
|
||||
workspace_settings
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
"#,
|
||||
w_id
|
||||
|
||||
@@ -682,9 +682,16 @@ async fn add_user_igroup(
|
||||
#[cfg(all(feature = "private", feature = "enterprise"))]
|
||||
{
|
||||
use crate::workspaces_ee::auto_add_user;
|
||||
let workspaces = sqlx::query!("SELECT workspace_id, auto_add_instance_groups_roles FROM workspace_settings WHERE $1 = ANY(COALESCE(auto_add_instance_groups, '{}'))", &name).fetch_all(&mut *tx).await?;
|
||||
let workspaces = sqlx::query!(
|
||||
r#"
|
||||
SELECT workspace_id, auto_invite->'instance_groups_roles' as instance_groups_roles
|
||||
FROM workspace_settings
|
||||
WHERE auto_invite->'instance_groups' ? $1
|
||||
"#,
|
||||
&name
|
||||
).fetch_all(&mut *tx).await?;
|
||||
for ws in workspaces {
|
||||
let role = ws.auto_add_instance_groups_roles.and_then(|r| r.get(&name).and_then(|v| v.as_str().map(String::from))).unwrap_or_else(|| "developer".to_string());
|
||||
let role = ws.instance_groups_roles.and_then(|r| r.get(&name).and_then(|v| v.as_str().map(String::from))).unwrap_or_else(|| "developer".to_string());
|
||||
let (is_admin, is_operator) = match role.as_str() { "admin" => (true, false), "operator" => (false, true), _ => (false, false) };
|
||||
auto_add_user(&email, &ws.workspace_id, &is_operator, &mut tx, &authed, Some(serde_json::json!({"source": "instance_group", "group": &name}))).await?;
|
||||
if is_admin { sqlx::query!("UPDATE usr SET is_admin = true WHERE workspace_id = $1 AND email = $2", &ws.workspace_id, &email).execute(&mut *tx).await?; }
|
||||
@@ -748,10 +755,10 @@ async fn list_igroups_with_workspaces(Extension(db): Extension<DB>) -> JsonResul
|
||||
ig.name as group_name,
|
||||
ws.workspace_id,
|
||||
w.name as workspace_name,
|
||||
ws.auto_add_instance_groups_roles->ig.name as role
|
||||
ws.auto_invite->'instance_groups_roles'->ig.name as role
|
||||
FROM instance_group ig
|
||||
INNER JOIN workspace_settings ws ON ws.auto_add_instance_groups IS NOT NULL
|
||||
AND ig.name = ANY(ws.auto_add_instance_groups)
|
||||
INNER JOIN workspace_settings ws ON ws.auto_invite->'instance_groups' IS NOT NULL
|
||||
AND ws.auto_invite->'instance_groups' ? ig.name
|
||||
INNER JOIN workspace w ON w.id = ws.workspace_id AND w.deleted = false
|
||||
ORDER BY ig.name, ws.workspace_id
|
||||
"#
|
||||
|
||||
@@ -1467,7 +1467,7 @@ async fn toggle_workspace_error_handler(
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
let error_handler_maybe: Option<String> = sqlx::query_scalar!(
|
||||
"SELECT error_handler FROM workspace_settings WHERE workspace_id = $1",
|
||||
"SELECT error_handler->>'path' FROM workspace_settings WHERE workspace_id = $1",
|
||||
w_id
|
||||
)
|
||||
.fetch_optional(&mut *tx)
|
||||
|
||||
@@ -1465,11 +1465,11 @@ async fn convert_user_to_group(
|
||||
r#"
|
||||
SELECT
|
||||
eig.igroup as group_name,
|
||||
ws.auto_add_instance_groups_roles
|
||||
ws.auto_invite->'instance_groups_roles' as instance_groups_roles
|
||||
FROM email_to_igroup eig
|
||||
INNER JOIN workspace_settings ws ON ws.workspace_id = $1
|
||||
WHERE eig.email = $2
|
||||
AND eig.igroup = ANY(ws.auto_add_instance_groups)
|
||||
AND ws.auto_invite->'instance_groups' ? eig.igroup
|
||||
"#,
|
||||
&w_id,
|
||||
&user_info.email
|
||||
@@ -1486,7 +1486,7 @@ async fn convert_user_to_group(
|
||||
|
||||
// Determine the group with highest precedence (same logic as process_instance_group_auto_adds)
|
||||
let roles: std::collections::HashMap<String, String> =
|
||||
if let Some(roles_json) = &eligible_groups[0].auto_add_instance_groups_roles {
|
||||
if let Some(roles_json) = &eligible_groups[0].instance_groups_roles {
|
||||
serde_json::from_value(roles_json.clone()).unwrap_or_default()
|
||||
} else {
|
||||
std::collections::HashMap::new()
|
||||
|
||||
@@ -249,12 +249,6 @@ pub struct WorkspaceSettings {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub slack_oauth_client_secret: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_invite_domain: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_invite_operator: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_add: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub customer_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub plan: Option<String>,
|
||||
@@ -265,25 +259,15 @@ pub struct WorkspaceSettings {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ai_config: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub error_handler: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub error_handler_extra_args: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub error_handler_muted_on_cancel: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub success_handler: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub success_handler_extra_args: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub large_file_storage: Option<serde_json::Value>, // effectively: DatasetsStorage
|
||||
pub large_file_storage: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ducklake: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub datatable: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub git_sync: Option<serde_json::Value>, // effectively: WorkspaceGitSyncSettings
|
||||
pub git_sync: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub deploy_ui: Option<serde_json::Value>, // effectively: WorkspaceDeploymentUISettings
|
||||
pub deploy_ui: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub default_app: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -296,13 +280,16 @@ pub struct WorkspaceSettings {
|
||||
pub operator_settings: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub git_app_installations: Option<serde_json::Value>,
|
||||
// Grouped config fields
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_add_instance_groups: Option<Vec<String>>,
|
||||
pub auto_invite: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_add_instance_groups_roles: Option<serde_json::Value>,
|
||||
pub error_handler: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub success_handler: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
// #[derive(sqlx::Type, Serialize, Deserialize, Debug)]
|
||||
/// #[derive(sqlx::Type, Serialize, Deserialize, Debug)]
|
||||
// #[sqlx(type_name = "WORKSPACE_KEY_KIND", rename_all = "lowercase")]
|
||||
// pub enum WorkspaceKeyKind {
|
||||
// Cloud,
|
||||
@@ -440,19 +427,84 @@ pub struct NewWorkspaceUser {
|
||||
pub operator: bool,
|
||||
}
|
||||
|
||||
// New format for error handler (grouped)
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditErrorHandler {
|
||||
pub error_handler: Option<String>,
|
||||
pub error_handler_extra_args: Option<serde_json::Value>,
|
||||
pub error_handler_muted_on_cancel: Option<bool>,
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct EditErrorHandlerNew {
|
||||
pub path: Option<String>,
|
||||
pub extra_args: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub muted_on_cancel: bool,
|
||||
#[serde(default)]
|
||||
pub muted_on_user_path: bool,
|
||||
}
|
||||
|
||||
// Legacy format for error handler (flat fields from old CLI)
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditSuccessHandler {
|
||||
pub struct EditErrorHandlerLegacy {
|
||||
pub error_handler: Option<String>,
|
||||
pub error_handler_extra_args: Option<serde_json::Value>,
|
||||
#[serde(default)]
|
||||
pub error_handler_muted_on_cancel: bool,
|
||||
}
|
||||
|
||||
// Accepts both old and new formats
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum EditErrorHandler {
|
||||
New(EditErrorHandlerNew),
|
||||
Legacy(EditErrorHandlerLegacy),
|
||||
}
|
||||
|
||||
impl EditErrorHandler {
|
||||
pub fn into_normalized(self) -> EditErrorHandlerNew {
|
||||
match self {
|
||||
EditErrorHandler::New(new) => new,
|
||||
EditErrorHandler::Legacy(legacy) => EditErrorHandlerNew {
|
||||
path: legacy.error_handler,
|
||||
extra_args: legacy.error_handler_extra_args,
|
||||
muted_on_cancel: legacy.error_handler_muted_on_cancel,
|
||||
muted_on_user_path: false, // Old format doesn't have this field
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New format for success handler (grouped)
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct EditSuccessHandlerNew {
|
||||
pub path: Option<String>,
|
||||
pub extra_args: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
// Legacy format for success handler (flat fields from old CLI)
|
||||
#[derive(Deserialize)]
|
||||
pub struct EditSuccessHandlerLegacy {
|
||||
pub success_handler: Option<String>,
|
||||
pub success_handler_extra_args: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
// Accepts both old and new formats
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum EditSuccessHandler {
|
||||
New(EditSuccessHandlerNew),
|
||||
Legacy(EditSuccessHandlerLegacy),
|
||||
}
|
||||
|
||||
impl EditSuccessHandler {
|
||||
pub fn into_normalized(self) -> EditSuccessHandlerNew {
|
||||
match self {
|
||||
EditSuccessHandler::New(new) => new,
|
||||
EditSuccessHandler::Legacy(legacy) => EditSuccessHandlerNew {
|
||||
path: legacy.success_handler,
|
||||
extra_args: legacy.success_handler_extra_args,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref EMAIL_REGEXP: Regex = Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap();
|
||||
}
|
||||
@@ -555,19 +607,11 @@ async fn get_settings(
|
||||
slack_email,
|
||||
slack_oauth_client_id,
|
||||
slack_oauth_client_secret,
|
||||
auto_invite_domain,
|
||||
auto_invite_operator,
|
||||
auto_add,
|
||||
customer_id,
|
||||
plan,
|
||||
webhook,
|
||||
deploy_to,
|
||||
ai_config,
|
||||
error_handler,
|
||||
error_handler_extra_args,
|
||||
error_handler_muted_on_cancel,
|
||||
success_handler,
|
||||
success_handler_extra_args,
|
||||
large_file_storage,
|
||||
datatable,
|
||||
ducklake,
|
||||
@@ -579,8 +623,9 @@ async fn get_settings(
|
||||
color,
|
||||
operator_settings,
|
||||
git_app_installations,
|
||||
auto_add_instance_groups,
|
||||
auto_add_instance_groups_roles
|
||||
auto_invite,
|
||||
error_handler,
|
||||
success_handler
|
||||
FROM
|
||||
workspace_settings
|
||||
WHERE
|
||||
@@ -745,9 +790,8 @@ async fn get_slack_oauth_config(
|
||||
) -> JsonResult<GetSlackOAuthConfigResponse> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
let settings = sqlx::query_as!(
|
||||
WorkspaceSettings,
|
||||
"SELECT * FROM workspace_settings WHERE workspace_id = $1",
|
||||
let settings = sqlx::query!(
|
||||
"SELECT slack_oauth_client_id, slack_oauth_client_secret FROM workspace_settings WHERE workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.fetch_one(&db)
|
||||
@@ -2158,6 +2202,9 @@ async fn edit_error_handler(
|
||||
) -> Result<String> {
|
||||
require_admin(is_admin, &username)?;
|
||||
|
||||
// Normalize to new format (handles both old CLI and new CLI requests)
|
||||
let ee = ee.into_normalized();
|
||||
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
sqlx::query_as!(
|
||||
@@ -2171,8 +2218,8 @@ async fn edit_error_handler(
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
if let Some(error_handler) = &ee.error_handler {
|
||||
match ee.error_handler_extra_args.as_ref() {
|
||||
if let Some(path) = &ee.path {
|
||||
match ee.extra_args.as_ref() {
|
||||
Some(extra_args) if extra_args.is_object() => {
|
||||
let Ok(email_recipients) = serde_json::from_value::<Option<Vec<String>>>(
|
||||
extra_args["email_recipients"].to_owned(),
|
||||
@@ -2196,41 +2243,34 @@ async fn edit_error_handler(
|
||||
None => {}
|
||||
_ => {
|
||||
return Err(Error::BadRequest(
|
||||
"Field `error_handler_extra_args` expected to be JSON object".to_string(),
|
||||
"Field `extra_args` expected to be JSON object".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
let mut error_handler = serde_json::json!({
|
||||
"path": path,
|
||||
});
|
||||
if let Some(extra_args) = &ee.extra_args {
|
||||
error_handler["extra_args"] = extra_args.clone();
|
||||
}
|
||||
if ee.muted_on_cancel {
|
||||
error_handler["muted_on_cancel"] = serde_json::json!(true);
|
||||
}
|
||||
if ee.muted_on_user_path {
|
||||
error_handler["muted_on_user_path"] = serde_json::json!(true);
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE
|
||||
workspace_settings
|
||||
SET
|
||||
error_handler = $1,
|
||||
error_handler_extra_args = $2,
|
||||
error_handler_muted_on_cancel = $3
|
||||
WHERE
|
||||
workspace_id = $4
|
||||
"#,
|
||||
"UPDATE workspace_settings SET error_handler = $1 WHERE workspace_id = $2",
|
||||
error_handler,
|
||||
ee.error_handler_extra_args,
|
||||
ee.error_handler_muted_on_cancel.unwrap_or(false),
|
||||
&w_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
} else {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE
|
||||
workspace_settings
|
||||
SET
|
||||
error_handler = NULL,
|
||||
error_handler_extra_args = NULL,
|
||||
error_handler_muted_on_cancel = false
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
"#,
|
||||
"UPDATE workspace_settings SET error_handler = NULL WHERE workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
@@ -2244,7 +2284,7 @@ async fn edit_error_handler(
|
||||
ActionKind::Update,
|
||||
&w_id,
|
||||
Some(&authed.email),
|
||||
Some([("error_handler", &format!("{:?}", ee.error_handler)[..])].into()),
|
||||
Some([("error_handler", &format!("{:?}", ee.path)[..])].into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
@@ -2272,6 +2312,9 @@ async fn edit_success_handler(
|
||||
) -> Result<String> {
|
||||
require_admin(authed.is_admin, &authed.username)?;
|
||||
|
||||
// Normalize to new format (handles both old CLI and new CLI requests)
|
||||
let es = es.into_normalized();
|
||||
|
||||
let mut tx = db.begin().await?;
|
||||
|
||||
sqlx::query_as!(
|
||||
@@ -2285,34 +2328,24 @@ async fn edit_success_handler(
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
if let Some(success_handler) = &es.success_handler {
|
||||
if let Some(path) = &es.path {
|
||||
let mut success_handler = serde_json::json!({
|
||||
"path": path,
|
||||
});
|
||||
if let Some(extra_args) = &es.extra_args {
|
||||
success_handler["extra_args"] = extra_args.clone();
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE
|
||||
workspace_settings
|
||||
SET
|
||||
success_handler = $1,
|
||||
success_handler_extra_args = $2
|
||||
WHERE
|
||||
workspace_id = $3
|
||||
"#,
|
||||
"UPDATE workspace_settings SET success_handler = $1 WHERE workspace_id = $2",
|
||||
success_handler,
|
||||
es.success_handler_extra_args,
|
||||
&w_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
} else {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE
|
||||
workspace_settings
|
||||
SET
|
||||
success_handler = NULL,
|
||||
success_handler_extra_args = NULL
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
"#,
|
||||
"UPDATE workspace_settings SET success_handler = NULL WHERE workspace_id = $1",
|
||||
&w_id
|
||||
)
|
||||
.execute(&mut *tx)
|
||||
@@ -2326,7 +2359,7 @@ async fn edit_success_handler(
|
||||
ActionKind::Update,
|
||||
&w_id,
|
||||
Some(&authed.email),
|
||||
Some([("success_handler", &format!("{:?}", es.success_handler)[..])].into()),
|
||||
Some([("success_handler", &format!("{:?}", es.path)[..])].into()),
|
||||
)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
|
||||
@@ -177,6 +177,8 @@ pub(crate) struct ArchiveQueryParams {
|
||||
include_key: Option<bool>,
|
||||
include_workspace_dependencies: Option<bool>,
|
||||
default_ts: Option<String>,
|
||||
/// Settings format version: "v1" (default) returns legacy flat format, "v2" returns grouped format
|
||||
settings_version: Option<String>,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -253,12 +255,41 @@ struct SimplifiedGroup {
|
||||
admins: Vec<String>,
|
||||
}
|
||||
|
||||
// V2 format: New grouped format
|
||||
#[derive(Serialize)]
|
||||
struct SimplifiedSettings {
|
||||
// slack_team_id: Option<String>,
|
||||
// slack_name: Option<String>,
|
||||
// slack_command_script: Option<String>,
|
||||
// slack_email: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
auto_invite: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
webhook: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
deploy_to: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
error_handler: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
success_handler: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
ai_config: Option<serde_json::Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
large_file_storage: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
git_sync: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
default_app: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
default_scripts: Option<Value>,
|
||||
name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
mute_critical_alerts: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
color: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
operator_settings: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
// V1 format: Legacy flat format for backward compatibility (matches main branch exactly)
|
||||
#[derive(Serialize)]
|
||||
struct SimplifiedSettingsLegacy {
|
||||
auto_invite_enabled: bool,
|
||||
auto_invite_as: String,
|
||||
auto_invite_mode: String,
|
||||
@@ -290,6 +321,25 @@ struct SimplifiedSettings {
|
||||
operator_settings: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
// Internal struct for querying database
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct SettingsRow {
|
||||
auto_invite: Option<Value>,
|
||||
webhook: Option<String>,
|
||||
deploy_to: Option<String>,
|
||||
error_handler: Option<Value>,
|
||||
success_handler: Option<Value>,
|
||||
ai_config: Option<serde_json::Value>,
|
||||
large_file_storage: Option<Value>,
|
||||
git_sync: Option<Value>,
|
||||
default_app: Option<String>,
|
||||
default_scripts: Option<Value>,
|
||||
name: Option<String>,
|
||||
mute_critical_alerts: Option<bool>,
|
||||
color: Option<String>,
|
||||
operator_settings: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
pub(crate) async fn tarball_workspace(
|
||||
authed: ApiAuthed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
@@ -311,6 +361,7 @@ pub(crate) async fn tarball_workspace(
|
||||
include_key,
|
||||
include_workspace_dependencies,
|
||||
default_ts,
|
||||
settings_version,
|
||||
}): Query<ArchiveQueryParams>,
|
||||
) -> Result<([(HeaderName, String); 2], impl IntoResponse)> {
|
||||
// require_admin(authed.is_admin, &authed.username)?;
|
||||
@@ -843,41 +894,103 @@ pub(crate) async fn tarball_workspace(
|
||||
}
|
||||
|
||||
if include_settings.unwrap_or(false) {
|
||||
let settings = sqlx::query_as!(
|
||||
SimplifiedSettings,
|
||||
let row = sqlx::query_as::<_, SettingsRow>(
|
||||
r#"SELECT
|
||||
-- slack_team_id,
|
||||
-- slack_name,
|
||||
-- slack_command_script,
|
||||
-- CASE WHEN slack_email = 'missing@email.xyz' THEN NULL ELSE slack_email END AS slack_email,
|
||||
auto_invite_domain IS NOT NULL AS "auto_invite_enabled!",
|
||||
CASE WHEN auto_invite_operator IS TRUE THEN 'operator' ELSE 'developer' END AS "auto_invite_as!",
|
||||
CASE WHEN auto_add IS TRUE THEN 'add' ELSE 'invite' END AS "auto_invite_mode!",
|
||||
auto_invite,
|
||||
webhook,
|
||||
deploy_to,
|
||||
error_handler,
|
||||
success_handler,
|
||||
ai_config,
|
||||
error_handler_extra_args,
|
||||
error_handler_muted_on_cancel,
|
||||
large_file_storage,
|
||||
git_sync,
|
||||
default_app,
|
||||
default_scripts,
|
||||
workspace.name,
|
||||
workspace.name as name,
|
||||
mute_critical_alerts,
|
||||
color,
|
||||
operator_settings
|
||||
FROM workspace_settings
|
||||
LEFT JOIN workspace ON workspace.id = workspace_settings.workspace_id
|
||||
WHERE workspace_id = $1"#,
|
||||
&w_id
|
||||
).fetch_one(&mut *tx).await?;
|
||||
)
|
||||
.bind(&w_id)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
let settings_str = serde_json::to_value(settings)
|
||||
.map(|v| serde_json::to_string_pretty(&v).ok())
|
||||
.ok()
|
||||
.flatten()
|
||||
.ok_or_else(|| Error::internal_err("Error serializing settings".to_string()))?;
|
||||
// Use v2 format only if explicitly requested, otherwise use v1 (legacy) for backward compatibility
|
||||
let settings_str = if settings_version.as_deref() == Some("v2") {
|
||||
let settings = SimplifiedSettings {
|
||||
auto_invite: row.auto_invite,
|
||||
webhook: row.webhook,
|
||||
deploy_to: row.deploy_to,
|
||||
error_handler: row.error_handler,
|
||||
success_handler: row.success_handler,
|
||||
ai_config: row.ai_config,
|
||||
large_file_storage: row.large_file_storage,
|
||||
git_sync: row.git_sync,
|
||||
default_app: row.default_app,
|
||||
default_scripts: row.default_scripts,
|
||||
name: row.name.clone().unwrap_or_default(),
|
||||
mute_critical_alerts: row.mute_critical_alerts,
|
||||
color: row.color.clone(),
|
||||
operator_settings: row.operator_settings.clone(),
|
||||
};
|
||||
serde_json::to_value(settings)
|
||||
.map(|v| serde_json::to_string_pretty(&v).ok())
|
||||
.ok()
|
||||
.flatten()
|
||||
} else {
|
||||
// V1 (legacy) format: convert JSONB to flat fields (matches main branch exactly)
|
||||
let (auto_invite_enabled, auto_invite_as, auto_invite_mode) =
|
||||
if let Some(ref ai) = row.auto_invite {
|
||||
let enabled = ai.get("enabled").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
let operator = ai.get("operator").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
let mode = ai.get("mode").and_then(|v| v.as_str()).unwrap_or("invite");
|
||||
(
|
||||
enabled,
|
||||
if operator { "operator".to_string() } else { "developer".to_string() },
|
||||
mode.to_string(),
|
||||
)
|
||||
} else {
|
||||
(false, "developer".to_string(), "invite".to_string())
|
||||
};
|
||||
|
||||
let (error_handler, error_handler_extra_args, error_handler_muted_on_cancel) =
|
||||
if let Some(ref eh) = row.error_handler {
|
||||
let path = eh.get("path").and_then(|v| v.as_str()).map(String::from);
|
||||
let extra_args = eh.get("extra_args").cloned();
|
||||
let muted_on_cancel = eh.get("muted_on_cancel").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
(path, extra_args, muted_on_cancel)
|
||||
} else {
|
||||
(None, None, false)
|
||||
};
|
||||
|
||||
let settings = SimplifiedSettingsLegacy {
|
||||
auto_invite_enabled,
|
||||
auto_invite_as,
|
||||
auto_invite_mode,
|
||||
webhook: row.webhook,
|
||||
deploy_to: row.deploy_to,
|
||||
error_handler,
|
||||
error_handler_extra_args,
|
||||
error_handler_muted_on_cancel,
|
||||
ai_config: row.ai_config,
|
||||
large_file_storage: row.large_file_storage,
|
||||
git_sync: row.git_sync,
|
||||
default_app: row.default_app,
|
||||
default_scripts: row.default_scripts,
|
||||
name: row.name.unwrap_or_default(),
|
||||
mute_critical_alerts: row.mute_critical_alerts,
|
||||
color: row.color,
|
||||
operator_settings: row.operator_settings,
|
||||
};
|
||||
serde_json::to_value(settings)
|
||||
.map(|v| serde_json::to_string_pretty(&v).ok())
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
.ok_or_else(|| Error::internal_err("Error serializing settings".to_string()))?;
|
||||
|
||||
archive
|
||||
.write_to_archive(&settings_str, "settings.json")
|
||||
|
||||
@@ -777,8 +777,8 @@ lazy_static::lazy_static! {
|
||||
static ref RESTART_UNLESS_CANCELLED_CACHE: Cache<(i64, String), bool> = Cache::new(10000);
|
||||
|
||||
// Cache for workspace error handler settings with 60s TTL
|
||||
// Key: workspace_id, Value: (error_handler, error_handler_extra_args, error_handler_muted_on_cancel, expiry_timestamp)
|
||||
static ref WORKSPACE_ERROR_HANDLER_CACHE: Cache<String, (Option<String>, Option<Json<Box<RawValue>>>, bool, i64)> = Cache::new(1000);
|
||||
// Key: workspace_id, Value: (error_handler, error_handler_extra_args, error_handler_muted_on_cancel, error_handler_muted_on_user_path, expiry_timestamp)
|
||||
static ref WORKSPACE_ERROR_HANDLER_CACHE: Cache<String, (Option<String>, Option<Json<Box<RawValue>>>, bool, bool, i64)> = Cache::new(1000);
|
||||
|
||||
// Cache for workspace success handler settings with 60s TTL
|
||||
// Key: workspace_id, Value: (success_handler, success_handler_extra_args, expiry_timestamp)
|
||||
@@ -1521,12 +1521,12 @@ pub async fn report_error_to_workspace_handler_or_critical_side_channel(
|
||||
let w_id = &queued_job.workspace_id;
|
||||
let row_result = sqlx::query_as::<_, (Option<String>, Option<Json<Box<RawValue>>>)>(
|
||||
r#"
|
||||
SELECT
|
||||
error_handler,
|
||||
error_handler_extra_args
|
||||
FROM
|
||||
workspace_settings
|
||||
WHERE
|
||||
SELECT
|
||||
error_handler->>'path',
|
||||
(error_handler->'extra_args')::text::json
|
||||
FROM
|
||||
workspace_settings
|
||||
WHERE
|
||||
workspace_id = $1
|
||||
"#,
|
||||
)
|
||||
@@ -1578,10 +1578,14 @@ pub async fn report_error_to_workspace_handler_or_critical_side_channel(
|
||||
async fn fetch_error_handler_from_db(
|
||||
db: &Pool<Postgres>,
|
||||
w_id: &str,
|
||||
) -> Result<(Option<String>, Option<Json<Box<RawValue>>>, bool), Error> {
|
||||
sqlx::query_as::<_, (Option<String>, Option<Json<Box<RawValue>>>, bool)>(
|
||||
) -> Result<(Option<String>, Option<Json<Box<RawValue>>>, bool, bool), Error> {
|
||||
sqlx::query_as::<_, (Option<String>, Option<Json<Box<RawValue>>>, Option<bool>, Option<bool>)>(
|
||||
r#"
|
||||
SELECT error_handler, error_handler_extra_args, error_handler_muted_on_cancel
|
||||
SELECT
|
||||
error_handler->>'path',
|
||||
(error_handler->'extra_args')::text::json,
|
||||
(error_handler->>'muted_on_cancel')::boolean,
|
||||
(error_handler->>'muted_on_user_path')::boolean
|
||||
FROM workspace_settings
|
||||
WHERE workspace_id = $1
|
||||
"#,
|
||||
@@ -1590,6 +1594,9 @@ async fn fetch_error_handler_from_db(
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.context("fetching error handler info from workspace_settings")?
|
||||
.map(|(path, extra_args, muted_on_cancel, muted_on_user_path)| {
|
||||
(path, extra_args, muted_on_cancel.unwrap_or(false), muted_on_user_path.unwrap_or(false))
|
||||
})
|
||||
.ok_or_else(|| Error::internal_err(format!("no workspace settings for id {w_id}")))
|
||||
}
|
||||
|
||||
@@ -1602,16 +1609,16 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync>
|
||||
let w_id = &queued_job.workspace_id;
|
||||
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let (error_handler, error_handler_extra_args, error_handler_muted_on_cancel) =
|
||||
let (error_handler, error_handler_extra_args, error_handler_muted_on_cancel, error_handler_muted_on_user_path) =
|
||||
if let Some(cached) = WORKSPACE_ERROR_HANDLER_CACHE.get(w_id) {
|
||||
if cached.3 > now {
|
||||
(cached.0.clone(), cached.1.clone(), cached.2)
|
||||
if cached.4 > now {
|
||||
(cached.0.clone(), cached.1.clone(), cached.2, cached.3)
|
||||
} else {
|
||||
let row = fetch_error_handler_from_db(db, w_id).await?;
|
||||
let expiry = now + WORKSPACE_HANDLER_CACHE_TTL_SECONDS;
|
||||
WORKSPACE_ERROR_HANDLER_CACHE.insert(
|
||||
w_id.clone(),
|
||||
(row.0.clone(), row.1.clone(), row.2, expiry),
|
||||
(row.0.clone(), row.1.clone(), row.2, row.3, expiry),
|
||||
);
|
||||
row
|
||||
}
|
||||
@@ -1620,7 +1627,7 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync>
|
||||
let expiry = now + WORKSPACE_HANDLER_CACHE_TTL_SECONDS;
|
||||
WORKSPACE_ERROR_HANDLER_CACHE.insert(
|
||||
w_id.clone(),
|
||||
(row.0.clone(), row.1.clone(), row.2, expiry),
|
||||
(row.0.clone(), row.1.clone(), row.2, row.3, expiry),
|
||||
);
|
||||
row
|
||||
};
|
||||
@@ -1629,6 +1636,15 @@ pub async fn send_error_to_workspace_handler<'a, 'c, T: Serialize + Send + Sync>
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Skip error handler for scripts/flows starting with u/
|
||||
if error_handler_muted_on_user_path {
|
||||
if let Some(ref path) = queued_job.runnable_path {
|
||||
if path.starts_with("u/") {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(error_handler) = error_handler {
|
||||
let ws_error_handler_muted: Option<bool> = match queued_job.kind {
|
||||
JobKind::Script => {
|
||||
@@ -1685,7 +1701,9 @@ async fn fetch_success_handler_from_db(
|
||||
) -> Result<(Option<String>, Option<Json<Box<RawValue>>>), Error> {
|
||||
sqlx::query_as::<_, (Option<String>, Option<Json<Box<RawValue>>>)>(
|
||||
r#"
|
||||
SELECT success_handler, success_handler_extra_args
|
||||
SELECT
|
||||
success_handler->>'path',
|
||||
(success_handler->'extra_args')::text::json
|
||||
FROM workspace_settings
|
||||
WHERE workspace_id = $1
|
||||
"#,
|
||||
|
||||
@@ -40,7 +40,7 @@ export async function downloadZip(
|
||||
}&skip_secrets=${skipSecrets ?? false}&include_schedules=${includeSchedules ?? false
|
||||
}&include_triggers=${includeTriggers ?? false}&include_users=${includeUsers ?? false
|
||||
}&include_groups=${includeGroups ?? false}&include_settings=${includeSettings ?? false
|
||||
}&include_key=${includeKey ?? false}&include_workspace_dependencies=${includeWorkspaceDependenciesValue}&default_ts=${defaultTs ?? "bun"}&skip_resource_types=${skipResourceTypes ?? false}`;
|
||||
}&include_key=${includeKey ?? false}&include_workspace_dependencies=${includeWorkspaceDependenciesValue}&default_ts=${defaultTs ?? "bun"}&skip_resource_types=${skipResourceTypes ?? false}&settings_version=v2`;
|
||||
|
||||
const zipResponse = await fetch(url, {
|
||||
headers: requestHeaders,
|
||||
|
||||
+156
-58
@@ -8,19 +8,37 @@ import { deepEqual } from "../utils/utils.ts";
|
||||
import { removeWorkerPrefix } from "../commands/worker-groups/worker-groups.ts";
|
||||
import { decrypt, encrypt } from "../utils/local_encryption.ts";
|
||||
|
||||
// New grouped config interfaces
|
||||
export interface AutoInviteConfig {
|
||||
enabled: boolean;
|
||||
domain?: string;
|
||||
operator: boolean;
|
||||
mode: "invite" | "add";
|
||||
instance_groups?: string[];
|
||||
instance_groups_roles?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ErrorHandlerConfig {
|
||||
path?: string;
|
||||
extra_args?: any;
|
||||
muted_on_cancel?: boolean;
|
||||
muted_on_user_path?: boolean;
|
||||
}
|
||||
|
||||
export interface SuccessHandlerConfig {
|
||||
path?: string;
|
||||
extra_args?: any;
|
||||
}
|
||||
|
||||
export interface SimplifiedSettings {
|
||||
// slack_team_id?: string;
|
||||
// slack_name?: string;
|
||||
// slack_command_script?: string;
|
||||
// slack_email?: string;
|
||||
auto_invite_enabled: boolean;
|
||||
auto_invite_as: string;
|
||||
auto_invite_mode: string;
|
||||
// Grouped format (current)
|
||||
auto_invite?: AutoInviteConfig;
|
||||
error_handler?: ErrorHandlerConfig;
|
||||
success_handler?: SuccessHandlerConfig;
|
||||
|
||||
// Other fields
|
||||
webhook?: string;
|
||||
deploy_to?: string;
|
||||
error_handler?: string;
|
||||
error_handler_extra_args?: any;
|
||||
error_handler_muted_on_cancel?: boolean;
|
||||
ai_config?: AIConfig;
|
||||
large_file_storage?: any;
|
||||
git_sync?: any;
|
||||
@@ -32,6 +50,84 @@ export interface SimplifiedSettings {
|
||||
operator_settings?: any;
|
||||
}
|
||||
|
||||
// Legacy settings interface for reading old settings.yaml files
|
||||
interface LegacySimplifiedSettings {
|
||||
auto_invite_enabled?: boolean;
|
||||
auto_invite_as?: string;
|
||||
auto_invite_mode?: string;
|
||||
error_handler?: string;
|
||||
error_handler_extra_args?: any;
|
||||
error_handler_muted_on_cancel?: boolean;
|
||||
success_handler?: string;
|
||||
success_handler_extra_args?: any;
|
||||
// Other fields same as SimplifiedSettings
|
||||
webhook?: string;
|
||||
deploy_to?: string;
|
||||
ai_config?: AIConfig;
|
||||
large_file_storage?: any;
|
||||
git_sync?: any;
|
||||
default_app?: string;
|
||||
default_scripts?: any;
|
||||
name?: string;
|
||||
mute_critical_alerts?: boolean;
|
||||
color?: string;
|
||||
operator_settings?: any;
|
||||
}
|
||||
|
||||
// Helper to convert legacy flat settings to new grouped format
|
||||
export function migrateToGroupedFormat(settings: any): SimplifiedSettings {
|
||||
const result: SimplifiedSettings = { name: settings.name ?? "" };
|
||||
|
||||
// Copy non-legacy fields
|
||||
if (settings.webhook !== undefined) result.webhook = settings.webhook;
|
||||
if (settings.deploy_to !== undefined) result.deploy_to = settings.deploy_to;
|
||||
if (settings.ai_config !== undefined) result.ai_config = settings.ai_config;
|
||||
if (settings.large_file_storage !== undefined) result.large_file_storage = settings.large_file_storage;
|
||||
if (settings.git_sync !== undefined) result.git_sync = settings.git_sync;
|
||||
if (settings.default_app !== undefined) result.default_app = settings.default_app;
|
||||
if (settings.default_scripts !== undefined) result.default_scripts = settings.default_scripts;
|
||||
if (settings.mute_critical_alerts !== undefined) result.mute_critical_alerts = settings.mute_critical_alerts;
|
||||
if (settings.color !== undefined) result.color = settings.color;
|
||||
if (settings.operator_settings !== undefined) result.operator_settings = settings.operator_settings;
|
||||
|
||||
// Handle auto_invite: check if already grouped or needs migration
|
||||
if (settings.auto_invite && typeof settings.auto_invite === "object") {
|
||||
result.auto_invite = settings.auto_invite;
|
||||
} else if (settings.auto_invite_enabled !== undefined) {
|
||||
// Legacy format
|
||||
result.auto_invite = {
|
||||
enabled: settings.auto_invite_enabled,
|
||||
operator: settings.auto_invite_as === "operator",
|
||||
mode: (settings.auto_invite_mode as "invite" | "add") ?? "invite",
|
||||
};
|
||||
}
|
||||
|
||||
// Handle error_handler: check if already grouped or needs migration
|
||||
if (settings.error_handler && typeof settings.error_handler === "object") {
|
||||
result.error_handler = settings.error_handler;
|
||||
} else if (typeof settings.error_handler === "string") {
|
||||
// Legacy format (error_handler was a string path)
|
||||
result.error_handler = {
|
||||
path: settings.error_handler,
|
||||
extra_args: settings.error_handler_extra_args,
|
||||
muted_on_cancel: settings.error_handler_muted_on_cancel ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
// Handle success_handler: check if already grouped or needs migration
|
||||
if (settings.success_handler && typeof settings.success_handler === "object") {
|
||||
result.success_handler = settings.success_handler;
|
||||
} else if (typeof settings.success_handler === "string") {
|
||||
// Legacy format (success_handler was a string path)
|
||||
result.success_handler = {
|
||||
path: settings.success_handler,
|
||||
extra_args: settings.success_handler_extra_args,
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const INSTANCE_SETTINGS_PATH = "instance_settings.yaml";
|
||||
let instanceSettingsPath = INSTANCE_SETTINGS_PATH;
|
||||
async function checkInstanceSettingsPath(opts: InstanceSyncOptions) {
|
||||
@@ -52,8 +148,11 @@ export async function pushWorkspaceSettings(
|
||||
workspace: string,
|
||||
_path: string,
|
||||
settings: SimplifiedSettings | undefined,
|
||||
localSettings: SimplifiedSettings
|
||||
localSettings: SimplifiedSettings | any
|
||||
) {
|
||||
// Migrate local settings from legacy format if needed
|
||||
localSettings = migrateToGroupedFormat(localSettings);
|
||||
|
||||
try {
|
||||
const remoteSettings = await wmill.getSettings({
|
||||
workspace,
|
||||
@@ -63,22 +162,13 @@ export async function pushWorkspaceSettings(
|
||||
workspace,
|
||||
});
|
||||
|
||||
// Build settings from remote (now using grouped format)
|
||||
settings = {
|
||||
// slack_team_id: remoteSettings.slack_team_id,
|
||||
// slack_name: remoteSettings.slack_name,
|
||||
// slack_command_script: remoteSettings.slack_command_script,
|
||||
// slack_email: remoteSettings.slack_email,
|
||||
auto_invite_enabled: remoteSettings.auto_invite_domain !== null,
|
||||
auto_invite_as: remoteSettings.auto_invite_operator
|
||||
? "operator"
|
||||
: "developer",
|
||||
auto_invite_mode: remoteSettings.auto_add ? "add" : "invite",
|
||||
auto_invite: remoteSettings.auto_invite as AutoInviteConfig | undefined,
|
||||
error_handler: remoteSettings.error_handler as ErrorHandlerConfig | undefined,
|
||||
success_handler: remoteSettings.success_handler as SuccessHandlerConfig | undefined,
|
||||
webhook: remoteSettings.webhook,
|
||||
deploy_to: remoteSettings.deploy_to,
|
||||
error_handler: remoteSettings.error_handler,
|
||||
error_handler_extra_args: remoteSettings.error_handler_extra_args,
|
||||
error_handler_muted_on_cancel:
|
||||
remoteSettings.error_handler_muted_on_cancel ?? false,
|
||||
ai_config: remoteSettings.ai_config,
|
||||
large_file_storage: remoteSettings.large_file_storage,
|
||||
git_sync: remoteSettings.git_sync,
|
||||
@@ -98,8 +188,9 @@ export async function pushWorkspaceSettings(
|
||||
return;
|
||||
}
|
||||
log.debug(`Workspace settings are not up-to-date, updating...`);
|
||||
|
||||
if (localSettings.webhook !== settings.webhook) {
|
||||
log.debug(`Updateing webhook...`);
|
||||
log.debug(`Updating webhook...`);
|
||||
await wmill.editWebhook({
|
||||
workspace,
|
||||
requestBody: {
|
||||
@@ -107,32 +198,25 @@ export async function pushWorkspaceSettings(
|
||||
},
|
||||
});
|
||||
}
|
||||
if (
|
||||
localSettings.auto_invite_as !== settings.auto_invite_as ||
|
||||
localSettings.auto_invite_enabled !== settings.auto_invite_enabled ||
|
||||
localSettings.auto_invite_mode !== settings.auto_invite_mode
|
||||
) {
|
||||
|
||||
// Handle auto_invite using grouped format
|
||||
if (!deepEqual(localSettings.auto_invite, settings.auto_invite)) {
|
||||
log.debug(`Updating auto invite...`);
|
||||
|
||||
if (!["operator", "developer"].includes(settings.auto_invite_as)) {
|
||||
const localAutoInvite = localSettings.auto_invite;
|
||||
if (localAutoInvite?.mode && !["add", "invite"].includes(localAutoInvite.mode)) {
|
||||
throw new Error(
|
||||
`Invalid value for auto_invite_as. Valid values are "operator" and "developer"`
|
||||
);
|
||||
}
|
||||
|
||||
if (!["add", "invite"].includes(settings.auto_invite_mode)) {
|
||||
throw new Error(
|
||||
`Invalid value for auto_invite_mode. Valid values are "invite" and "add"`
|
||||
`Invalid value for auto_invite.mode. Valid values are "invite" and "add"`
|
||||
);
|
||||
}
|
||||
try {
|
||||
await wmill.editAutoInvite({
|
||||
workspace,
|
||||
requestBody: localSettings.auto_invite_enabled
|
||||
requestBody: localAutoInvite?.enabled
|
||||
? {
|
||||
operator: localSettings.auto_invite_as === "operator",
|
||||
operator: localAutoInvite.operator ?? false,
|
||||
invite_all: true,
|
||||
auto_add: localSettings.auto_invite_mode === "add",
|
||||
auto_add: localAutoInvite.mode === "add",
|
||||
}
|
||||
: {},
|
||||
});
|
||||
@@ -143,16 +227,17 @@ export async function pushWorkspaceSettings(
|
||||
);
|
||||
await wmill.editAutoInvite({
|
||||
workspace,
|
||||
requestBody: localSettings.auto_invite_enabled
|
||||
requestBody: localAutoInvite?.enabled
|
||||
? {
|
||||
operator: localSettings.auto_invite_as === "operator",
|
||||
operator: localAutoInvite.operator ?? false,
|
||||
invite_all: false,
|
||||
auto_add: localSettings.auto_invite_mode === "add",
|
||||
auto_add: localAutoInvite.mode === "add",
|
||||
}
|
||||
: {},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!deepEqual(localSettings.ai_config, settings.ai_config)) {
|
||||
log.debug(`Updating copilot settings...`);
|
||||
await wmill.editCopilotConfig({
|
||||
@@ -160,26 +245,35 @@ export async function pushWorkspaceSettings(
|
||||
requestBody: localSettings.ai_config ?? {},
|
||||
});
|
||||
}
|
||||
if (
|
||||
localSettings.error_handler != settings.error_handler ||
|
||||
!deepEqual(
|
||||
localSettings.error_handler_extra_args,
|
||||
settings.error_handler_extra_args
|
||||
) ||
|
||||
(localSettings.error_handler_muted_on_cancel ?? false) !=
|
||||
(settings.error_handler_muted_on_cancel ?? false)
|
||||
) {
|
||||
|
||||
// Handle error_handler using grouped format
|
||||
if (!deepEqual(localSettings.error_handler, settings.error_handler)) {
|
||||
log.debug(`Updating error handler...`);
|
||||
const localErrorHandler = localSettings.error_handler;
|
||||
await wmill.editErrorHandler({
|
||||
workspace,
|
||||
requestBody: {
|
||||
error_handler: localSettings.error_handler,
|
||||
error_handler_extra_args: localSettings.error_handler_extra_args,
|
||||
error_handler_muted_on_cancel:
|
||||
localSettings.error_handler_muted_on_cancel ?? false,
|
||||
path: localErrorHandler?.path,
|
||||
extra_args: localErrorHandler?.extra_args,
|
||||
muted_on_cancel: localErrorHandler?.muted_on_cancel ?? false,
|
||||
muted_on_user_path: localErrorHandler?.muted_on_user_path ?? false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Handle success_handler using grouped format
|
||||
if (!deepEqual(localSettings.success_handler, settings.success_handler)) {
|
||||
log.debug(`Updating success handler...`);
|
||||
const localSuccessHandler = localSettings.success_handler;
|
||||
await wmill.editSuccessHandler({
|
||||
workspace,
|
||||
requestBody: {
|
||||
path: localSuccessHandler?.path,
|
||||
extra_args: localSuccessHandler?.extra_args,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (localSettings.deploy_to != settings.deploy_to) {
|
||||
log.debug(`Updating deploy to...`);
|
||||
await wmill.editDeployTo({
|
||||
@@ -189,6 +283,7 @@ export async function pushWorkspaceSettings(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!deepEqual(localSettings.large_file_storage, settings.large_file_storage)
|
||||
) {
|
||||
@@ -200,6 +295,7 @@ export async function pushWorkspaceSettings(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!deepEqual(localSettings.git_sync, settings.git_sync)) {
|
||||
log.debug(`Updating git sync...`);
|
||||
await wmill.editWorkspaceGitSyncConfig({
|
||||
@@ -209,6 +305,7 @@ export async function pushWorkspaceSettings(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!deepEqual(localSettings.default_scripts, settings.default_scripts)) {
|
||||
log.debug(`Updating default scripts...`);
|
||||
await wmill.editDefaultScripts({
|
||||
@@ -216,6 +313,7 @@ export async function pushWorkspaceSettings(
|
||||
requestBody: localSettings.default_scripts,
|
||||
});
|
||||
}
|
||||
|
||||
if (localSettings.default_app != settings.default_app) {
|
||||
log.debug(`Updating default app...`);
|
||||
await wmill.editWorkspaceDefaultApp({
|
||||
@@ -256,7 +354,7 @@ export async function pushWorkspaceSettings(
|
||||
});
|
||||
}
|
||||
|
||||
if (localSettings.operator_settings != settings.operator_settings) {
|
||||
if (!deepEqual(localSettings.operator_settings, settings.operator_settings)) {
|
||||
log.debug(`Updating operator settings...`);
|
||||
await wmill.updateOperatorSettings({
|
||||
workspace,
|
||||
|
||||
@@ -92,11 +92,19 @@
|
||||
|
||||
async function loadSettings(): Promise<void> {
|
||||
const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
|
||||
auto_invite_domain = settings.auto_invite_domain
|
||||
operatorOnly = settings.auto_invite_operator
|
||||
autoAdd = settings.auto_add
|
||||
autoAddInstanceGroups = settings.auto_add_instance_groups || []
|
||||
autoAddInstanceGroupsRoles = settings.auto_add_instance_groups_roles || {}
|
||||
const autoInvite = settings.auto_invite as {
|
||||
enabled?: boolean
|
||||
domain?: string
|
||||
operator?: boolean
|
||||
mode?: string
|
||||
instance_groups?: string[]
|
||||
instance_groups_roles?: Record<string, string>
|
||||
} | undefined
|
||||
auto_invite_domain = autoInvite?.enabled ? (autoInvite?.domain ?? '*') : undefined
|
||||
operatorOnly = autoInvite?.operator ?? false
|
||||
autoAdd = autoInvite?.mode === 'add'
|
||||
autoAddInstanceGroups = autoInvite?.instance_groups || []
|
||||
autoAddInstanceGroupsRoles = autoInvite?.instance_groups_roles || {}
|
||||
}
|
||||
|
||||
let getUsagePromise: CancelablePromise<UserUsage[]> | undefined = undefined
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
let errorHandlerItemKind: 'flow' | 'script' = $state('script')
|
||||
let errorHandlerExtraArgs: Record<string, any> = $state({})
|
||||
let errorHandlerMutedOnCancel: boolean | undefined = $state(undefined)
|
||||
let errorHandlerMutedOnUserPath: boolean | undefined = $state(undefined)
|
||||
let successHandlerScriptPath: string | undefined = $state(undefined)
|
||||
let criticalAlertUIMuted: boolean | undefined = $state(undefined)
|
||||
let initialCriticalAlertUIMuted: boolean | undefined = $state(undefined)
|
||||
@@ -335,11 +336,14 @@
|
||||
initialCodeCompletionModel = codeCompletionModel
|
||||
initialCustomPrompts = clone(customPrompts)
|
||||
initialMaxTokensPerModel = clone(maxTokensPerModel)
|
||||
errorHandlerItemKind = settings.error_handler
|
||||
? (settings.error_handler.split('/')[0] as 'flow' | 'script')
|
||||
const errorHandler = settings.error_handler as { path?: string; extra_args?: any; muted_on_cancel?: boolean; muted_on_user_path?: boolean } | undefined
|
||||
const errorHandlerPath = errorHandler?.path ?? ''
|
||||
errorHandlerItemKind = errorHandlerPath
|
||||
? (errorHandlerPath.split('/')[0] as 'flow' | 'script')
|
||||
: 'script'
|
||||
errorHandlerScriptPath = (settings.error_handler ?? '').split('/').slice(1).join('/')
|
||||
errorHandlerMutedOnCancel = settings.error_handler_muted_on_cancel
|
||||
errorHandlerScriptPath = errorHandlerPath.split('/').slice(1).join('/')
|
||||
errorHandlerMutedOnCancel = errorHandler?.muted_on_cancel
|
||||
errorHandlerMutedOnUserPath = errorHandler?.muted_on_user_path
|
||||
criticalAlertUIMuted = settings.mute_critical_alerts
|
||||
initialCriticalAlertUIMuted = settings.mute_critical_alerts
|
||||
if (emptyString($enterpriseLicense)) {
|
||||
@@ -347,8 +351,9 @@
|
||||
} else {
|
||||
errorHandlerSelected = getHandlerType(errorHandlerScriptPath)
|
||||
}
|
||||
errorHandlerExtraArgs = settings.error_handler_extra_args ?? {}
|
||||
successHandlerScriptPath = (settings.success_handler ?? '').split('/').slice(1).join('/')
|
||||
errorHandlerExtraArgs = errorHandler?.extra_args ?? {}
|
||||
const successHandler = settings.success_handler as { path?: string; extra_args?: any } | undefined
|
||||
successHandlerScriptPath = (successHandler?.path ?? '').split('/').slice(1).join('/')
|
||||
workspaceDefaultAppPath = settings.default_app
|
||||
|
||||
s3ResourceSettings = convertBackendSettingsToFrontendSettings(
|
||||
@@ -498,9 +503,10 @@
|
||||
await WorkspaceService.editErrorHandler({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
error_handler: `${errorHandlerItemKind}/${errorHandlerScriptPath}`,
|
||||
error_handler_extra_args: errorHandlerExtraArgs,
|
||||
error_handler_muted_on_cancel: errorHandlerMutedOnCancel
|
||||
path: `${errorHandlerItemKind}/${errorHandlerScriptPath}`,
|
||||
extra_args: errorHandlerExtraArgs,
|
||||
muted_on_cancel: errorHandlerMutedOnCancel,
|
||||
muted_on_user_path: errorHandlerMutedOnUserPath
|
||||
}
|
||||
})
|
||||
sendUserToast(`workspace error handler set to ${errorHandlerScriptPath}`)
|
||||
@@ -508,9 +514,10 @@
|
||||
await WorkspaceService.editErrorHandler({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
error_handler: undefined,
|
||||
error_handler_extra_args: undefined,
|
||||
error_handler_muted_on_cancel: undefined
|
||||
path: undefined,
|
||||
extra_args: undefined,
|
||||
muted_on_cancel: undefined,
|
||||
muted_on_user_path: undefined
|
||||
}
|
||||
})
|
||||
sendUserToast(`workspace error handler removed`)
|
||||
@@ -522,7 +529,7 @@
|
||||
await WorkspaceService.editSuccessHandler({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
success_handler: `script/${successHandlerScriptPath}`
|
||||
path: `script/${successHandlerScriptPath}`
|
||||
}
|
||||
})
|
||||
sendUserToast(`workspace success handler set to ${successHandlerScriptPath}`)
|
||||
@@ -530,7 +537,7 @@
|
||||
await WorkspaceService.editSuccessHandler({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
success_handler: undefined
|
||||
path: undefined
|
||||
}
|
||||
})
|
||||
sendUserToast(`workspace success handler removed`)
|
||||
@@ -1156,6 +1163,14 @@
|
||||
bind:checked={errorHandlerMutedOnCancel}
|
||||
options={{ right: 'Do not run error handler for canceled jobs' }}
|
||||
/>
|
||||
<Toggle
|
||||
disabled={!$enterpriseLicense ||
|
||||
((errorHandlerSelected === 'slack' || errorHandlerSelected === 'teams') &&
|
||||
!emptyString(errorHandlerScriptPath) &&
|
||||
emptyString(errorHandlerExtraArgs['channel']))}
|
||||
bind:checked={errorHandlerMutedOnUserPath}
|
||||
options={{ right: 'Do not run error handler for u/ scripts and flows' }}
|
||||
/>
|
||||
<Button
|
||||
disabled={!$enterpriseLicense ||
|
||||
((errorHandlerSelected === 'slack' || errorHandlerSelected === 'teams') &&
|
||||
|
||||
Reference in New Issue
Block a user