diff --git a/backend/.sqlx/query-11ae5e1aade2bc9a78af2d956ece73766ae3330319619a68383b508a76f2d19c.json b/backend/.sqlx/query-11ae5e1aade2bc9a78af2d956ece73766ae3330319619a68383b508a76f2d19c.json index dcd14131c0..67bdf6ea9d 100644 --- a/backend/.sqlx/query-11ae5e1aade2bc9a78af2d956ece73766ae3330319619a68383b508a76f2d19c.json +++ b/backend/.sqlx/query-11ae5e1aade2bc9a78af2d956ece73766ae3330319619a68383b508a76f2d19c.json @@ -42,7 +42,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/.sqlx/query-254776959f60455a00a2c29a1783ee6b27c836197df6454d3bb5c59441c34f44.json b/backend/.sqlx/query-254776959f60455a00a2c29a1783ee6b27c836197df6454d3bb5c59441c34f44.json index ad66cfa646..272aefc9d2 100644 --- a/backend/.sqlx/query-254776959f60455a00a2c29a1783ee6b27c836197df6454d3bb5c59441c34f44.json +++ b/backend/.sqlx/query-254776959f60455a00a2c29a1783ee6b27c836197df6454d3bb5c59441c34f44.json @@ -67,7 +67,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/.sqlx/query-52ad1773a9f5a1b6be1bb16af408f051f27d3f21b615c468b85aa75b64d943db.json b/backend/.sqlx/query-52ad1773a9f5a1b6be1bb16af408f051f27d3f21b615c468b85aa75b64d943db.json index 2e4467041b..4df35d6cf7 100644 --- a/backend/.sqlx/query-52ad1773a9f5a1b6be1bb16af408f051f27d3f21b615c468b85aa75b64d943db.json +++ b/backend/.sqlx/query-52ad1773a9f5a1b6be1bb16af408f051f27d3f21b615c468b85aa75b64d943db.json @@ -28,7 +28,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/.sqlx/query-539d9ae486c7d1f5fb8c7278d698675b04359182b781b0cb0164402b798a7fef.json b/backend/.sqlx/query-539d9ae486c7d1f5fb8c7278d698675b04359182b781b0cb0164402b798a7fef.json new file mode 100644 index 0000000000..d635307e22 --- /dev/null +++ b/backend/.sqlx/query-539d9ae486c7d1f5fb8c7278d698675b04359182b781b0cb0164402b798a7fef.json @@ -0,0 +1,19 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE schedule SET ws_error_handler_muted = $1, on_failure = $2, on_failure_extra_args = $3, on_failure_times = $4, on_failure_exact = $5 WHERE workspace_id = $6", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bool", + "Varchar", + "Json", + "Int4", + "Bool", + "Text" + ] + }, + "nullable": [] + }, + "hash": "539d9ae486c7d1f5fb8c7278d698675b04359182b781b0cb0164402b798a7fef" +} diff --git a/backend/.sqlx/query-542ebd3d6cd8522d117112ef9eb13ff95b93d67994823a4a84d37639eb17e0b9.json b/backend/.sqlx/query-542ebd3d6cd8522d117112ef9eb13ff95b93d67994823a4a84d37639eb17e0b9.json new file mode 100644 index 0000000000..e1e0178bc0 --- /dev/null +++ b/backend/.sqlx/query-542ebd3d6cd8522d117112ef9eb13ff95b93d67994823a4a84d37639eb17e0b9.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE schedule SET on_recovery = $1, on_recovery_extra_args = $2, on_recovery_times = $3 WHERE workspace_id = $4", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Json", + "Int4", + "Text" + ] + }, + "nullable": [] + }, + "hash": "542ebd3d6cd8522d117112ef9eb13ff95b93d67994823a4a84d37639eb17e0b9" +} diff --git a/backend/.sqlx/query-620ddf29c5e867079df4c2aa6e80bccb19beeb9ddfa308ca97f254cd5ba8157e.json b/backend/.sqlx/query-620ddf29c5e867079df4c2aa6e80bccb19beeb9ddfa308ca97f254cd5ba8157e.json index c84e2ab8e2..e6282c7e80 100644 --- a/backend/.sqlx/query-620ddf29c5e867079df4c2aa6e80bccb19beeb9ddfa308ca97f254cd5ba8157e.json +++ b/backend/.sqlx/query-620ddf29c5e867079df4c2aa6e80bccb19beeb9ddfa308ca97f254cd5ba8157e.json @@ -60,7 +60,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json b/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json index 8b139967a6..61da7601af 100644 --- a/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json +++ b/backend/.sqlx/query-9d3556319411a27a875bf6cf0e5eda837cc63e4d8be912c0b5bfeea4a0c8db2e.json @@ -40,7 +40,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/.sqlx/query-a9915d3c72a278ff08fbe6571c2a4fc993507e255004107345b7151322e063de.json b/backend/.sqlx/query-a9915d3c72a278ff08fbe6571c2a4fc993507e255004107345b7151322e063de.json index b0437fe128..de4127eb14 100644 --- a/backend/.sqlx/query-a9915d3c72a278ff08fbe6571c2a4fc993507e255004107345b7151322e063de.json +++ b/backend/.sqlx/query-a9915d3c72a278ff08fbe6571c2a4fc993507e255004107345b7151322e063de.json @@ -46,7 +46,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/.sqlx/query-b6074eb9369de071f79165bf506fbc44f1688035e1928b4c3649d875187b1e1d.json b/backend/.sqlx/query-b6074eb9369de071f79165bf506fbc44f1688035e1928b4c3649d875187b1e1d.json index 0385dfd1d5..40ab7503e5 100644 --- a/backend/.sqlx/query-b6074eb9369de071f79165bf506fbc44f1688035e1928b4c3649d875187b1e1d.json +++ b/backend/.sqlx/query-b6074eb9369de071f79165bf506fbc44f1688035e1928b4c3649d875187b1e1d.json @@ -42,7 +42,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/.sqlx/query-c7febee7146a28481ce36f3570bceb38751a6527827525a582d9f8804981f552.json b/backend/.sqlx/query-c7febee7146a28481ce36f3570bceb38751a6527827525a582d9f8804981f552.json index 091c4160e3..3b43ee29f5 100644 --- a/backend/.sqlx/query-c7febee7146a28481ce36f3570bceb38751a6527827525a582d9f8804981f552.json +++ b/backend/.sqlx/query-c7febee7146a28481ce36f3570bceb38751a6527827525a582d9f8804981f552.json @@ -42,7 +42,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/.sqlx/query-d48070ef889723f408ed6c1a97b5ab9f0e98340cd33c6e29824f6030f0089071.json b/backend/.sqlx/query-d48070ef889723f408ed6c1a97b5ab9f0e98340cd33c6e29824f6030f0089071.json index 7df86d1a0e..c0c9ef873f 100644 --- a/backend/.sqlx/query-d48070ef889723f408ed6c1a97b5ab9f0e98340cd33c6e29824f6030f0089071.json +++ b/backend/.sqlx/query-d48070ef889723f408ed6c1a97b5ab9f0e98340cd33c6e29824f6030f0089071.json @@ -37,7 +37,6 @@ "bash", "postgresql", "nativets", - "Nativets", "bun", "mysql", "bigquery", diff --git a/backend/windmill-api/openapi-deref.yaml b/backend/windmill-api/openapi-deref.yaml index 4bd29fe91e..baae3f947c 100644 --- a/backend/windmill-api/openapi-deref.yaml +++ b/backend/windmill-api/openapi-deref.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: 1.200.0 + version: 1.201.0 title: Windmill API contact: name: Windmill Team @@ -875,6 +875,19 @@ paths: 'text/plain:': schema: type: string + /settings/send_stats: + post: + summary: send stats + operationId: sendStats + tags: + - setting + responses: + '200': + description: status + content: + 'text/plain:': + schema: + type: string /users/email: get: summary: get current user email (if logged in) @@ -8150,6 +8163,49 @@ paths: - id - success - duration_ms + /w/{workspace}/schedules/setdefaulthandler: + post: + summary: Set default error or recoevery handler + operationId: setDefaultErrorOrRecoveryHandler + tags: + - schedule + parameters: + - name: workspace + in: path + required: true + schema: *ref_0 + requestBody: + description: Handler description + required: true + content: + application/json: + schema: + type: object + properties: + handler_type: + type: string + enum: + - error + - recovery + override_existing: + type: boolean + path: + type: string + extra_args: + type: object + number_of_occurence: + type: integer + number_of_occurence_exact: + type: boolean + workspace_handler_muted: + type: boolean + required: + - handler_type + - override_existing + - path + responses: + '201': + description: default error handler set /groups/list: get: summary: list instance groups diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 114a5f04db..a3578534c8 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -5298,6 +5298,45 @@ paths: items: $ref: "#/components/schemas/ScheduleWJobs" + /w/{workspace}/schedules/setdefaulthandler: + post: + summary: Set default error or recoevery handler + operationId: setDefaultErrorOrRecoveryHandler + tags: + - schedule + parameters: + - $ref: "#/components/parameters/WorkspaceId" + requestBody: + description: Handler description + required: true + content: + application/json: + schema: + type: object + properties: + handler_type: + type: string + enum: ["error", "recovery"] + override_existing: + type: boolean + path: + type: string + extra_args: + type: object + number_of_occurence: + type: integer + number_of_occurence_exact: + type: boolean + workspace_handler_muted: + type: boolean + required: + - handler_type + - override_existing + - path + responses: + "201": + description: default error handler set + /groups/list: get: summary: list instance groups diff --git a/backend/windmill-api/src/schedule.rs b/backend/windmill-api/src/schedule.rs index 64f84bf25c..1f065096cb 100644 --- a/backend/windmill-api/src/schedule.rs +++ b/backend/windmill-api/src/schedule.rs @@ -8,7 +8,9 @@ use crate::{ db::{ApiAuthed, DB}, + settings::set_global_setting_internal, users::maybe_refresh_folders, + utils::require_super_admin, }; use axum::{ extract::{Extension, Path, Query}, @@ -40,6 +42,7 @@ pub fn workspaced_service() -> Router { .route("/update/*path", post(edit_schedule)) .route("/delete/*path", delete(delete_schedule)) .route("/setenabled/*path", post(set_enabled)) + .route("/setdefaulthandler", post(set_default_error_handler)) } pub fn global_service() -> Router { @@ -65,6 +68,18 @@ pub struct NewSchedule { pub ws_error_handler_muted: Option, } +#[derive(Serialize, Deserialize)] +pub struct ErrorOrRecoveryHandler { + pub handler_type: String, // 'error' or 'recovery' + pub override_existing: bool, + + pub path: String, + pub extra_args: Option, + pub number_of_occurence: Option, + pub number_of_occurence_exact: Option, + pub workspace_handler_muted: Option, +} + async fn check_path_conflict<'c>( tx: &mut Transaction<'c, Postgres>, w_id: &str, @@ -486,6 +501,77 @@ async fn delete_schedule( Ok(format!("schedule {} deleted", path)) } +async fn set_default_error_handler( + authed: ApiAuthed, + Extension(db): Extension, + Path(w_id): Path, + Json(payload): Json, +) -> Result<()> { + require_super_admin(&db, &authed.email).await?; + let (key, value) = match payload.handler_type.as_str() { + "error" => { + let key = format!("default_error_handler_{}", w_id); + let value = serde_json::json!({ + "wsErrorHandlerMuted": payload.workspace_handler_muted, + "errorHandlerPath": payload.path, + "errorHandlerExtraArgs": payload.extra_args, + "failedTimes": payload.number_of_occurence, + "failedExact": payload.number_of_occurence_exact, + }); + Ok((key, value)) + } + "recovery" => { + let key = format!("default_recovery_handler_{}", w_id); + let value = serde_json::json!({ + "recoveryHandlerPath": payload.path, + "recoveryHandlerExtraArgs": payload.extra_args, + "recoveredTimes": payload.number_of_occurence, + }); + Ok((key, value)) + } + _ => Err(Error::BadRequest( + "handler_type must be either 'error' or 'recovery'".to_string(), + )), + }?; + + set_global_setting_internal(&db, key, value).await?; + + if payload.override_existing { + match payload.handler_type.as_str() { + "error" => { + sqlx::query!( + "UPDATE schedule SET ws_error_handler_muted = $1, on_failure = $2, on_failure_extra_args = $3, on_failure_times = $4, on_failure_exact = $5 WHERE workspace_id = $6", + payload.workspace_handler_muted, + payload.path, + payload.extra_args, + payload.number_of_occurence, + payload.number_of_occurence_exact, + w_id, + ) + .execute(&db) + .await?; + Ok(()) + } + "recovery" => { + sqlx::query!( + "UPDATE schedule SET on_recovery = $1, on_recovery_extra_args = $2, on_recovery_times = $3 WHERE workspace_id = $4", + payload.path, + payload.extra_args, + payload.number_of_occurence, + w_id, + ) + .execute(&db) + .await?; + Ok(()) + } + _ => Err(Error::BadRequest( + "handler_type must be either 'error' or 'recovery'".to_string(), + )), + }?; + } + Ok(()) +} + async fn check_flow_conflict<'c>( tx: &mut Transaction<'c, Postgres>, w_id: &str, diff --git a/backend/windmill-api/src/settings.rs b/backend/windmill-api/src/settings.rs index ee4de50b18..c92fe3e8e7 100644 --- a/backend/windmill-api/src/settings.rs +++ b/backend/windmill-api/src/settings.rs @@ -125,25 +125,32 @@ pub async fn set_global_setting( Json(value): Json, ) -> error::Result<()> { require_super_admin(&db, &authed.email).await?; - match value.value { + set_global_setting_internal(&db, key, value.value).await +} + +pub async fn set_global_setting_internal( + db: &DB, + key: String, + value: serde_json::Value, +) -> error::Result<()> { + match value { serde_json::Value::Null => { - delete_global_setting(&db, &key).await?; + delete_global_setting(db, &key).await?; } serde_json::Value::String(x) if x.is_empty() => { - delete_global_setting(&db, &key).await?; + delete_global_setting(db, &key).await?; } v => { sqlx::query!( - "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2, updated_at = now()", - key, - v - ) - .execute(&db) - .await?; + "INSERT INTO global_settings (name, value) VALUES ($1, $2) ON CONFLICT (name) DO UPDATE SET value = $2, updated_at = now()", + key, + v + ) + .execute(db) + .await?; tracing::info!("Set global setting {} to {}", key, v); } }; - Ok(()) } diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index ee50d84337..eb3be29850 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -1468,12 +1468,6 @@ pub fn is_none_or_false(val: &Option) -> bool { } } -pub fn is_none_or_empty(val: &Option) -> bool { - match val { - Some(val) => !val, - None => true, - } -} enum ArchiveImpl { Zip(async_zip::write::ZipFileWriter), Tar(tokio_tar::Builder), diff --git a/frontend/src/lib/components/ScheduleEditorInner.svelte b/frontend/src/lib/components/ScheduleEditorInner.svelte index ea529f9c89..60d63bed9e 100644 --- a/frontend/src/lib/components/ScheduleEditorInner.svelte +++ b/frontend/src/lib/components/ScheduleEditorInner.svelte @@ -10,6 +10,7 @@ import ErrorOrRecoveryHandler from '$lib/components/ErrorOrRecoveryHandler.svelte' import Toggle from '$lib/components/Toggle.svelte' import Tooltip from '$lib/components/Tooltip.svelte' + import Dropdown from '$lib/components/Dropdown.svelte' import { FlowService, ScheduleService, @@ -21,6 +22,7 @@ import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores' import { canWrite, emptyString, formatCron, sendUserToast } from '$lib/utils' import { faList, faSave } from '@fortawesome/free-solid-svg-icons' + import { Save } from 'lucide-svelte' import { createEventDispatcher } from 'svelte' import Section from '$lib/components/Section.svelte' @@ -164,42 +166,42 @@ } } - async function saveAsDefaultErrorHandler() { + async function saveAsDefaultErrorHandler(overrideExisting: boolean) { if (!$enterpriseLicense) { sendUserToast(`Setting default error handler is an enterprise edition feature`, true) return } if ($workspaceStore && errorHandlerPath !== undefined) { - await SettingService.setGlobal({ - key: 'default_error_handler_' + $workspaceStore!, + await ScheduleService.setDefaultErrorOrRecoveryHandler({ + workspace: $workspaceStore!, requestBody: { - value: { - wsErrorHandlerMuted: wsErrorHandlerMuted, - errorHandlerPath: `${errorHandleritemKind}/${errorHandlerPath}`, - errorHandlerExtraArgs: errorHandlerExtraArgs, - failedTimes: failedTimes, - failedExact: failedExact - } + handler_type: 'error', + override_existing: overrideExisting, + path: `${errorHandleritemKind}/${errorHandlerPath}`, + extra_args: errorHandlerExtraArgs, + number_of_occurence: failedTimes, + number_of_occurence_exact: failedExact, + workspace_handler_muted: wsErrorHandlerMuted } }) sendUserToast(`Default error handler saved to ${errorHandlerPath}`, false) } } - async function saveAsDefaultRecoveryHandler() { + async function saveAsDefaultRecoveryHandler(overrideExisting: boolean) { if (!$enterpriseLicense) { sendUserToast(`Setting default recovery handler is an enterprise edition feature`, true) return } if ($workspaceStore && errorHandlerPath !== undefined) { - await SettingService.setGlobal({ - key: 'default_recovery_handler_' + $workspaceStore!, + await ScheduleService.setDefaultErrorOrRecoveryHandler({ + workspace: $workspaceStore!, requestBody: { - value: { - recoveryHandlerPath: `${recoveryHandlerItemKind}/${recoveryHandlerPath}`, - recoveryHandlerExtraArgs: recoveryHandlerExtraArgs, - recoveredTimes: recoveredTimes - } + handler_type: 'recovery', + override_existing: overrideExisting, + path: `${recoveryHandlerItemKind}/${recoveryHandlerPath}`, + extra_args: recoveryHandlerExtraArgs, + number_of_occurence: recoveredTimes } }) sendUserToast(`Default recovery handler saved to ${errorHandlerPath}`, false) @@ -430,16 +432,28 @@
- + + + Set as default + +
@@ -519,16 +533,28 @@
- + + + Set as default + +