From 2ce11cea3be04ff4d7ff7e7f1ff1e3d2d9b8c67e Mon Sep 17 00:00:00 2001 From: hugocasa Date: Thu, 28 Aug 2025 18:56:57 +0200 Subject: [PATCH 01/22] prevent past due team plans to overuse (#6487) * prevent past due team plans to overuse * nit * update ee ref --- ...d2ea9245f05e772f3601c99312c13ed65ae1a.json | 16 ---- ...a6aa59599f852cc17b16323fc627b6ad8671e.json | 16 ---- ...6242a39dd40186f1dbb48ad3018bd9f6913ec.json | 34 +++++++ ...a2e60c9c24decb2a6b41125247bbf741e9c25.json | 46 ---------- ...1789afb8250adde6f38f458b115e787ed876f.json | 58 ++++++++++++ ...50aa180b29bbe931d948f6e61976f71b7cdb9.json | 22 ----- backend/ee-repo-ref.txt | 2 +- ...250827102435_add_team_plan_status.down.sql | 7 ++ ...20250827102435_add_team_plan_status.up.sql | 18 ++++ backend/src/main.rs | 2 +- backend/windmill-api/openapi.yaml | 10 +++ backend/windmill-api/src/workspaces.rs | 4 +- backend/windmill-common/src/workspaces.rs | 43 +++++++-- backend/windmill-queue/src/jobs.rs | 90 +++++++++++-------- backend/windmill-worker/src/common.rs | 5 +- .../components/settings/PremiumInfo.svelte | 11 ++- .../src/routes/(root)/(logged)/+layout.svelte | 35 +++++++- 17 files changed, 263 insertions(+), 156 deletions(-) delete mode 100644 backend/.sqlx/query-0156016836adeb2714d99811e4ad2ea9245f05e772f3601c99312c13ed65ae1a.json delete mode 100644 backend/.sqlx/query-03e213d2934991c57af64b5ae94a6aa59599f852cc17b16323fc627b6ad8671e.json create mode 100644 backend/.sqlx/query-124e67b0cee1baa6295846db4ad6242a39dd40186f1dbb48ad3018bd9f6913ec.json delete mode 100644 backend/.sqlx/query-4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25.json create mode 100644 backend/.sqlx/query-ac4a375035049304db7ae084b2d1789afb8250adde6f38f458b115e787ed876f.json delete mode 100644 backend/.sqlx/query-d768bbc46f8a9c4289b918c88ca50aa180b29bbe931d948f6e61976f71b7cdb9.json create mode 100644 backend/migrations/20250827102435_add_team_plan_status.down.sql create mode 100644 backend/migrations/20250827102435_add_team_plan_status.up.sql diff --git a/backend/.sqlx/query-0156016836adeb2714d99811e4ad2ea9245f05e772f3601c99312c13ed65ae1a.json b/backend/.sqlx/query-0156016836adeb2714d99811e4ad2ea9245f05e772f3601c99312c13ed65ae1a.json deleted file mode 100644 index af706cdac9..0000000000 --- a/backend/.sqlx/query-0156016836adeb2714d99811e4ad2ea9245f05e772f3601c99312c13ed65ae1a.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO script\n (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets) \n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets \n\n FROM script WHERE hash = $2 AND workspace_id = $3;\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Int8", - "Text" - ] - }, - "nullable": [] - }, - "hash": "0156016836adeb2714d99811e4ad2ea9245f05e772f3601c99312c13ed65ae1a" -} diff --git a/backend/.sqlx/query-03e213d2934991c57af64b5ae94a6aa59599f852cc17b16323fc627b6ad8671e.json b/backend/.sqlx/query-03e213d2934991c57af64b5ae94a6aa59599f852cc17b16323fc627b6ad8671e.json deleted file mode 100644 index eee4c4d493..0000000000 --- a/backend/.sqlx/query-03e213d2934991c57af64b5ae94a6aa59599f852cc17b16323fc627b6ad8671e.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO script\n (workspace_id, hash, path, parent_hashes, summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets) \n\n SELECT workspace_id, $1, path, array_prepend($2::bigint, COALESCE(parent_hashes, '{}'::bigint[])), summary, description, content, created_by, schema, is_template, extra_perms, lock, language, kind, tag, draft_only, envs, concurrent_limit, concurrency_time_window_s, cache_ttl, dedicated_worker, ws_error_handler_muted, priority, restart_unless_cancelled, delete_after_use, timeout, concurrency_key, visible_to_runner_only, no_main_func, codebase, has_preprocessor, on_behalf_of_email, schema_validation, assets \n\n FROM script WHERE hash = $2 AND workspace_id = $3", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Int8", - "Text" - ] - }, - "nullable": [] - }, - "hash": "03e213d2934991c57af64b5ae94a6aa59599f852cc17b16323fc627b6ad8671e" -} diff --git a/backend/.sqlx/query-124e67b0cee1baa6295846db4ad6242a39dd40186f1dbb48ad3018bd9f6913ec.json b/backend/.sqlx/query-124e67b0cee1baa6295846db4ad6242a39dd40186f1dbb48ad3018bd9f6913ec.json new file mode 100644 index 0000000000..51a1ec68cb --- /dev/null +++ b/backend/.sqlx/query-124e67b0cee1baa6295846db4ad6242a39dd40186f1dbb48ad3018bd9f6913ec.json @@ -0,0 +1,34 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n w.premium,\n COALESCE(cw.is_past_due, false) as \"is_past_due!\",\n cw.max_tolerated_executions\n FROM\n workspace w\n LEFT JOIN cloud_workspace_settings cw ON cw.workspace_id = w.id\n WHERE\n w.id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "premium", + "type_info": "Bool" + }, + { + "ordinal": 1, + "name": "is_past_due!", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "max_tolerated_executions", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + null, + true + ] + }, + "hash": "124e67b0cee1baa6295846db4ad6242a39dd40186f1dbb48ad3018bd9f6913ec" +} diff --git a/backend/.sqlx/query-4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25.json b/backend/.sqlx/query-4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25.json deleted file mode 100644 index ab5b04afc6..0000000000 --- a/backend/.sqlx/query-4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT owner, premium, usage.usage as \"usage?\", workspace_settings.customer_id, workspace_settings.plan FROM workspace LEFT JOIN workspace_settings ON workspace_settings.workspace_id = $1 LEFT JOIN usage ON usage.id = $1 AND month_ = EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date) AND usage.is_workspace IS true WHERE workspace.id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "owner", - "type_info": "Varchar" - }, - { - "ordinal": 1, - "name": "premium", - "type_info": "Bool" - }, - { - "ordinal": 2, - "name": "usage?", - "type_info": "Int4" - }, - { - "ordinal": 3, - "name": "customer_id", - "type_info": "Varchar" - }, - { - "ordinal": 4, - "name": "plan", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - false, - true, - true - ] - }, - "hash": "4b8132b04e454eddfe6724c6cc3a2e60c9c24decb2a6b41125247bbf741e9c25" -} diff --git a/backend/.sqlx/query-ac4a375035049304db7ae084b2d1789afb8250adde6f38f458b115e787ed876f.json b/backend/.sqlx/query-ac4a375035049304db7ae084b2d1789afb8250adde6f38f458b115e787ed876f.json new file mode 100644 index 0000000000..9e3e7cb863 --- /dev/null +++ b/backend/.sqlx/query-ac4a375035049304db7ae084b2d1789afb8250adde6f38f458b115e787ed876f.json @@ -0,0 +1,58 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT \n owner, \n premium, \n usage.usage as \"usage?\", \n workspace_settings.customer_id, \n workspace_settings.plan, \n COALESCE(cw.is_past_due, false) as \"is_past_due!\", \n cw.max_tolerated_executions\n FROM workspace\n LEFT JOIN workspace_settings \n ON workspace_settings.workspace_id = $1\n LEFT JOIN usage \n ON usage.id = $1\n AND month_ = EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date)\n AND usage.is_workspace IS true\n LEFT JOIN cloud_workspace_settings cw\n ON cw.workspace_id = $1\n WHERE workspace.id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "owner", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "premium", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "usage?", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "customer_id", + "type_info": "Varchar" + }, + { + "ordinal": 4, + "name": "plan", + "type_info": "Varchar" + }, + { + "ordinal": 5, + "name": "is_past_due!", + "type_info": "Bool" + }, + { + "ordinal": 6, + "name": "max_tolerated_executions", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + true, + true, + null, + true + ] + }, + "hash": "ac4a375035049304db7ae084b2d1789afb8250adde6f38f458b115e787ed876f" +} diff --git a/backend/.sqlx/query-d768bbc46f8a9c4289b918c88ca50aa180b29bbe931d948f6e61976f71b7cdb9.json b/backend/.sqlx/query-d768bbc46f8a9c4289b918c88ca50aa180b29bbe931d948f6e61976f71b7cdb9.json deleted file mode 100644 index 22b38a5217..0000000000 --- a/backend/.sqlx/query-d768bbc46f8a9c4289b918c88ca50aa180b29bbe931d948f6e61976f71b7cdb9.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT premium FROM workspace WHERE id = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "premium", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false - ] - }, - "hash": "d768bbc46f8a9c4289b918c88ca50aa180b29bbe931d948f6e61976f71b7cdb9" -} diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index 111a3f2d18..dfa2a01850 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -6396854336ae27fb14ccb792d80c31ff614b2afa \ No newline at end of file +d16e52d570f10dfdabb04a9061fe7ebdbf5a2aa1 \ No newline at end of file diff --git a/backend/migrations/20250827102435_add_team_plan_status.down.sql b/backend/migrations/20250827102435_add_team_plan_status.down.sql new file mode 100644 index 0000000000..980b70d425 --- /dev/null +++ b/backend/migrations/20250827102435_add_team_plan_status.down.sql @@ -0,0 +1,7 @@ +-- Add down migration script here +DROP FUNCTION notify_team_plan_status_change; +DROP TRIGGER notify_team_plan_status_change ON cloud_workspace_settings; + +ALTER TABLE cloud_workspace_settings + DROP COLUMN is_past_due, + DROP COLUMN max_tolerated_executions; \ No newline at end of file diff --git a/backend/migrations/20250827102435_add_team_plan_status.up.sql b/backend/migrations/20250827102435_add_team_plan_status.up.sql new file mode 100644 index 0000000000..1b2007bfe7 --- /dev/null +++ b/backend/migrations/20250827102435_add_team_plan_status.up.sql @@ -0,0 +1,18 @@ +-- Add up migration script here +ALTER TABLE cloud_workspace_settings + ADD COLUMN is_past_due BOOLEAN NOT NULL DEFAULT FALSE, + ADD COLUMN max_tolerated_executions INTEGER; + +CREATE OR REPLACE FUNCTION notify_team_plan_status_change() +RETURNS TRIGGER AS $$ +BEGIN + PERFORM pg_notify('notify_workspace_premium_change', NEW.workspace_id); -- reuse the same channel as the one used for workspace premium change => clear cache + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + + +CREATE TRIGGER team_plan_status_change_trigger +AFTER UPDATE OF is_past_due, max_tolerated_executions ON cloud_workspace_settings +FOR EACH ROW +EXECUTE FUNCTION notify_team_plan_status_change(); \ No newline at end of file diff --git a/backend/src/main.rs b/backend/src/main.rs index 9b80e58eef..be0b86ec46 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -857,7 +857,7 @@ Windmill Community Edition {GIT_VERSION} "notify_workspace_premium_change" => { let workspace_id = n.payload(); tracing::info!("Workspace premium change detected, invalidating workspace premium cache: {}", workspace_id); - windmill_common::workspaces::IS_PREMIUM_CACHE.remove(workspace_id); + windmill_common::workspaces::TEAM_PLAN_CACHE.remove(workspace_id); }, "notify_runnable_version_change" => { let payload = n.payload(); diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index c2471cebfb..b0afbfb217 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -2034,6 +2034,11 @@ paths: - workspace parameters: - $ref: "#/components/parameters/WorkspaceId" + - name: skip_subscription_fetch + in: query + description: skip fetching subscription status from stripe + schema: + type: boolean responses: "200": @@ -2051,9 +2056,14 @@ paths: type: string status: type: string + is_past_due: + type: boolean + max_tolerated_executions: + type: number required: - premium - owner + - is_past_due /w/{workspace}/workspaces/threshold_alert: get: diff --git a/backend/windmill-api/src/workspaces.rs b/backend/windmill-api/src/workspaces.rs index 78052b8ed8..9e3c7dc25e 100644 --- a/backend/windmill-api/src/workspaces.rs +++ b/backend/windmill-api/src/workspaces.rs @@ -415,7 +415,9 @@ async fn is_premium( ) -> JsonResult { require_admin(authed.is_admin, &authed.username)?; #[cfg(feature = "cloud")] - let premium = windmill_common::workspaces::is_premium_workspace(&_db, &_w_id).await; + let premium = windmill_common::workspaces::get_team_plan_status(&_db, &_w_id) + .await + .premium; #[cfg(not(feature = "cloud"))] let premium = false; Ok(Json(premium)) diff --git a/backend/windmill-common/src/workspaces.rs b/backend/windmill-common/src/workspaces.rs index 5d7d3d9967..c120e3c39a 100644 --- a/backend/windmill-common/src/workspaces.rs +++ b/backend/windmill-common/src/workspaces.rs @@ -86,22 +86,47 @@ impl Default for GitSyncSettings { } } +#[derive(Clone)] +pub struct TeamPlanStatus { + pub premium: bool, + pub is_past_due: bool, + pub max_tolerated_executions: Option, +} + lazy_static::lazy_static! { - pub static ref IS_PREMIUM_CACHE: Cache = Cache::new(5000); + pub static ref TEAM_PLAN_CACHE: Cache = Cache::new(5000); } #[cfg(feature = "cloud")] -pub async fn is_premium_workspace(_db: &crate::DB, _w_id: &str) -> bool { - let cached = IS_PREMIUM_CACHE.get(_w_id); +pub async fn get_team_plan_status(_db: &crate::DB, _w_id: &str) -> TeamPlanStatus { + let cached = TEAM_PLAN_CACHE.get(_w_id); if let Some(cached) = cached { return cached; } - let premium = sqlx::query_scalar!("SELECT premium FROM workspace WHERE id = $1", _w_id) - .fetch_one(_db) - .await - .unwrap_or(false); - IS_PREMIUM_CACHE.insert(_w_id.to_string(), premium); - premium + let team_plan_info = sqlx::query_as!( + TeamPlanStatus, + r#" + SELECT + w.premium, + COALESCE(cw.is_past_due, false) as "is_past_due!", + cw.max_tolerated_executions + FROM + workspace w + LEFT JOIN cloud_workspace_settings cw ON cw.workspace_id = w.id + WHERE + w.id = $1 + "#, + _w_id + ) + .fetch_one(_db) + .await + .unwrap_or_else(|_| TeamPlanStatus { + premium: false, + is_past_due: false, + max_tolerated_executions: None, + }); + TEAM_PLAN_CACHE.insert(_w_id.to_string(), team_plan_info.clone()); + team_plan_info } #[derive(Deserialize, Serialize, Debug)] diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 09b2a39695..3667239119 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -1419,8 +1419,9 @@ fn apply_completed_job_cloud_usage( let email2 = email.clone(); tokio::task::spawn(async move { let additional_usage = _duration / 1000; - let premium_workspace = - windmill_common::workspaces::is_premium_workspace(&db, &w_id).await; + let premium_workspace = windmill_common::workspaces::get_team_plan_status(&db, &w_id) + .await + .premium; tokio::time::timeout(std::time::Duration::from_secs(10), async move { let _ = sqlx::query!( "INSERT INTO usage (id, is_workspace, month_, usage) @@ -3636,8 +3637,8 @@ pub async fn push<'c, 'd>( ) -> Result<(Uuid, Transaction<'c, Postgres>), Error> { #[cfg(feature = "cloud")] if *CLOUD_HOSTED { - let premium_workspace = - windmill_common::workspaces::is_premium_workspace(_db, workspace_id).await; + let team_plan_status = + windmill_common::workspaces::get_team_plan_status(_db, workspace_id).await; // we track only non flow steps let (workspace_usage, user_usage) = if !matches!( job_payload, @@ -3655,7 +3656,7 @@ pub async fn push<'c, 'd>( .await .map_err(|e| Error::internal_err(format!("updating usage: {e:#}")))?; - let user_usage = if !premium_workspace { + let user_usage = if !team_plan_status.premium { Some(sqlx::query_scalar!( "INSERT INTO usage (id, is_workspace, month_, usage) VALUES ($1, FALSE, EXTRACT(YEAR FROM current_date) * 12 + EXTRACT(MONTH FROM current_date), 1) @@ -3678,7 +3679,7 @@ pub async fn push<'c, 'd>( Ok((None, None)) }?; - if !premium_workspace { + if !team_plan_status.premium || team_plan_status.is_past_due { let is_super_admin = sqlx::query_scalar!("SELECT super_admin FROM password WHERE email = $1", email) .fetch_optional(_db) @@ -3686,7 +3687,8 @@ pub async fn push<'c, 'd>( .unwrap_or(false); if !is_super_admin { - if email != ERROR_HANDLER_USER_EMAIL + if !team_plan_status.premium + && email != ERROR_HANDLER_USER_EMAIL && email != SCHEDULE_ERROR_HANDLER_USER_EMAIL && email != SCHEDULE_RECOVERY_HANDLER_USER_EMAIL && email != "worker@windmill.dev" @@ -3765,43 +3767,53 @@ pub async fn push<'c, 'd>( .flatten() .unwrap_or(1) }; + if team_plan_status.premium { + // team plan is premium but past due, we check if the workspace has exceeded the max tolerated executions + if team_plan_status.max_tolerated_executions.is_none() + || workspace_usage > team_plan_status.max_tolerated_executions.unwrap() + { + return Err(error::Error::QuotaExceeded(format!( + "Workspace {workspace_id} team plan is past due and isn't allowed to run any more jobs. Please fix your payment method in the workspace settings." + ))); + } + } else { + if workspace_usage > MAX_FREE_EXECS + && !matches!(job_payload, JobPayload::Dependencies { .. }) + && !matches!(job_payload, JobPayload::FlowDependencies { .. }) + && !matches!(job_payload, JobPayload::AppDependencies { .. }) + { + return Err(error::Error::QuotaExceeded(format!( + "Workspace {workspace_id} has exceeded the free usage limit of {MAX_FREE_EXECS} that applies outside of premium workspaces." + ))); + } - if workspace_usage > MAX_FREE_EXECS - && !matches!(job_payload, JobPayload::Dependencies { .. }) - && !matches!(job_payload, JobPayload::FlowDependencies { .. }) - && !matches!(job_payload, JobPayload::AppDependencies { .. }) - { - return Err(error::Error::QuotaExceeded(format!( - "Workspace {workspace_id} has exceeded the free usage limit of {MAX_FREE_EXECS} that applies outside of premium workspaces." - ))); - } + let in_queue_workspace = sqlx::query_scalar!( + "SELECT COUNT(id) FROM v2_job_queue WHERE workspace_id = $1", + workspace_id + ) + .fetch_one(_db) + .await? + .unwrap_or(0); - let in_queue_workspace = sqlx::query_scalar!( - "SELECT COUNT(id) FROM v2_job_queue WHERE workspace_id = $1", - workspace_id - ) - .fetch_one(_db) - .await? - .unwrap_or(0); + if in_queue_workspace > MAX_FREE_EXECS as i64 { + return Err(error::Error::QuotaExceeded(format!( + "Workspace {workspace_id} has exceeded the jobs in queue limit of {MAX_FREE_EXECS} that applies outside of premium workspaces." + ))); + } - if in_queue_workspace > MAX_FREE_EXECS as i64 { - return Err(error::Error::QuotaExceeded(format!( - "Workspace {workspace_id} has exceeded the jobs in queue limit of {MAX_FREE_EXECS} that applies outside of premium workspaces." - ))); - } - - let concurrent_runs_workspace = sqlx::query_scalar!( + let concurrent_runs_workspace = sqlx::query_scalar!( "SELECT COUNT(id) FROM v2_job_queue WHERE running = true AND workspace_id = $1", - workspace_id - ) - .fetch_one(_db) - .await? - .unwrap_or(0); + workspace_id + ) + .fetch_one(_db) + .await? + .unwrap_or(0); - if concurrent_runs_workspace > MAX_FREE_CONCURRENT_RUNS as i64 { - return Err(error::Error::QuotaExceeded(format!( - "Workspace {workspace_id} has exceeded the concurrent runs limit of {MAX_FREE_CONCURRENT_RUNS} that applies outside of premium workspaces." - ))); + if concurrent_runs_workspace > MAX_FREE_CONCURRENT_RUNS as i64 { + return Err(error::Error::QuotaExceeded(format!( + "Workspace {workspace_id} has exceeded the concurrent runs limit of {MAX_FREE_CONCURRENT_RUNS} that applies outside of premium workspaces." + ))); + } } } } diff --git a/backend/windmill-worker/src/common.rs b/backend/windmill-worker/src/common.rs index 2b7523ecad..11718f3cf5 100644 --- a/backend/windmill-worker/src/common.rs +++ b/backend/windmill-worker/src/common.rs @@ -680,11 +680,12 @@ pub async fn resolve_job_timeout( let mut warn_msg: Option = None; #[cfg(feature = "cloud")] let cloud_premium_workspace = *CLOUD_HOSTED - && windmill_common::workspaces::is_premium_workspace( + && windmill_common::workspaces::get_team_plan_status( _conn.as_sql().expect("cloud cannot use http connection"), _w_id, ) - .await; + .await + .premium; #[cfg(not(feature = "cloud"))] let cloud_premium_workspace = false; diff --git a/frontend/src/lib/components/settings/PremiumInfo.svelte b/frontend/src/lib/components/settings/PremiumInfo.svelte index 1ed4b2fb12..bd7a91a026 100644 --- a/frontend/src/lib/components/settings/PremiumInfo.svelte +++ b/frontend/src/lib/components/settings/PremiumInfo.svelte @@ -30,6 +30,8 @@ seatsFromExtraComps: number usedSeats: number owner: string + is_past_due: boolean + max_tolerated_executions?: number } | undefined = undefined const plans = { @@ -157,8 +159,13 @@ {#if premiumInfo?.status === 'past_due'}

- Your last invoice is unpaid. Please update your payment method in the customer portal to - prevent account downgrade and the interruption of your job executions. + {#if premiumInfo.max_tolerated_executions === undefined || premiumInfo.usage > premiumInfo.max_tolerated_executions} + Your last invoice is unpaid, you cannot run any more jobs. Please update your payment + method in the customer portal to continue running jobs. + {:else} + Your last invoice is unpaid. Please update your payment method in the customer portal to + prevent the interruption of your job executions. + {/if}

{/if} diff --git a/frontend/src/routes/(root)/(logged)/+layout.svelte b/frontend/src/routes/(root)/(logged)/+layout.svelte index 0969a4df3f..d03689e3d1 100644 --- a/frontend/src/routes/(root)/(logged)/+layout.svelte +++ b/frontend/src/routes/(root)/(logged)/+layout.svelte @@ -11,7 +11,7 @@ UserService, WorkspaceService } from '$lib/gen' - import { capitalize, classNames, getModifierKey } from '$lib/utils' + import { capitalize, classNames, getModifierKey, sendUserToast } from '$lib/utils' import WorkspaceMenu from '$lib/components/sidebar/WorkspaceMenu.svelte' import SidebarContent from '$lib/components/sidebar/SidebarContent.svelte' import CriticalAlertModal from '$lib/components/sidebar/CriticalAlertModal.svelte' @@ -305,6 +305,30 @@ muteSettings = { global: g_muted, workspace: ws_muted } } + + async function checkTeamPlanStatus(workspace: string) { + const premiumInfo = await WorkspaceService.getPremiumInfo({ + workspace, + skipSubscriptionFetch: true // won't load subscription status from stripe but only the past due status from db + }) + if (premiumInfo.is_past_due) { + if ( + premiumInfo.max_tolerated_executions === undefined || + (premiumInfo.usage ?? 0) > premiumInfo.max_tolerated_executions + ) { + sendUserToast( + 'Your last invoice is unpaid, you cannot run any more jobs. Please update your payment method in the workspace settings to continue running jobs.', + true + ) + } else { + sendUserToast( + 'Your last invoice is unpaid. Please update your payment method in the workspace settings to prevent the interruption of your job executions.', + true + ) + } + } + } + $effect(() => { $page.url && userSettings != undefined && untrack(() => onQueryChangeUserSettings()) }) @@ -350,6 +374,15 @@ mountModal = false } }) + + $effect(() => { + if (isCloudHosted()) { + const workspace = $workspaceStore + if (workspace) { + checkTeamPlanStatus(workspace) + } + } + }) From 4973c860f2c28d9bdc2af94530d90eb177234e5d Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 28 Aug 2025 17:44:57 +0000 Subject: [PATCH 02/22] fix: fix workflow as code behavior with multithread --- python-client/wmill/wmill/client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python-client/wmill/wmill/client.py b/python-client/wmill/wmill/client.py index f24f2a35ee..36e7f7dc9a 100644 --- a/python-client/wmill/wmill/client.py +++ b/python-client/wmill/wmill/client.py @@ -1478,16 +1478,16 @@ def task(*args, **kwargs): params = {} if tag is not None: params["tag"] = tag - r = _client.post( + w_as_code_response = _client.post( f"/w/{w_id}/jobs/run/workflow_as_code/{job_id}/{f_name}", json={"args": json}, params=params, ) - job_id = r.text + job_id = w_as_code_response.text print(f"Executing task {func.__name__} on job {job_id}") - r = _client.wait_job(job_id) + job_result = _client.wait_job(job_id) print(f"Task {func.__name__} ({job_id}) completed") - return r + return job_result return inner From 6f4bdc0148fee335db5ea1d8912e53f22eeffee3 Mon Sep 17 00:00:00 2001 From: BaptisteMoureaux <43876576+BaptisteMoureaux@users.noreply.github.com> Date: Thu, 28 Aug 2025 20:06:36 +0200 Subject: [PATCH 03/22] bump lsp go version & add private packages go support (#6484) --- lsp/Dockerfile | 10 +++++----- lsp/pyls_launcher.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lsp/Dockerfile b/lsp/Dockerfile index df34a4549a..de599e45ec 100644 --- a/lsp/Dockerfile +++ b/lsp/Dockerfile @@ -9,7 +9,7 @@ ENV PIPENV_VENV_IN_PROJECT=1 ENV XDG_CACHE_HOME=/pyls/.cache RUN apt-get update \ - && apt-get install -y shellcheck wget \ + && apt-get install -y shellcheck wget git ca-certificates \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* \ && pip install pipenv @@ -21,13 +21,13 @@ RUN set -eux; \ url=; \ case "$arch" in \ 'amd64') \ - targz='go1.22.5.linux-amd64.tar.gz'; \ + targz='go1.25.0.linux-amd64.tar.gz'; \ ;; \ 'arm64') \ - targz='go1.22.5.linux-arm64.tar.gz'; \ + targz='go1.25.0.linux-arm64.tar.gz'; \ ;; \ 'armhf') \ - targz='go1.22.5.linux-armv6l.tar.gz'; \ + targz='go1.25.0.linux-armv6l.tar.gz'; \ ;; \ *) echo >&2 "error: unsupported architecture '$arch' (likely packaging update needed)"; exit 1 ;; \ esac; \ @@ -59,4 +59,4 @@ RUN chmod -R a+rX /usr/local && \ EXPOSE 3001 -CMD ["sh", "-c", "if [ -d /root/.cache ]; then export XDG_CACHE_HOME=/root/.cache && cp -r /pyls/.cache /root/.cache; fi && python3 pyls_launcher.py"] +CMD ["sh", "-c", "if [ -n \"$NETRC\" ]; then echo \"$NETRC\" > /root/.netrc && chmod 600 /root/.netrc; fi && if [ -d /root/.cache ]; then export XDG_CACHE_HOME=/root/.cache && cp -r /pyls/.cache /root/.cache; fi && python3 pyls_launcher.py"] diff --git a/lsp/pyls_launcher.py b/lsp/pyls_launcher.py index 614d47294e..8b19ca5ef5 100644 --- a/lsp/pyls_launcher.py +++ b/lsp/pyls_launcher.py @@ -97,7 +97,7 @@ if __name__ == "__main__": go_mod_path = os.path.join(monaco_path, "go.mod") if not os.path.exists(go_mod_path): f = open(go_mod_path, "w") - f.write("module mymod\ngo 1.22") + f.write("module mymod\ngo 1.25") f.close() port = int(os.environ.get("PORT", "3001")) app = web.Application( From 7a1c28f6d7ca177351187d53bc40e5225a862ffc Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 28 Aug 2025 20:29:04 +0100 Subject: [PATCH 04/22] chore(main): release 1.537.0 (#6486) * chore(main): release 1.537.0 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 15 +++ backend/Cargo.lock | 91 +++++++++---------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/src/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 76 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be301a9f36..6a9df670c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [1.537.0](https://github.com/windmill-labs/windmill/compare/v1.536.0...v1.537.0) (2025-08-28) + + +### Features + +* **aichat:** allow reverting specific line for inline script suggestions ([#6480](https://github.com/windmill-labs/windmill/issues/6480)) ([0cc11b3](https://github.com/windmill-labs/windmill/commit/0cc11b3f31aeee60a9d4a231cea5d4285d7ab37e)) +* autovacuum or high intensity tables ([4ad0d25](https://github.com/windmill-labs/windmill/commit/4ad0d255f3eea303e97eab5325f89930b26f9e52)) + + +### Bug Fixes + +* fix okta and oauth0 sso settings ([73a3f4c](https://github.com/windmill-labs/windmill/commit/73a3f4cc73271759650e9246f4eb2e0efb7c7e37)) +* fix relative imports cache invalidation ([#6468](https://github.com/windmill-labs/windmill/issues/6468)) ([006f326](https://github.com/windmill-labs/windmill/commit/006f32602c7609b282f15135989c5f164c109c1c)) +* fix workflow as code behavior with multithread ([4973c86](https://github.com/windmill-labs/windmill/commit/4973c860f2c28d9bdc2af94530d90eb177234e5d)) + ## [1.536.0](https://github.com/windmill-labs/windmill/compare/v1.535.0...v1.536.0) (2025-08-27) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 7b7bc45206..669bd8efbb 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -5863,7 +5863,7 @@ dependencies = [ "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.3+wasi-0.2.4", "wasm-bindgen", ] @@ -10253,9 +10253,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases 0.2.1", @@ -10264,7 +10264,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls 0.23.29", - "socket2 0.5.10", + "socket2 0.6.0", "thiserror 2.0.16", "tokio", "tracing", @@ -10273,9 +10273,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", "getrandom 0.3.3", @@ -10294,16 +10294,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.0", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -14770,11 +14770,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -15129,7 +15129,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "axum", @@ -15183,7 +15183,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "argon2", @@ -15300,7 +15300,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.536.0" +version = "1.537.0" dependencies = [ "base64 0.22.1", "chrono", @@ -15315,7 +15315,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.536.0" +version = "1.537.0" dependencies = [ "chrono", "serde", @@ -15328,7 +15328,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "axum", @@ -15347,7 +15347,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "async-recursion", @@ -15427,7 +15427,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.536.0" +version = "1.537.0" dependencies = [ "regex", "serde", @@ -15442,7 +15442,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "bytes", @@ -15466,7 +15466,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.536.0" +version = "1.537.0" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -15478,7 +15478,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.536.0" +version = "1.537.0" dependencies = [ "convert_case 0.6.0", "serde", @@ -15487,7 +15487,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "lazy_static", @@ -15499,7 +15499,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "serde_json", @@ -15511,7 +15511,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "gosyn", @@ -15523,7 +15523,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "lazy_static", @@ -15535,7 +15535,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "serde_json", @@ -15547,7 +15547,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "nu-parser", @@ -15558,7 +15558,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15569,7 +15569,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15581,7 +15581,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "async-recursion", @@ -15604,7 +15604,7 @@ dependencies = [ [[package]] name = "windmill-parser-ruby" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "lazy_static", @@ -15618,7 +15618,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -15635,7 +15635,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "lazy_static", @@ -15649,7 +15649,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "lazy_static", @@ -15667,7 +15667,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -15692,7 +15692,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "serde_json", @@ -15702,7 +15702,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "async-recursion", @@ -15735,7 +15735,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.536.0" +version = "1.537.0" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -15745,7 +15745,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.536.0" +version = "1.537.0" dependencies = [ "anyhow", "async-recursion", @@ -16325,13 +16325,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.3", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "writeable" diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 3f770576bf..69e5d4333e 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.536.0" +version = "1.537.0" authors.workspace = true edition.workspace = true @@ -33,7 +33,7 @@ members = [ ] [workspace.package] -version = "1.536.0" +version = "1.537.0" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index b0afbfb217..eead7304a1 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.536.0 + version: 1.537.0 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index cdebe91692..dd145efd45 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts"; import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts"; -export const VERSION = "v1.536.0"; +export const VERSION = "v1.537.0"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/src/main.ts b/cli/src/main.ts index 8f98e43c0c..743ad64c75 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -68,7 +68,7 @@ export { // } // }); -export const VERSION = "1.536.0"; +export const VERSION = "1.537.0"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6401b0b925..144e871b1c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.536.0", + "version": "1.537.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.536.0", + "version": "1.537.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 1f0841eea9..f98f5789ab 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.536.0", + "version": "1.537.0", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 5fe27bbf38..001b2c6440 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.536.0" -wmill_pg = ">=1.536.0" +wmill = ">=1.537.0" +wmill_pg = ">=1.537.0" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index 653590ef27..aec8eecbc7 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.536.0 + version: 1.537.0 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index 68a5fd2cc3..114e9b0818 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.536.0' + ModuleVersion = '1.537.0' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 459257aee7..73633134e4 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.536.0" +version = "1.537.0" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index a515d6d8ae..ee75201d9b 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.536.0" +version = "1.537.0" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 49fd1c2814..3126b9087e 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.536.0", + "version": "1.537.0", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index 6018f1935e..674c770bc6 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.536.0", + "version": "1.537.0", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index 3fe765ee77..a2da630536 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.536.0 +1.537.0 From e28c9df60f20b70a4dcef802839750bb843b91a4 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 29 Aug 2025 06:57:01 +0000 Subject: [PATCH 05/22] fix: fix preprocessor not displaying immediately on addition --- frontend/src/lib/components/Editor.svelte | 14 ++++++++------ .../src/lib/components/graph/FlowGraphV2.svelte | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/frontend/src/lib/components/Editor.svelte b/frontend/src/lib/components/Editor.svelte index 4bbdeb0d71..d421920951 100644 --- a/frontend/src/lib/components/Editor.svelte +++ b/frontend/src/lib/components/Editor.svelte @@ -544,9 +544,7 @@ let sqlSchemaCompletor: IDisposable | undefined = undefined - async function updateSchema() { - const newSchemaRes = lang === 'graphql' ? args?.api : args?.database - + async function updateSchema(newSchemaRes: string | undefined) { if (typeof newSchemaRes === 'string') { const resourcePath = newSchemaRes.replace('$res:', '') dbSchema = $dbSchemas[resourcePath] @@ -1621,10 +1619,14 @@ ? untrack(() => addSqlTypeCompletions()) : sqlTypeCompletor?.dispose() }) + + let lastArg = undefined $effect(() => { - console.log('updating schema', lang, $dbSchemas) - readFieldsRecursively(args) - lang && $dbSchemas && untrack(() => updateSchema()) + let newArg = lang === 'graphql' ? args?.api : args?.database + if (newArg !== lastArg) { + lastArg = newArg + $dbSchemas && untrack(() => updateSchema(newArg)) + } }) $effect(() => { console.log('updating db schema completions', dbSchema, lang) diff --git a/frontend/src/lib/components/graph/FlowGraphV2.svelte b/frontend/src/lib/components/graph/FlowGraphV2.svelte index 68ea11ed58..e8a5ee548d 100644 --- a/frontend/src/lib/components/graph/FlowGraphV2.svelte +++ b/frontend/src/lib/components/graph/FlowGraphV2.svelte @@ -487,7 +487,7 @@ additionalAssetsMap: flowGraphAssetsCtx?.val.additionalAssetsMap }, untrack(() => failureModule), - untrack(() => preprocessorModule), + preprocessorModule, eventHandler, success, $useDataflow, From fb25e413f80ab30993ffb233d90d18cf182167b5 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 29 Aug 2025 07:06:17 +0000 Subject: [PATCH 06/22] nit check --- frontend/src/lib/components/Editor.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/Editor.svelte b/frontend/src/lib/components/Editor.svelte index d421920951..7ed6954710 100644 --- a/frontend/src/lib/components/Editor.svelte +++ b/frontend/src/lib/components/Editor.svelte @@ -150,7 +150,7 @@ import { writable } from 'svelte/store' import { formatResourceTypes } from './copilot/chat/script/core' import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte' - import { editorPositionMap, readFieldsRecursively } from '$lib/utils' + import { editorPositionMap } from '$lib/utils' import { extToLang, langToExt } from '$lib/editorLangUtils' import { aiChatManager } from './copilot/chat/AIChatManager.svelte' import type { Selection } from 'monaco-editor' From e2b344ed02b02972366a011c3e873e0f43ab2843 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 29 Aug 2025 07:24:47 +0000 Subject: [PATCH 07/22] fix: skipPreprocessor on re-running job immedaitely from UI --- .../src/routes/(root)/(logged)/run/[...run]/+page.svelte | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte b/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte index 94c2d380b6..cb42658b10 100644 --- a/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte @@ -355,12 +355,14 @@ if (job?.job_kind == 'script') { id = await JobService.runScriptByHash({ ...commonArgs, - hash: job.script_hash! + hash: job.script_hash!, + skipPreprocessor: true }) } else { id = await JobService.runFlowByPath({ ...commonArgs, - path: job.script_path! + path: job.script_path!, + skipPreprocessor: true }) } From 641d5651c5b6fa898e4fc8ad29a8508f9400c6d8 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 29 Aug 2025 09:38:25 +0000 Subject: [PATCH 08/22] fix: fix error handling of pre-processor steps --- backend/windmill-worker/src/js_eval.rs | 2 +- backend/windmill-worker/src/worker_flow.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/windmill-worker/src/js_eval.rs b/backend/windmill-worker/src/js_eval.rs index d5bf2add3b..8b0b2119b5 100644 --- a/backend/windmill-worker/src/js_eval.rs +++ b/backend/windmill-worker/src/js_eval.rs @@ -520,7 +520,7 @@ function get_from_env(name) {{ .map(|a| { format!("let {a} = get_from_env(\"{a}\");\n",) }) .join(""), if expr.contains("error") && transform_context.contains(&"previous_result".to_string()) { - "let error = previous_result.error" + "let error = previous_result.error;" } else { "" }, diff --git a/backend/windmill-worker/src/worker_flow.rs b/backend/windmill-worker/src/worker_flow.rs index 37c6e3fc70..f4874f6456 100644 --- a/backend/windmill-worker/src/worker_flow.rs +++ b/backend/windmill-worker/src/worker_flow.rs @@ -1027,7 +1027,7 @@ pub async fn update_flow_status_after_job_completion_internal( .ok_or_else(|| Error::internal_err(format!("requiring flow to be in the queue")))?; tx.commit().await?; - if matches!(module_step, Step::PreprocessorStep) { + if matches!(module_step, Step::PreprocessorStep) && success { let tag_and_concurrency_key = get_tag_and_concurrency(&flow, db).await; let require_args = tag_and_concurrency_key.as_ref().is_some_and(|x| { x.tag.as_ref().is_some_and(|t| t.contains("$args")) @@ -1144,9 +1144,7 @@ pub async fn update_flow_status_after_job_completion_internal( "error while updating args in preprocessing step: {e:#}" )) })?; - if success { - return Ok(UpdateFlowStatusAfterJobCompletion::PreprocessingStep); - } + return Ok(UpdateFlowStatusAfterJobCompletion::PreprocessingStep); } let job_root = flow_job From 89e20f51972cfbcfc6e4f6160b2ed82ce0fd4ad4 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 29 Aug 2025 09:43:00 +0000 Subject: [PATCH 09/22] chore(main): release 1.537.1 (#6491) * chore(main): release 1.537.1 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 9 +++ backend/Cargo.lock | 68 +++++++++---------- backend/Cargo.toml | 4 +- backend/windmill-api/openapi.yaml | 2 +- benchmarks/lib.ts | 2 +- cli/src/main.ts | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- lsp/Pipfile | 4 +- openflow.openapi.yaml | 2 +- .../WindmillClient/WindmillClient.psd1 | 2 +- python-client/wmill/pyproject.toml | 2 +- python-client/wmill_pg/pyproject.toml | 2 +- typescript-client/jsr.json | 2 +- typescript-client/package.json | 2 +- version.txt | 2 +- 16 files changed, 60 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a9df670c1..89fe783df1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [1.537.1](https://github.com/windmill-labs/windmill/compare/v1.537.0...v1.537.1) (2025-08-29) + + +### Bug Fixes + +* fix error handling of pre-processor steps ([641d565](https://github.com/windmill-labs/windmill/commit/641d5651c5b6fa898e4fc8ad29a8508f9400c6d8)) +* fix preprocessor not displaying immediately on addition ([e28c9df](https://github.com/windmill-labs/windmill/commit/e28c9df60f20b70a4dcef802839750bb843b91a4)) +* skipPreprocessor on re-running job immedaitely from UI ([e2b344e](https://github.com/windmill-labs/windmill/commit/e2b344ed02b02972366a011c3e873e0f43ab2843)) + ## [1.537.0](https://github.com/windmill-labs/windmill/compare/v1.536.0...v1.537.0) (2025-08-28) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 669bd8efbb..1c7d202114 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1018,9 +1018,9 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.61.4" +version = "0.61.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a16e040799d29c17412943bdbf488fd75db04112d0c0d4b9290bacf5ae0014b9" +checksum = "eaa31b350998e703e9826b2104dd6f63be0508666e1aba88137af060e8944047" dependencies = [ "aws-smithy-types", ] @@ -2105,9 +2105,9 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "comfy-table" -version = "7.1.4" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" +checksum = "3f8e18d0dca9578507f13f9803add0df13362b02c501c1c17734f0dbb52eaf0b" dependencies = [ "unicode-segmentation", "unicode-width 0.2.1", @@ -9877,9 +9877,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -15129,7 +15129,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "axum", @@ -15183,7 +15183,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "argon2", @@ -15300,7 +15300,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.537.0" +version = "1.537.1" dependencies = [ "base64 0.22.1", "chrono", @@ -15315,7 +15315,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.537.0" +version = "1.537.1" dependencies = [ "chrono", "serde", @@ -15328,7 +15328,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "axum", @@ -15347,7 +15347,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "async-recursion", @@ -15427,7 +15427,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.537.0" +version = "1.537.1" dependencies = [ "regex", "serde", @@ -15442,7 +15442,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "bytes", @@ -15466,7 +15466,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.537.0" +version = "1.537.1" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -15478,7 +15478,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.537.0" +version = "1.537.1" dependencies = [ "convert_case 0.6.0", "serde", @@ -15487,7 +15487,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "lazy_static", @@ -15499,7 +15499,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "serde_json", @@ -15511,7 +15511,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "gosyn", @@ -15523,7 +15523,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "lazy_static", @@ -15535,7 +15535,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "serde_json", @@ -15547,7 +15547,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "nu-parser", @@ -15558,7 +15558,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15569,7 +15569,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15581,7 +15581,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "async-recursion", @@ -15604,7 +15604,7 @@ dependencies = [ [[package]] name = "windmill-parser-ruby" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "lazy_static", @@ -15618,7 +15618,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -15635,7 +15635,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "lazy_static", @@ -15649,7 +15649,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "lazy_static", @@ -15667,7 +15667,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -15692,7 +15692,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "serde_json", @@ -15702,7 +15702,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "async-recursion", @@ -15735,7 +15735,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.537.0" +version = "1.537.1" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -15745,7 +15745,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.537.0" +version = "1.537.1" dependencies = [ "anyhow", "async-recursion", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 69e5d4333e..fce0ea353a 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.537.0" +version = "1.537.1" authors.workspace = true edition.workspace = true @@ -33,7 +33,7 @@ members = [ ] [workspace.package] -version = "1.537.0" +version = "1.537.1" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index eead7304a1..f788be50d2 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.537.0 + version: 1.537.1 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index dd145efd45..bab3c7951b 100644 --- a/benchmarks/lib.ts +++ b/benchmarks/lib.ts @@ -2,7 +2,7 @@ import { sleep } from "https://deno.land/x/sleep@v1.2.1/mod.ts"; import * as windmill from "https://deno.land/x/windmill@v1.174.0/mod.ts"; import * as api from "https://deno.land/x/windmill@v1.174.0/windmill-api/index.ts"; -export const VERSION = "v1.537.0"; +export const VERSION = "v1.537.1"; export async function login(email: string, password: string): Promise { return await windmill.UserService.login({ diff --git a/cli/src/main.ts b/cli/src/main.ts index 743ad64c75..28a98df79c 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -68,7 +68,7 @@ export { // } // }); -export const VERSION = "1.537.0"; +export const VERSION = "1.537.1"; const command = new Command() .name("wmill") diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 144e871b1c..c37ee5d0f4 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.537.0", + "version": "1.537.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.537.0", + "version": "1.537.1", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index f98f5789ab..7174d7cafa 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.537.0", + "version": "1.537.1", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 001b2c6440..3c3a6899f4 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.537.0" -wmill_pg = ">=1.537.0" +wmill = ">=1.537.1" +wmill_pg = ">=1.537.1" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index aec8eecbc7..cd43078197 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.537.0 + version: 1.537.1 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index 114e9b0818..9f98410bcf 100644 --- a/powershell-client/WindmillClient/WindmillClient.psd1 +++ b/powershell-client/WindmillClient/WindmillClient.psd1 @@ -12,7 +12,7 @@ RootModule = 'WindmillClient.psm1' # Version number of this module. - ModuleVersion = '1.537.0' + ModuleVersion = '1.537.1' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 73633134e4..60905554cb 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.537.0" +version = "1.537.1" description = "A client library for accessing Windmill server wrapping the Windmill client API" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/python-client/wmill_pg/pyproject.toml b/python-client/wmill_pg/pyproject.toml index ee75201d9b..e1f37fee4b 100644 --- a/python-client/wmill_pg/pyproject.toml +++ b/python-client/wmill_pg/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill-pg" -version = "1.537.0" +version = "1.537.1" description = "An extension client for the wmill client library focused on pg" license = "Apache-2.0" homepage = "https://windmill.dev" diff --git a/typescript-client/jsr.json b/typescript-client/jsr.json index 3126b9087e..bc3557e9ed 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.537.0", + "version": "1.537.1", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index 674c770bc6..47dc2c096e 100644 --- a/typescript-client/package.json +++ b/typescript-client/package.json @@ -1,7 +1,7 @@ { "name": "windmill-client", "description": "Windmill SDK client for browsers and Node.js", - "version": "1.537.0", + "version": "1.537.1", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index a2da630536..4daaa7338c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.537.0 +1.537.1 From 8cd1c6474b3c55d706683f83b40ef5569245b559 Mon Sep 17 00:00:00 2001 From: hugocasa Date: Fri, 29 Aug 2025 15:00:27 +0200 Subject: [PATCH 10/22] fix(frontend): capture/trigger UI nits (#6494) --- .../components/triggers/CaptureWrapper.svelte | 15 ++------- .../triggers/http/RouteEditorInner.svelte | 2 +- .../WebsocketTriggerEditorInner.svelte | 32 +++++++++---------- 3 files changed, 19 insertions(+), 30 deletions(-) diff --git a/frontend/src/lib/components/triggers/CaptureWrapper.svelte b/frontend/src/lib/components/triggers/CaptureWrapper.svelte index 133bb029a2..4177c3d146 100644 --- a/frontend/src/lib/components/triggers/CaptureWrapper.svelte +++ b/frontend/src/lib/components/triggers/CaptureWrapper.svelte @@ -2,7 +2,7 @@ import { workspaceStore } from '$lib/stores' import { CaptureService, type CaptureConfig, type CaptureTriggerKind } from '$lib/gen' import { onDestroy, untrack } from 'svelte' - import { isObject, sendUserToast, sleep } from '$lib/utils' + import { sendUserToast, sleep } from '$lib/utils' import RouteCapture from './http/RouteCapture.svelte' import type { ConnectionInfo } from '../common/alert/ConnectionIndicator.svelte' import type { CaptureInfo } from './CaptureSection.svelte' @@ -37,7 +37,7 @@ captureType = 'webhook', data = {}, connectionInfo = $bindable(undefined), - args = $bindable({}), + args = {}, isValid = false, triggerDeployed = false }: Props = $props() @@ -100,7 +100,6 @@ } return captureConfigs } - getCaptureConfigs().then((captureConfigs) => setDefaultArgs(captureConfigs)) async function capture() { let i = 0 @@ -120,16 +119,6 @@ } } - function setDefaultArgs(captureConfigs: { [key: string]: CaptureConfig }) { - if (captureType in captureConfigs) { - const triggerConfig = captureConfigs[captureType].trigger_config - args = isObject(triggerConfig) ? triggerConfig : {} - } else { - args = {} - } - ready = true - } - onDestroy(() => { captureActive = false }) diff --git a/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte b/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte index a89425478b..67cd7639e0 100644 --- a/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte +++ b/frontend/src/lib/components/triggers/http/RouteEditorInner.svelte @@ -596,7 +596,7 @@ {can_write} bind:static_asset_config showTestingBadge={isEditor} - isDraftOnly={trigger?.isDraft} + isDraftOnly={trigger ? trigger.isDraft : false} /> {#if !is_static_website} diff --git a/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte b/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte index d55000aba9..19c156fc8c 100644 --- a/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +++ b/frontend/src/lib/components/triggers/websocket/WebsocketTriggerEditorInner.svelte @@ -417,8 +417,8 @@ - {#if !hideTarget} -
+
+ {#if !hideTarget}

Pick a script or flow to be triggered @@ -449,21 +449,21 @@ {/if}

+ {/if} - { - can_return_message = !can_return_message - }} - options={{ - right: 'Send runnable result', - rightTooltip: - 'Whether the runnable result should be sent as a message to the websocket server when not null.' - }} - disabled={!can_write} - /> -
- {/if} + { + can_return_message = !can_return_message + }} + options={{ + right: 'Send runnable result', + rightTooltip: + 'Whether the runnable result should be sent as a message to the websocket server when not null.' + }} + disabled={!can_write} + /> +
Date: Sat, 30 Aug 2025 02:32:35 +0000 Subject: [PATCH 11/22] fix: schema editor reactivity improvements (#6496) * all * all * all * all * all * nit * all * all * all * all --- frontend/package-lock.json | 3 +- frontend/package.json | 2 +- frontend/src/global.d.ts | 4 +- frontend/src/lib/components/ArgInput.svelte | 27 +- .../lib/components/EditableSchemaForm.svelte | 108 ++++---- frontend/src/lib/components/Portal.svelte | 18 +- frontend/src/lib/components/SchemaForm.svelte | 4 +- .../apps/components/layout/AppModal.svelte | 4 +- .../apps/editor/AppEditorHeader.svelte | 1 + .../component/ComponentNavigation.svelte | 8 +- .../component/componentCallbacks.svelte.ts | 2 +- .../ArrayStaticInputEditor.svelte | 5 +- .../editor/settingsPanel/GridCondition.svelte | 5 +- .../editor/settingsPanel/GridNavbar.svelte | 5 +- .../apps/editor/settingsPanel/GridTab.svelte | 5 +- .../editor/settingsPanel/TableActions.svelte | 5 +- .../common/drawer/Disposable.svelte | 101 +++++--- .../components/common/drawer/Drawer.svelte | 27 +- .../components/flows/content/FlowInput.svelte | 37 ++- .../flows/content/FlowModuleSuspend.svelte | 33 ++- .../flows/content/ScriptEditorDrawer.svelte | 21 +- .../propertyPicker/ObjectViewer.svelte | 16 +- .../components/schema/AddPropertyV2.svelte | 12 +- .../schema/EditableSchemaDrawer.svelte | 234 +++++++++--------- .../schema/EditableSchemaSdkWrapper.svelte | 22 +- .../schema/EditableSchemaWrapper.svelte | 12 +- .../schema/FlowPropertyEditor.svelte | 52 +--- .../components/schema/SchemaFormDND.svelte | 38 +-- .../schema/editable_schema_wrapper.ts | 1 - .../triggers/AddTriggersButton.svelte | 1 + .../routes/test_dev/sdk_schema/+page.svelte | 11 +- 31 files changed, 433 insertions(+), 391 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c37ee5d0f4..69d3d0a144 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -25,7 +25,7 @@ "@scalar/openapi-parser": "^0.15.0", "@tanstack/svelte-table": "npm:tanstack-table-8-svelte-5@^0.1", "@tutorlatin/svelte-tiny-virtual-list": "^3.0.2", - "@windmill-labs/svelte-dnd-action": "^0.9.48", + "@windmill-labs/svelte-dnd-action": "^0.9.44", "@xterm/addon-fit": "^0.10.0", "@xyflow/svelte": "^1.0.0", "ag-charts-community": "^9.0.1", @@ -3898,6 +3898,7 @@ "version": "0.9.48", "resolved": "https://registry.npmjs.org/@windmill-labs/svelte-dnd-action/-/svelte-dnd-action-0.9.48.tgz", "integrity": "sha512-A6pWayH3nOi79DZohTscGj5t2PuNtHlKM5WIAj9WlVDx5pS1A+MsyfuNZi8oCpj2590wFmQl+58G4y4xZqDa1Q==", + "license": "MIT", "peerDependencies": { "svelte": ">=3.23.0 || ^5.0.0-next.0" } diff --git a/frontend/package.json b/frontend/package.json index 7174d7cafa..775d5f4ec0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -92,7 +92,7 @@ "@scalar/openapi-parser": "^0.15.0", "@tanstack/svelte-table": "npm:tanstack-table-8-svelte-5@^0.1", "@tutorlatin/svelte-tiny-virtual-list": "^3.0.2", - "@windmill-labs/svelte-dnd-action": "^0.9.48", + "@windmill-labs/svelte-dnd-action": "^0.9.44", "@xterm/addon-fit": "^0.10.0", "@xyflow/svelte": "^1.0.0", "ag-charts-community": "^9.0.1", diff --git a/frontend/src/global.d.ts b/frontend/src/global.d.ts index 3cf0168db4..bd06974068 100644 --- a/frontend/src/global.d.ts +++ b/frontend/src/global.d.ts @@ -1,7 +1,7 @@ /// -declare type Item = import('svelte-dnd-action').Item -declare type DndEvent = import('svelte-dnd-action').DndEvent +declare type Item = import('@windmill-labs/svelte-dnd-action').Item +declare type DndEvent = import('@windmill-labs/svelte-dnd-action').DndEvent declare namespace svelte.JSX { interface HTMLAttributes { onconsider?: (event: CustomEvent> & { target: EventTarget & T }) => void diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index 5e8b189a09..7897832188 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -992,8 +992,11 @@ } } bind:args={value} - dndType={`nested-${title}`} - hiddenArgs={['label', 'kind']} + hiddenArgs={[ + oneOf?.find((o) => Object.keys(o.properties ?? {}).includes('kind')) + ? 'kind' + : 'label' + ]} on:reorder={(e) => { if (oneOf && oneOf[objIdx]) { const keys = e.detail @@ -1086,20 +1089,14 @@ {disablePortal} {disabled} {prettifyHeader} - bind:schema={ - () => ({ - properties, - $schema: '', - required: nestedRequired ?? [], - type: 'object', - order - }), - (newSchema) => { - dispatch('nestedChange') - } - } + schema={{ + properties, + $schema: '', + required: nestedRequired ?? [], + type: 'object', + order + }} bind:args={value} - dndType={`nested-${title}`} on:reorder={(e) => { const keys = e.detail order = keys diff --git a/frontend/src/lib/components/EditableSchemaForm.svelte b/frontend/src/lib/components/EditableSchemaForm.svelte index a3dfc5a168..529e99a1f9 100644 --- a/frontend/src/lib/components/EditableSchemaForm.svelte +++ b/frontend/src/lib/components/EditableSchemaForm.svelte @@ -14,13 +14,18 @@ import FlowPropertyEditor from './schema/FlowPropertyEditor.svelte' import PropertyEditor from './schema/PropertyEditor.svelte' import SimpleEditor from './SimpleEditor.svelte' - import { createEventDispatcher, tick, untrack } from 'svelte' + import { createEventDispatcher, untrack } from 'svelte' import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' import Label from './Label.svelte' import { sendUserToast } from '$lib/toast' import Toggle from './Toggle.svelte' - import { DynamicSelect, emptyString } from '$lib/utils' + import { + DynamicSelect, + emptyString, + generateRandomString, + readFieldsRecursively + } from '$lib/utils' import Popover from './meltComponents/Popover.svelte' import SchemaFormDnd from './schema/SchemaFormDND.svelte' import { deepEqual } from 'fast-equals' @@ -48,7 +53,6 @@ isAppInput?: boolean displayWebhookWarning?: boolean onlyMaskPassword?: boolean - dndType?: string | undefined editTab: | 'inputEditor' | 'history' @@ -75,6 +79,8 @@ addProperty?: import('svelte').Snippet runButton?: import('svelte').Snippet extraTab?: import('svelte').Snippet + schemaFormClassName?: string + onChange?: (args: Record) => void } let { @@ -91,7 +97,6 @@ isAppInput = false, displayWebhookWarning = false, onlyMaskPassword = false, - dndType = undefined, editTab, previewSchema = undefined, editPanelInitialSize = undefined, @@ -110,7 +115,9 @@ openEditTab, addProperty, runButton, - extraTab + extraTab, + schemaFormClassName = undefined, + onChange = undefined }: Props = $props() $effect.pre(() => { @@ -125,6 +132,13 @@ } }) + $effect(() => { + if (onChange) { + readFieldsRecursively(args) + onChange(args ?? {}) + } + }) + $effect(() => { if (schema && dynSelectCode !== undefined && dynSelectLang !== undefined) { if (dynSelectCode && dynSelectCode.trim()) { @@ -154,9 +168,10 @@ let variableEditor: VariableEditor | undefined = $state(undefined) let keys: string[] = $state( - Array.isArray(schema?.order) + (Array.isArray(schema?.order) ? [...schema.order] : (Object.keys(schema?.properties ?? {}) ?? Object.keys(schema?.properties ?? {})) + ).filter((x) => !hiddenArgs?.includes(x)) ) function alignOrderWithProperties(schema: { @@ -187,22 +202,18 @@ return hasChanged } function onSchemaChange() { - let editSchema = false if (alignOrderWithProperties(schema)) { - console.log('alignOrderWithProperties', JSON.stringify(schema, null, 2)) - editSchema = true + // console.log('alignOrderWithProperties', JSON.stringify(schema, null, 2)) } - let lkeys = schema?.order ?? Object.keys(schema?.properties ?? {}) + let lkeys = (schema?.order ?? Object.keys(schema?.properties ?? {})).filter( + (x) => !hiddenArgs?.includes(x) + ) if (schema?.properties && !deepEqual(lkeys, keys)) { keys = [...lkeys] - editSchema = true if (opened == undefined) { opened = keys[0] } } - if (editSchema) { - schema = schema - } } let opened: string | undefined = $state(untrack(() => keys[0])) @@ -248,30 +259,31 @@ // clear the input el.value = oldName } else { + let newSchema = $state.snapshot(schema) if (args) { args[newName] = args[oldName] delete args[oldName] } - schema.properties[newName] = schema.properties[oldName] - delete schema.properties[oldName] + newSchema.properties[newName] = newSchema.properties[oldName] + delete newSchema.properties[oldName] - if (schema.required?.includes(oldName)) { - schema.required = schema.required?.map((x) => (x === oldName ? newName : x)) + if (newSchema.required?.includes(oldName)) { + newSchema.required = newSchema.required?.map((x) => (x === oldName ? newName : x)) } // Replace the old name with the new name in the order array - if (schema.order) { - const index = schema.order.indexOf(oldName) + if (newSchema.order) { + const index = newSchema.order.indexOf(oldName) if (index !== -1) { - schema.order[index] = newName + newSchema.order[index] = newName } } opened = newName - schema = $state.snapshot(schema) - dispatch('change', schema) + schema = newSchema + sendUserToast('Argument renamed') } } @@ -368,6 +380,8 @@ const code = generateFn(functionName) dynSelectCode = dynSelectCode ? dynSelectCode.concat(code) : code } + + let dndType = $state(generateRandomString())
@@ -404,15 +418,15 @@ class="min-h-0 overflow-y-auto grow rounded-md {runButton ? 'flex flex-col gap-2' : ''}" > (previewSchema ? previewSchema : schema), (newSchema) => { schema = newSchema - tick().then(() => dispatch('change', schema)) } } - {dndType} + {hiddenArgs} {disableDnd} {onlyMaskPassword} bind:args @@ -420,11 +434,16 @@ opened = e.detail }} on:reorder={(e) => { + let order = e.detail + let newProperties = {} + for (let key of order) { + newProperties[key] = schema.properties[key] + } schema = { ...schema, + properties: newProperties, order: e.detail } - tick().then(() => dispatch('change', schema)) }} helperScript={{ type: 'inline', @@ -436,9 +455,6 @@ {diff} on:acceptChange on:rejectChange - on:nestedChange={() => { - dispatch('change', schema) - }} {shouldDispatchChanges} bind:isValid noVariablePicker={noVariablePicker || customUi?.disableVariablePicker === true} @@ -446,8 +462,8 @@ {@render runButton?.()} -
- {#if dynSelectFunctions.length > 0} + {#if dynSelectFunctions.length > 0} +
- {/if} -
+
+ {/if}
@@ -514,7 +530,7 @@ {#if jsonEnabled && customUi?.jsonOnly != true}
{#if addPropertyInEditorTab} - + {#snippet trigger()}
{#if opened === argName}
- {#if !hiddenArgs.includes(argName) && Object.keys(schema?.properties ?? {}).includes(argName)} + {#if Object.keys(schema?.properties ?? {}).includes(argName)} {#if typeof args == 'object' && schema?.properties[argName]} { - schema = $state.snapshot(schema) - dispatch('change', schema) - }} > {#snippet typeeditor()} {#if isFlowInput || isAppInput} @@ -742,13 +754,9 @@ type: v } } + schema.properties = schema.properties } } - on:selected={(e) => { - schema = schema - dispatch('change', schema) - dispatch('schemaChange') - }} > {#snippet children({ item })} {#each typeOptions as x} @@ -762,6 +770,9 @@ {#if isFlowInput || isAppInput} { + dndType = generateRandomString() + }} bind:defaultValue={schema.properties[argName].default} {variableEditor} {itemPicker} @@ -792,12 +803,6 @@ (x) => x !== argName ) } - dispatch('change', schema) - }} - on:schemaChange={(e) => { - schema = $state.snapshot(schema) - dispatch('change', schema) - dispatch('schemaChange') }} /> {/if} @@ -822,7 +827,6 @@ on:change={() => { try { schema = JSON.parse(schemaString) - dispatch('change', schema) error = '' } catch (err) { error = err.message diff --git a/frontend/src/lib/components/Portal.svelte b/frontend/src/lib/components/Portal.svelte index 5ea9c44e95..801188de2e 100644 --- a/frontend/src/lib/components/Portal.svelte +++ b/frontend/src/lib/components/Portal.svelte @@ -1,4 +1,4 @@ - -