From afa8104cb0c1a8f1a6fe124a6e01c1d32f049afa Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 24 Sep 2025 18:39:52 +0000 Subject: [PATCH 01/33] fix(ui): workers button on navbar require a single click only --- .../meltComponents/MenuSingleItem.svelte | 17 +------- .../components/sidebar/SidebarContent.svelte | 41 ++++++++----------- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/frontend/src/lib/components/meltComponents/MenuSingleItem.svelte b/frontend/src/lib/components/meltComponents/MenuSingleItem.svelte index 65ec922bfb..83a78d878e 100644 --- a/frontend/src/lib/components/meltComponents/MenuSingleItem.svelte +++ b/frontend/src/lib/components/meltComponents/MenuSingleItem.svelte @@ -1,24 +1,11 @@
- {@render triggr?.({ trigger })} -
- {@render children?.({ item })} -
+ {@render children?.({})}
diff --git a/frontend/src/lib/components/sidebar/SidebarContent.svelte b/frontend/src/lib/components/sidebar/SidebarContent.svelte index f45c698be5..509e649d01 100644 --- a/frontend/src/lib/components/sidebar/SidebarContent.svelte +++ b/frontend/src/lib/components/sidebar/SidebarContent.svelte @@ -359,7 +359,7 @@ } ] : []), - ...($workspaceStore?.startsWith("wm-fork") + ...($workspaceStore?.startsWith('wm-fork') ? [ { label: 'Delete Forked Workspace', @@ -370,7 +370,7 @@ faIcon: undefined } ] - : []), + : []) ], disabled: $userStore?.operator }, @@ -573,11 +573,6 @@ {:else} - {#snippet triggr({ trigger })} -
- -
- {/snippet} {#snippet children({ item })} {/snippet} @@ -665,20 +660,20 @@ -{#if $workspaceStore?.startsWith("wm-fork-")} - { - deleteWorkspaceForkModal = false - }} - on:confirmed={() => { - deleteFork() - }} -> -
- Are you sure you want to delete this workspace fork? (deleting {$workspaceStore}) -
-
+{#if $workspaceStore?.startsWith('wm-fork-')} + { + deleteWorkspaceForkModal = false + }} + on:confirmed={() => { + deleteFork() + }} + > +
+ Are you sure you want to delete this workspace fork? (deleting {$workspaceStore}) +
+
{/if} From 0f5de6837be59dcf24df470266d4f743dd012ce2 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 24 Sep 2025 18:48:00 +0000 Subject: [PATCH 02/33] nit --- frontend/src/lib/components/sidebar/SidebarContent.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/lib/components/sidebar/SidebarContent.svelte b/frontend/src/lib/components/sidebar/SidebarContent.svelte index 509e649d01..de6c22f3ee 100644 --- a/frontend/src/lib/components/sidebar/SidebarContent.svelte +++ b/frontend/src/lib/components/sidebar/SidebarContent.svelte @@ -572,9 +572,9 @@ {/snippet} {:else} - - {#snippet children({ item })} - + + {#snippet children({})} + {/snippet} {/if} From 4117342d46a5daf87954ea79f9e8e19c8fea6f0e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 24 Sep 2025 19:05:35 +0000 Subject: [PATCH 03/33] nit runs page reset --- frontend/src/lib/components/sidebar/MenuLink.svelte | 5 ++++- frontend/src/lib/components/sidebar/SidebarContent.svelte | 7 ++++++- .../src/routes/(root)/(logged)/runs/[...path]/+page.svelte | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/sidebar/MenuLink.svelte b/frontend/src/lib/components/sidebar/MenuLink.svelte index 82188ac9fc..9ecdc65072 100644 --- a/frontend/src/lib/components/sidebar/MenuLink.svelte +++ b/frontend/src/lib/components/sidebar/MenuLink.svelte @@ -19,6 +19,7 @@ lightMode?: boolean item?: MenubarMenuElements['item'] | undefined class?: string + onclick?: (ev: MouseEvent) => any } let { @@ -31,7 +32,8 @@ disabled = false, lightMode = false, item = undefined, - class: classNames = '' + class: classNames = '', + onclick = undefined }: Props = $props() let isSelected = $state(false) @@ -56,6 +58,7 @@ goto(href) } }} + {onclick} class={twMerge( 'group flex items-center px-2 py-2 text-sm font-light rounded-md h-8 gap-3', isSelected diff --git a/frontend/src/lib/components/sidebar/SidebarContent.svelte b/frontend/src/lib/components/sidebar/SidebarContent.svelte index de6c22f3ee..bdbb17ba3f 100644 --- a/frontend/src/lib/components/sidebar/SidebarContent.svelte +++ b/frontend/src/lib/components/sidebar/SidebarContent.svelte @@ -172,7 +172,12 @@ href: `${base}/runs`, icon: Play, aiId: 'sidebar-menu-link-runs', - aiDescription: 'Button to navigate to runs' + aiDescription: 'Button to navigate to runs', + onclick: () => { + setTimeout(() => { + window.dispatchEvent(new Event('popstate')) + }, 100) + } }, { label: 'Variables', diff --git a/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte index 153d8772ef..c6889d7466 100644 --- a/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte @@ -66,6 +66,7 @@ let allowWildcards: boolean = $state(page.url.searchParams.get('allow_wildcards') == 'true') let concurrencyKey: string | null = $state(page.url.searchParams.get('concurrency_key')) let tag: string | null = $state(page.url.searchParams.get('tag')) + // Rest of filters handled by RunsFilter let success: 'running' | 'suspended' | 'waiting' | 'success' | 'failure' | undefined = $state( (page.url.searchParams.get('success') ?? undefined) as @@ -350,6 +351,7 @@ } function reset() { + path = page.params.path ?? null minTs = undefined maxTs = undefined jobs = undefined From 6f15459d3bbbec6178b387373990ab1ebe0b9b19 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Wed, 24 Sep 2025 21:06:31 +0200 Subject: [PATCH 04/33] chore(main): release 1.548.2 (#6674) * chore(main): release 1.548.2 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 7 +++ backend/Cargo.lock | 56 +++++++++---------- 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, 52 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10cabf9519..94ca30c905 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.548.2](https://github.com/windmill-labs/windmill/compare/v1.548.1...v1.548.2) (2025-09-24) + + +### Bug Fixes + +* **ui:** workers button on navbar require a single click only ([afa8104](https://github.com/windmill-labs/windmill/commit/afa8104cb0c1a8f1a6fe124a6e01c1d32f049afa)) + ## [1.548.1](https://github.com/windmill-labs/windmill/compare/v1.548.0...v1.548.1) (2025-09-24) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 5b8b1a7b5b..f5be1d32a8 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -15119,7 +15119,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "aws-sdk-config", @@ -15179,7 +15179,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "argon2", @@ -15299,7 +15299,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.548.1" +version = "1.548.2" dependencies = [ "base64 0.22.1", "chrono", @@ -15314,7 +15314,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.548.1" +version = "1.548.2" dependencies = [ "chrono", "serde", @@ -15327,7 +15327,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "axum", @@ -15346,7 +15346,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "async-recursion", @@ -15430,7 +15430,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.548.1" +version = "1.548.2" dependencies = [ "regex", "serde", @@ -15445,7 +15445,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "bytes", @@ -15469,7 +15469,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.548.1" +version = "1.548.2" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -15481,7 +15481,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.548.1" +version = "1.548.2" dependencies = [ "convert_case 0.6.0", "serde", @@ -15490,7 +15490,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "lazy_static", @@ -15502,7 +15502,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "serde_json", @@ -15514,7 +15514,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "gosyn", @@ -15526,7 +15526,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "lazy_static", @@ -15538,7 +15538,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "serde_json", @@ -15550,7 +15550,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "nu-parser", @@ -15561,7 +15561,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15572,7 +15572,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15584,7 +15584,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "async-recursion", @@ -15607,7 +15607,7 @@ dependencies = [ [[package]] name = "windmill-parser-ruby" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "lazy_static", @@ -15621,7 +15621,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -15638,7 +15638,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "lazy_static", @@ -15652,7 +15652,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "lazy_static", @@ -15670,7 +15670,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -15695,7 +15695,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "serde_json", @@ -15705,7 +15705,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "async-recursion", @@ -15738,7 +15738,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.548.1" +version = "1.548.2" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -15748,7 +15748,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.548.1" +version = "1.548.2" dependencies = [ "anyhow", "async-once-cell", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 173ce528f7..d0e9521029 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.548.1" +version = "1.548.2" authors.workspace = true edition.workspace = true @@ -34,7 +34,7 @@ members = [ exclude = ["./windmill-duckdb-ffi-internal"] [workspace.package] -version = "1.548.1" +version = "1.548.2" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 263a07785e..fe521fead9 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.548.1 + version: 1.548.2 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 489c5ab4c0..87610fd15d 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.548.1"; +export const VERSION = "v1.548.2"; 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 777ad9f2d0..7105277a57 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -68,7 +68,7 @@ export { // } // }); -export const VERSION = "1.548.1"; +export const VERSION = "1.548.2"; export const WM_FORK_PREFIX = "wm-fork"; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e503cf2d65..025ac5b064 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.548.1", + "version": "1.548.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.548.1", + "version": "1.548.2", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 111f0195ee..95914d3ba8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.548.1", + "version": "1.548.2", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index b359fd21e7..64468f184b 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.548.1" -wmill_pg = ">=1.548.1" +wmill = ">=1.548.2" +wmill_pg = ">=1.548.2" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index eb9ff3048b..c7b32c98fc 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.548.1 + version: 1.548.2 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index 5f7d3fc6c8..c09017f98a 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.548.1' + ModuleVersion = '1.548.2' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 1d2d51d46a..61bdb2b8ad 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.548.1" +version = "1.548.2" 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 1a9c263403..d2a8459e3a 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.548.1" +version = "1.548.2" 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 7643f04807..20689c1dce 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.548.1", + "version": "1.548.2", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index 5c42159de7..bc49b7b517 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.548.1", + "version": "1.548.2", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index e5158c8391..a4fd967220 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.548.1 +1.548.2 From a308782bcf7ef9913887521d74796b490619d0c8 Mon Sep 17 00:00:00 2001 From: dieriba Date: Wed, 24 Sep 2025 21:47:52 +0200 Subject: [PATCH 05/33] fix: websocket runnable #6675 --- .../src/triggers/websocket/handler.rs | 3 +- .../src/triggers/websocket/listener.rs | 47 +++---------------- .../src/triggers/websocket/mod.rs | 6 +-- 3 files changed, 12 insertions(+), 44 deletions(-) diff --git a/backend/windmill-api/src/triggers/websocket/handler.rs b/backend/windmill-api/src/triggers/websocket/handler.rs index 827367e195..510f6e97ae 100644 --- a/backend/windmill-api/src/triggers/websocket/handler.rs +++ b/backend/windmill-api/src/triggers/websocket/handler.rs @@ -12,6 +12,7 @@ use tokio_tungstenite::connect_async; use windmill_common::{ db::UserDB, error::{Error, Result}, + worker::to_raw_value, }; use windmill_git_sync::DeployedObject; @@ -236,7 +237,7 @@ impl TriggerCrud for WebsocketTrigger { url.starts_with("$flow:"), &db, authed.clone(), - config.url_runnable_args.as_ref(), + config.url_runnable_args.as_ref().map(to_raw_value).as_ref(), &workspace_id, ) .await?, diff --git a/backend/windmill-api/src/triggers/websocket/listener.rs b/backend/windmill-api/src/triggers/websocket/listener.rs index 9a3f83a016..bfecaf479d 100644 --- a/backend/windmill-api/src/triggers/websocket/listener.rs +++ b/backend/windmill-api/src/triggers/websocket/listener.rs @@ -5,7 +5,7 @@ use crate::triggers::{ trigger_runnable, trigger_runnable_and_wait_for_raw_result, trigger_runnable_and_wait_for_raw_result_with_error_ctx, TriggerJobArgs, }, - websocket::WebsocketConfig, + websocket::{get_url_from_runnable_value, WebsocketConfig}, Listener, }; use anyhow::Context; @@ -31,43 +31,6 @@ use windmill_common::{ use windmill_queue::PushArgsOwned; impl ListeningTrigger { - async fn get_url_from_runnable(&self, db: &DB) -> Result { - let runnable_kind = if self.is_flow { "Flow" } else { "Script" }; - tracing::info!( - "Running {} {} to get WebSocket URL", - runnable_kind.to_lowercase(), - self.path - ); - - let authed = self.authed(db, "ws").await?; - - let args = raw_value_to_args_hashmap( - self.trigger_config.url_runnable_args.as_ref().map(|r| &r.0), - )?; - - let result = trigger_runnable_and_wait_for_raw_result_with_error_ctx( - db, - None, - authed, - &self.workspace_id, - &self.script_path, - self.is_flow, - PushArgsOwned { args, extra: None }, - None, - None, - None, - "".to_string(), // doesn't matter as no retry/error handler - ) - .await?; - - serde_json::from_str::(result.get()).map_err(|_| { - Error::BadConfig(format!( - "{} {} did not return a string", - runnable_kind, self.path, - )) - }) - } - async fn send_initial_messages( &self, writer: &mut SplitSink>, Message>, @@ -189,8 +152,12 @@ impl Listener for WebsocketTrigger { )) => { return Ok(None); }, - - url_result = listening_trigger.get_url_from_runnable(&db) => match url_result { + url_result = { + let authed = listening_trigger.authed(db, "ws").await?; + let args = listening_trigger.trigger_config.url_runnable_args.as_ref().map(|r| &r.0); + let path = url.splitn(2, ':').nth(1).unwrap(); + get_url_from_runnable_value(path, url.starts_with("$flow:"), db, authed, args, &listening_trigger.workspace_id) + } => match url_result { Ok(url) => Cow::Owned(url), Err(err) => { return Err(anyhow::anyhow!("Error getting WebSocket URL from runnable after 5 tries: {:?}", err).into()); diff --git a/backend/windmill-api/src/triggers/websocket/mod.rs b/backend/windmill-api/src/triggers/websocket/mod.rs index 86d3bd735b..7a4d98bcc1 100644 --- a/backend/windmill-api/src/triggers/websocket/mod.rs +++ b/backend/windmill-api/src/triggers/websocket/mod.rs @@ -61,11 +61,11 @@ pub struct TestWebsocketConfig { } pub fn value_to_args_hashmap( - args: Option<&serde_json::Value>, + args: Option<&Box>, ) -> Result>> { let args = if let Some(args) = args { let args_map: Option> = - serde_json::from_value(args.clone()) + serde_json::from_str(args.get()) .map_err(|e| Error::BadRequest(format!("invalid json: {}", e)))?; args_map @@ -89,7 +89,7 @@ pub async fn get_url_from_runnable_value( is_flow: bool, db: &DB, authed: ApiAuthed, - args: Option<&serde_json::Value>, + args: Option<&Box>, workspace_id: &str, ) -> Result { tracing::info!( From f5d238edcfed6b0f066d459cdc718679a7b51187 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 25 Sep 2025 10:29:08 +0000 Subject: [PATCH 06/33] fix: fix job loader token initialization --- frontend/src/lib/components/JobLoader.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/lib/components/JobLoader.svelte b/frontend/src/lib/components/JobLoader.svelte index e03a327422..ecdd8df69f 100644 --- a/frontend/src/lib/components/JobLoader.svelte +++ b/frontend/src/lib/components/JobLoader.svelte @@ -96,6 +96,8 @@ let lastNoLogs = $state(noLogs) let lastCompletedJobId = $state(undefined) + let token = getContext<{ token?: string }>('AuthToken') + $effect(() => { let newIsLoading = currentId !== undefined untrack(() => { @@ -676,7 +678,6 @@ ) } - let token = getContext<{ token?: string }>('AuthToken') if (token?.token && token.token != '') { params.set('token', token.token) } From b47c21f2992479b4769d79b2a8b9c3eb09d83d2e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 25 Sep 2025 10:30:44 +0000 Subject: [PATCH 07/33] nit --- frontend/src/lib/components/Dev.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/Dev.svelte b/frontend/src/lib/components/Dev.svelte index ed2c6c1b32..4a0f3af5dc 100644 --- a/frontend/src/lib/components/Dev.svelte +++ b/frontend/src/lib/components/Dev.svelte @@ -574,7 +574,7 @@ let themeDarkRaw = $derived($page.url.searchParams.get('activeColorTheme')) let themeDark = $derived(themeDarkRaw == '2' || themeDarkRaw == '4') - $effect(() => { + $effect.pre(() => { setContext<{ token?: string }>('AuthToken', { token }) }) $effect.pre(() => { From 78c0bea6b4fce9493f04ce381491613bca184a7a Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 25 Sep 2025 12:36:22 +0200 Subject: [PATCH 08/33] chore(main): release 1.548.3 (#6676) * chore(main): release 1.548.3 * Apply automatic changes --------- Co-authored-by: rubenfiszel <275584+rubenfiszel@users.noreply.github.com> --- CHANGELOG.md | 8 +++ backend/Cargo.lock | 64 +++++++++---------- 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, 57 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ca30c905..252293616e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.548.3](https://github.com/windmill-labs/windmill/compare/v1.548.2...v1.548.3) (2025-09-25) + + +### Bug Fixes + +* fix job loader token initialization ([f5d238e](https://github.com/windmill-labs/windmill/commit/f5d238edcfed6b0f066d459cdc718679a7b51187)) +* websocket runnable [#6675](https://github.com/windmill-labs/windmill/issues/6675) ([a308782](https://github.com/windmill-labs/windmill/commit/a308782bcf7ef9913887521d74796b490619d0c8)) + ## [1.548.2](https://github.com/windmill-labs/windmill/compare/v1.548.1...v1.548.2) (2025-09-24) diff --git a/backend/Cargo.lock b/backend/Cargo.lock index f5be1d32a8..d99f586304 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -9794,9 +9794,9 @@ dependencies = [ [[package]] name = "postgres-protocol" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" +checksum = "fbef655056b916eb868048276cfd5d6a7dea4f81560dfd047f97c8c6fe3fcfd4" dependencies = [ "base64 0.22.1", "byteorder", @@ -9831,7 +9831,7 @@ dependencies = [ "bytes", "chrono", "fallible-iterator 0.2.0", - "postgres-protocol 0.6.8", + "postgres-protocol 0.6.9", "serde", "serde_json", "uuid", @@ -13687,7 +13687,7 @@ dependencies = [ "percent-encoding", "phf 0.11.3", "pin-project-lite", - "postgres-protocol 0.6.8", + "postgres-protocol 0.6.9", "postgres-types 0.2.9", "rand 0.9.0", "socket2 0.5.10", @@ -15119,7 +15119,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windmill" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "aws-sdk-config", @@ -15179,7 +15179,7 @@ dependencies = [ [[package]] name = "windmill-api" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "argon2", @@ -15299,7 +15299,7 @@ dependencies = [ [[package]] name = "windmill-api-client" -version = "1.548.2" +version = "1.548.3" dependencies = [ "base64 0.22.1", "chrono", @@ -15314,7 +15314,7 @@ dependencies = [ [[package]] name = "windmill-audit" -version = "1.548.2" +version = "1.548.3" dependencies = [ "chrono", "serde", @@ -15327,7 +15327,7 @@ dependencies = [ [[package]] name = "windmill-autoscaling" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "axum", @@ -15346,7 +15346,7 @@ dependencies = [ [[package]] name = "windmill-common" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "async-recursion", @@ -15430,7 +15430,7 @@ dependencies = [ [[package]] name = "windmill-git-sync" -version = "1.548.2" +version = "1.548.3" dependencies = [ "regex", "serde", @@ -15445,7 +15445,7 @@ dependencies = [ [[package]] name = "windmill-indexer" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "bytes", @@ -15469,7 +15469,7 @@ dependencies = [ [[package]] name = "windmill-macros" -version = "1.548.2" +version = "1.548.3" dependencies = [ "itertools 0.14.0", "lazy_static", @@ -15481,7 +15481,7 @@ dependencies = [ [[package]] name = "windmill-parser" -version = "1.548.2" +version = "1.548.3" dependencies = [ "convert_case 0.6.0", "serde", @@ -15490,7 +15490,7 @@ dependencies = [ [[package]] name = "windmill-parser-bash" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "lazy_static", @@ -15502,7 +15502,7 @@ dependencies = [ [[package]] name = "windmill-parser-csharp" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "serde_json", @@ -15514,7 +15514,7 @@ dependencies = [ [[package]] name = "windmill-parser-go" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "gosyn", @@ -15526,7 +15526,7 @@ dependencies = [ [[package]] name = "windmill-parser-graphql" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "lazy_static", @@ -15538,7 +15538,7 @@ dependencies = [ [[package]] name = "windmill-parser-java" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "serde_json", @@ -15550,7 +15550,7 @@ dependencies = [ [[package]] name = "windmill-parser-nu" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "nu-parser", @@ -15561,7 +15561,7 @@ dependencies = [ [[package]] name = "windmill-parser-php" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15572,7 +15572,7 @@ dependencies = [ [[package]] name = "windmill-parser-py" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "itertools 0.14.0", @@ -15584,7 +15584,7 @@ dependencies = [ [[package]] name = "windmill-parser-py-imports" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "async-recursion", @@ -15607,7 +15607,7 @@ dependencies = [ [[package]] name = "windmill-parser-ruby" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "lazy_static", @@ -15621,7 +15621,7 @@ dependencies = [ [[package]] name = "windmill-parser-rust" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -15638,7 +15638,7 @@ dependencies = [ [[package]] name = "windmill-parser-sql" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "lazy_static", @@ -15652,7 +15652,7 @@ dependencies = [ [[package]] name = "windmill-parser-ts" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "lazy_static", @@ -15670,7 +15670,7 @@ dependencies = [ [[package]] name = "windmill-parser-wasm" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "getrandom 0.2.16", @@ -15695,7 +15695,7 @@ dependencies = [ [[package]] name = "windmill-parser-yaml" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "serde_json", @@ -15705,7 +15705,7 @@ dependencies = [ [[package]] name = "windmill-queue" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "async-recursion", @@ -15738,7 +15738,7 @@ dependencies = [ [[package]] name = "windmill-sql-datatype-parser-wasm" -version = "1.548.2" +version = "1.548.3" dependencies = [ "wasm-bindgen", "wasm-bindgen-test", @@ -15748,7 +15748,7 @@ dependencies = [ [[package]] name = "windmill-worker" -version = "1.548.2" +version = "1.548.3" dependencies = [ "anyhow", "async-once-cell", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index d0e9521029..d58171ed62 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "windmill" -version = "1.548.2" +version = "1.548.3" authors.workspace = true edition.workspace = true @@ -34,7 +34,7 @@ members = [ exclude = ["./windmill-duckdb-ffi-internal"] [workspace.package] -version = "1.548.2" +version = "1.548.3" authors = ["Ruben Fiszel "] edition = "2021" diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index fe521fead9..741692c56c 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.548.2 + version: 1.548.3 title: Windmill API contact: diff --git a/benchmarks/lib.ts b/benchmarks/lib.ts index 87610fd15d..f293d8a189 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.548.2"; +export const VERSION = "v1.548.3"; 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 7105277a57..d1262cd081 100644 --- a/cli/src/main.ts +++ b/cli/src/main.ts @@ -68,7 +68,7 @@ export { // } // }); -export const VERSION = "1.548.2"; +export const VERSION = "1.548.3"; export const WM_FORK_PREFIX = "wm-fork"; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 025ac5b064..10896a6a5b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "windmill-components", - "version": "1.548.2", + "version": "1.548.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "windmill-components", - "version": "1.548.2", + "version": "1.548.3", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/frontend/package.json b/frontend/package.json index 95914d3ba8..c1ef4211bf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "windmill-components", - "version": "1.548.2", + "version": "1.548.3", "scripts": { "dev": "vite dev", "build": "vite build", diff --git a/lsp/Pipfile b/lsp/Pipfile index 64468f184b..068718836e 100644 --- a/lsp/Pipfile +++ b/lsp/Pipfile @@ -4,8 +4,8 @@ verify_ssl = true name = "pypi" [packages] -wmill = ">=1.548.2" -wmill_pg = ">=1.548.2" +wmill = ">=1.548.3" +wmill_pg = ">=1.548.3" sendgrid = "*" mysql-connector-python = "*" pymongo = "*" diff --git a/openflow.openapi.yaml b/openflow.openapi.yaml index c7b32c98fc..86e34b1f31 100644 --- a/openflow.openapi.yaml +++ b/openflow.openapi.yaml @@ -1,7 +1,7 @@ openapi: "3.0.3" info: - version: 1.548.2 + version: 1.548.3 title: OpenFlow Spec contact: name: Ruben Fiszel diff --git a/powershell-client/WindmillClient/WindmillClient.psd1 b/powershell-client/WindmillClient/WindmillClient.psd1 index c09017f98a..cff65fa5ef 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.548.2' + ModuleVersion = '1.548.3' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/python-client/wmill/pyproject.toml b/python-client/wmill/pyproject.toml index 61bdb2b8ad..380a5c54d0 100644 --- a/python-client/wmill/pyproject.toml +++ b/python-client/wmill/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "wmill" -version = "1.548.2" +version = "1.548.3" 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 d2a8459e3a..a33ca3d449 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.548.2" +version = "1.548.3" 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 20689c1dce..3506053725 100644 --- a/typescript-client/jsr.json +++ b/typescript-client/jsr.json @@ -1,6 +1,6 @@ { "name": "@windmill/windmill", - "version": "1.548.2", + "version": "1.548.3", "exports": "./src/index.ts", "publish": { "exclude": ["!src", "./s3Types.ts", "./client.ts"] diff --git a/typescript-client/package.json b/typescript-client/package.json index bc49b7b517..024bad2dec 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.548.2", + "version": "1.548.3", "author": "Ruben Fiszel", "license": "Apache 2.0", "devDependencies": { diff --git a/version.txt b/version.txt index a4fd967220..052820f521 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.548.2 +1.548.3 From 4205e83cfde453827eab23c31e76a0f0490d31b7 Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Fri, 26 Sep 2025 07:25:44 -0400 Subject: [PATCH 09/33] fix: scim group handling when deleting instance user + conversion (#6677) * fix: scim group handling when deleting instance user + conversion * sqlx + compilation --- ...1f7f387f5055c47f493271d26731336257384.json | 10 +- ...cc6d6bf1df758b30e99bd661da866062ef14f.json | 23 +++ ...0f7e9858eef249577ce23969522793c6b5608.json | 29 ++++ ...5a54d80812ff144e711100e6ac659785c991d.json | 18 +++ ...94f91df7e588d4d2431bc85f4d8734920c8bf.json | 51 +++++++ ...c38fc64deb1226aab9dc3bc4465324fce37d1.json | 16 ++ ...7950620e753dde97c8d2d88cd60ff8c3f339.json} | 4 +- ...ca3761d400391f1f46a8294da3e6c9af63887.json | 15 ++ ...5ef756b8e5c1955fbe111df9ee171dc262338.json | 89 +++++++++++ ...0cb549a34b96554ae1872355b90304f5dcb76.json | 4 +- ...212a5bd4039b57fab20b163617e33a4c9dd46.json | 14 ++ ...e5f5763c55a5ac255f939f1fda8d3f9200f8d.json | 41 +++++ ...77afbd8b3a660b3be27514b517c077c63c238.json | 89 +++++++++++ backend/windmill-api/openapi.yaml | 21 +++ backend/windmill-api/src/users.rs | 143 +++++++++++++++++- .../settings/WorkspaceUserSettings.svelte | 96 +++++++++++- 16 files changed, 651 insertions(+), 12 deletions(-) create mode 100644 backend/.sqlx/query-16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f.json create mode 100644 backend/.sqlx/query-1ff185d8b8b897a72180cd0002e0f7e9858eef249577ce23969522793c6b5608.json create mode 100644 backend/.sqlx/query-326fd614ebd965b9bb6f3e578f75a54d80812ff144e711100e6ac659785c991d.json create mode 100644 backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json create mode 100644 backend/.sqlx/query-85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1.json rename backend/.sqlx/{query-2d5f58dd2aff3bd49f3891ae76df23e2aa39891931516426f65b229314a0cee1.json => query-9c3ddb90295db7d6afcbdb077f017950620e753dde97c8d2d88cd60ff8c3f339.json} (72%) create mode 100644 backend/.sqlx/query-a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887.json create mode 100644 backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json create mode 100644 backend/.sqlx/query-b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46.json create mode 100644 backend/.sqlx/query-eb110e722ba8ac32d9d69010dc7e5f5763c55a5ac255f939f1fda8d3f9200f8d.json create mode 100644 backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json diff --git a/backend/.sqlx/query-07168aaf14cb6beff0ad4274b441f7f387f5055c47f493271d26731336257384.json b/backend/.sqlx/query-07168aaf14cb6beff0ad4274b441f7f387f5055c47f493271d26731336257384.json index e7ed0aee65..d29a18c691 100644 --- a/backend/.sqlx/query-07168aaf14cb6beff0ad4274b441f7f387f5055c47f493271d26731336257384.json +++ b/backend/.sqlx/query-07168aaf14cb6beff0ad4274b441f7f387f5055c47f493271d26731336257384.json @@ -46,11 +46,11 @@ ] }, "nullable": [ - false, - false, - false, - false, - false, + true, + true, + true, + true, + true, true, true ] diff --git a/backend/.sqlx/query-16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f.json b/backend/.sqlx/query-16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f.json new file mode 100644 index 0000000000..1af42ff529 --- /dev/null +++ b/backend/.sqlx/query-16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f.json @@ -0,0 +1,23 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT hash FROM script WHERE path = $1 AND workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "hash", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false + ] + }, + "hash": "16e4b1bead9fc77fd98658b8cb8cc6d6bf1df758b30e99bd661da866062ef14f" +} diff --git a/backend/.sqlx/query-1ff185d8b8b897a72180cd0002e0f7e9858eef249577ce23969522793c6b5608.json b/backend/.sqlx/query-1ff185d8b8b897a72180cd0002e0f7e9858eef249577ce23969522793c6b5608.json new file mode 100644 index 0000000000..6b8589e8e6 --- /dev/null +++ b/backend/.sqlx/query-1ff185d8b8b897a72180cd0002e0f7e9858eef249577ce23969522793c6b5608.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n eig.igroup as group_name,\n ws.auto_add_instance_groups_roles\n FROM email_to_igroup eig\n INNER JOIN workspace_settings ws ON ws.workspace_id = $1\n WHERE eig.email = $2\n AND eig.igroup = ANY(ws.auto_add_instance_groups)\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "group_name", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "auto_add_instance_groups_roles", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "1ff185d8b8b897a72180cd0002e0f7e9858eef249577ce23969522793c6b5608" +} diff --git a/backend/.sqlx/query-326fd614ebd965b9bb6f3e578f75a54d80812ff144e711100e6ac659785c991d.json b/backend/.sqlx/query-326fd614ebd965b9bb6f3e578f75a54d80812ff144e711100e6ac659785c991d.json new file mode 100644 index 0000000000..b7645b6a44 --- /dev/null +++ b/backend/.sqlx/query-326fd614ebd965b9bb6f3e578f75a54d80812ff144e711100e6ac659785c991d.json @@ -0,0 +1,18 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE usr SET added_via = $1, is_admin = $2, operator = $3 WHERE username = $4 AND workspace_id = $5", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Jsonb", + "Bool", + "Bool", + "Text", + "Text" + ] + }, + "nullable": [] + }, + "hash": "326fd614ebd965b9bb6f3e578f75a54d80812ff144e711100e6ac659785c991d" +} diff --git a/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json b/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json new file mode 100644 index 0000000000..6f08d98113 --- /dev/null +++ b/backend/.sqlx/query-804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf.json @@ -0,0 +1,51 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO script (summary, description, dedicated_worker, content, workspace_id, path, hash, language, tag, created_by, lock) VALUES ('', '', true, $1, $2, $3, $4, $5, $6, $7, '') ON CONFLICT (workspace_id, hash) DO NOTHING", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text", + "Varchar", + "Varchar", + "Int8", + { + "Custom": { + "name": "script_lang", + "kind": { + "Enum": [ + "python3", + "deno", + "go", + "bash", + "postgresql", + "nativets", + "bun", + "mysql", + "bigquery", + "snowflake", + "graphql", + "powershell", + "mssql", + "php", + "bunnative", + "rust", + "ansible", + "csharp", + "oracledb", + "nu", + "java", + "duckdb", + "ruby" + ] + } + } + }, + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "804fc11e35f4afc0db194b6fe2594f91df7e588d4d2431bc85f4d8734920c8bf" +} diff --git a/backend/.sqlx/query-85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1.json b/backend/.sqlx/query-85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1.json new file mode 100644 index 0000000000..bce7324fb6 --- /dev/null +++ b/backend/.sqlx/query-85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO v2_job_queue (id, workspace_id, scheduled_for, tag) SELECT unnest($1::uuid[]), $2, now(), $3", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "UuidArray", + "Varchar", + "Varchar" + ] + }, + "nullable": [] + }, + "hash": "85705fc3d7f8ba5f1b12d5fb222c38fc64deb1226aab9dc3bc4465324fce37d1" +} diff --git a/backend/.sqlx/query-2d5f58dd2aff3bd49f3891ae76df23e2aa39891931516426f65b229314a0cee1.json b/backend/.sqlx/query-9c3ddb90295db7d6afcbdb077f017950620e753dde97c8d2d88cd60ff8c3f339.json similarity index 72% rename from backend/.sqlx/query-2d5f58dd2aff3bd49f3891ae76df23e2aa39891931516426f65b229314a0cee1.json rename to backend/.sqlx/query-9c3ddb90295db7d6afcbdb077f017950620e753dde97c8d2d88cd60ff8c3f339.json index 9d2440990e..96bc0d74f4 100644 --- a/backend/.sqlx/query-2d5f58dd2aff3bd49f3891ae76df23e2aa39891931516426f65b229314a0cee1.json +++ b/backend/.sqlx/query-9c3ddb90295db7d6afcbdb077f017950620e753dde97c8d2d88cd60ff8c3f339.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT workspace_id as workspace, path, summary, description, schema FROM script as o \n WHERE created_at = (select max(created_at) from script where o.path = path and workspace_id = $1 AND archived = false) \n AND workspace_id = $1 and archived = false", + "query": "SELECT workspace_id as workspace, path, summary, description, schema FROM script as o\n WHERE created_at = (select max(created_at) from script where o.path = path and workspace_id = $1 AND archived = false)\n AND workspace_id = $1 and archived = false", "describe": { "columns": [ { @@ -42,5 +42,5 @@ true ] }, - "hash": "2d5f58dd2aff3bd49f3891ae76df23e2aa39891931516426f65b229314a0cee1" + "hash": "9c3ddb90295db7d6afcbdb077f017950620e753dde97c8d2d88cd60ff8c3f339" } diff --git a/backend/.sqlx/query-a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887.json b/backend/.sqlx/query-a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887.json new file mode 100644 index 0000000000..1afc61978e --- /dev/null +++ b/backend/.sqlx/query-a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887.json @@ -0,0 +1,15 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO v2_job_status (id, flow_status) SELECT unnest($1::uuid[]), $2", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "UuidArray", + "Jsonb" + ] + }, + "nullable": [] + }, + "hash": "a0b3e10e077d30c1da135dff9feca3761d400391f1f46a8294da3e6c9af63887" +} diff --git a/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json b/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json new file mode 100644 index 0000000000..e07a3ccdd7 --- /dev/null +++ b/backend/.sqlx/query-ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338.json @@ -0,0 +1,89 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO v2_job (id, runnable_id, runnable_path, kind, script_lang, tag, created_by, permissioned_as, permissioned_as_email, workspace_id, raw_flow) (SELECT gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10 FROM generate_series(1, 1)) RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Int8", + "Varchar", + { + "Custom": { + "name": "job_kind", + "kind": { + "Enum": [ + "script", + "preview", + "flow", + "dependencies", + "flowpreview", + "script_hub", + "identity", + "flowdependencies", + "http", + "graphql", + "postgresql", + "noop", + "appdependencies", + "deploymentcallback", + "singlescriptflow", + "flowscript", + "flownode", + "appscript", + "aiagent" + ] + } + } + }, + { + "Custom": { + "name": "script_lang", + "kind": { + "Enum": [ + "python3", + "deno", + "go", + "bash", + "postgresql", + "nativets", + "bun", + "mysql", + "bigquery", + "snowflake", + "graphql", + "powershell", + "mssql", + "php", + "bunnative", + "rust", + "ansible", + "csharp", + "oracledb", + "nu", + "java", + "duckdb", + "ruby" + ] + } + } + }, + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Jsonb" + ] + }, + "nullable": [ + false + ] + }, + "hash": "ab04cda71f8e2be9acbecabe1ee5ef756b8e5c1955fbe111df9ee171dc262338" +} diff --git a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json index 54e94cfb8f..99269c9851 100644 --- a/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json +++ b/backend/.sqlx/query-b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76.json @@ -18,8 +18,8 @@ "Left": [] }, "nullable": [ - false, - true + true, + false ] }, "hash": "b3dbdfb50ee8118bdaed3164b210cb549a34b96554ae1872355b90304f5dcb76" diff --git a/backend/.sqlx/query-b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46.json b/backend/.sqlx/query-b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46.json new file mode 100644 index 0000000000..a49baeefaf --- /dev/null +++ b/backend/.sqlx/query-b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO v2_job_runtime (id) SELECT unnest($1::uuid[])", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "UuidArray" + ] + }, + "nullable": [] + }, + "hash": "b4a9abcb38997587b28655b0f4a212a5bd4039b57fab20b163617e33a4c9dd46" +} diff --git a/backend/.sqlx/query-eb110e722ba8ac32d9d69010dc7e5f5763c55a5ac255f939f1fda8d3f9200f8d.json b/backend/.sqlx/query-eb110e722ba8ac32d9d69010dc7e5f5763c55a5ac255f939f1fda8d3f9200f8d.json new file mode 100644 index 0000000000..6b2a36ec10 --- /dev/null +++ b/backend/.sqlx/query-eb110e722ba8ac32d9d69010dc7e5f5763c55a5ac255f939f1fda8d3f9200f8d.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT email, is_admin, operator, added_via FROM usr WHERE username = $1 AND workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "email", + "type_info": "Varchar" + }, + { + "ordinal": 1, + "name": "is_admin", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "operator", + "type_info": "Bool" + }, + { + "ordinal": 3, + "name": "added_via", + "type_info": "Jsonb" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + true + ] + }, + "hash": "eb110e722ba8ac32d9d69010dc7e5f5763c55a5ac255f939f1fda8d3f9200f8d" +} diff --git a/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json b/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json new file mode 100644 index 0000000000..0e42bd0fdb --- /dev/null +++ b/backend/.sqlx/query-ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238.json @@ -0,0 +1,89 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO v2_job (id, runnable_id, runnable_path, kind, script_lang, tag, created_by, permissioned_as, permissioned_as_email, workspace_id) (SELECT gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9 FROM generate_series(1, $10)) RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Int8", + "Varchar", + { + "Custom": { + "name": "job_kind", + "kind": { + "Enum": [ + "script", + "preview", + "flow", + "dependencies", + "flowpreview", + "script_hub", + "identity", + "flowdependencies", + "http", + "graphql", + "postgresql", + "noop", + "appdependencies", + "deploymentcallback", + "singlescriptflow", + "flowscript", + "flownode", + "appscript", + "aiagent" + ] + } + } + }, + { + "Custom": { + "name": "script_lang", + "kind": { + "Enum": [ + "python3", + "deno", + "go", + "bash", + "postgresql", + "nativets", + "bun", + "mysql", + "bigquery", + "snowflake", + "graphql", + "powershell", + "mssql", + "php", + "bunnative", + "rust", + "ansible", + "csharp", + "oracledb", + "nu", + "java", + "duckdb", + "ruby" + ] + } + } + }, + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Varchar", + "Int4" + ] + }, + "nullable": [ + false + ] + }, + "hash": "ff0403790674cdb07022af71c2377afbd8b3a660b3be27514b517c077c63c238" +} diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 741692c56c..69b19975fc 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -575,6 +575,27 @@ paths: text/plain: schema: type: string + /w/{workspace}/users/convert_to_group/{username}: + post: + summary: convert manual user to group user (require admin privilege) + operationId: convertUserToGroup + tags: + - user + - admin + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - name: username + in: path + required: true + schema: + type: string + responses: + "200": + description: convert user to group user + content: + text/plain: + schema: + type: string /github_app/connected_repositories: get: diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index 949d563ac6..722c05ebce 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -71,6 +71,7 @@ pub fn workspaced_service() -> Router { .route("/get/:user", get(get_workspace_user)) .route("/update/:user", post(update_workspace_user)) .route("/delete/:user", delete(delete_workspace_user)) + .route("/convert_to_group/:user", post(convert_user_to_group)) .route("/is_owner/*path", get(is_owner_of_path)) .route("/whois/:username", get(whois)) .route("/whoami", get(whoami)) @@ -1362,6 +1363,138 @@ async fn update_workspace_user( Ok(format!("user {} updated", user_email)) } +async fn convert_user_to_group( + authed: ApiAuthed, + Extension(db): Extension, + Path((w_id, username_to_convert)): Path<(String, String)>, +) -> Result { + require_admin(authed.is_admin, &authed.username)?; + let mut tx = db.begin().await?; + + // Get user email and current status + let user_info = sqlx::query!( + "SELECT email, is_admin, operator, added_via FROM usr WHERE username = $1 AND workspace_id = $2", + username_to_convert, + &w_id + ) + .fetch_optional(&mut *tx) + .await?; + + let user_info = not_found_if_none(user_info, "User", &username_to_convert)?; + + // Check if user is already a group user + if let Some(added_via) = &user_info.added_via { + if added_via.get("source").and_then(|v| v.as_str()) == Some("instance_group") { + return Err(Error::BadRequest("User is already a group user".to_string())); + } + } + + // Find which instance groups this user belongs to that are configured for auto-add in this workspace + let eligible_groups = sqlx::query!( + r#" + SELECT + eig.igroup as group_name, + ws.auto_add_instance_groups_roles + FROM email_to_igroup eig + INNER JOIN workspace_settings ws ON ws.workspace_id = $1 + WHERE eig.email = $2 + AND eig.igroup = ANY(ws.auto_add_instance_groups) + "#, + &w_id, + &user_info.email + ) + .fetch_all(&mut *tx) + .await?; + + if eligible_groups.is_empty() { + return Err(Error::BadRequest( + "User is not a member of any instance groups configured for auto-add in this workspace".to_string() + )); + } + + // Determine the group with highest precedence (same logic as process_instance_group_auto_adds) + let roles: std::collections::HashMap = if let Some(roles_json) = &eligible_groups[0].auto_add_instance_groups_roles { + serde_json::from_value(roles_json.clone()).unwrap_or_default() + } else { + std::collections::HashMap::new() + }; + + let mut best_group = &eligible_groups[0].group_name; + let mut best_precedence = 0u8; + + for group in &eligible_groups { + let default_role = "developer".to_string(); + let role = roles.get(&group.group_name).unwrap_or(&default_role); + + let precedence = match role.as_str() { + "admin" => 3, + "developer" => 2, + "operator" => 1, + _ => 2, + }; + + if precedence > best_precedence { + best_precedence = precedence; + best_group = &group.group_name; + } + } + + let primary_group_name = best_group; + + // Determine role from group configuration using the selected primary group + let default_role = "developer".to_string(); + let role = roles.get(primary_group_name).unwrap_or(&default_role).as_str(); + + let (is_admin, is_operator) = match role { + "admin" => (true, false), + "operator" => (false, true), + _ => (false, false), + }; + + // Update user with instance group information + let instance_group_source = serde_json::json!({ + "source": "instance_group", + "group": primary_group_name + }); + + sqlx::query!( + "UPDATE usr SET added_via = $1, is_admin = $2, operator = $3 WHERE username = $4 AND workspace_id = $5", + instance_group_source, + is_admin, + is_operator, + username_to_convert, + &w_id + ) + .execute(&mut *tx) + .await?; + + audit_log( + &mut *tx, + &authed, + "users.convert_to_group", + ActionKind::Update, + &w_id, + Some(&username_to_convert), + Some([("group", primary_group_name.as_str()), ("role", role)].into()), + ) + .await?; + + tx.commit().await?; + + handle_deployment_metadata( + &authed.email, + &authed.username, + &db, + &w_id, + windmill_git_sync::DeployedObject::User { email: user_info.email.clone() }, + Some(format!("Converted user '{}' to group user (group: {}, role: {})", &user_info.email, primary_group_name, role)), + true, + ) + .await?; + + Ok(format!("User {} converted to group user (group: {}, role: {})", username_to_convert, primary_group_name, role)) +} + async fn update_user( authed: ApiAuthed, Path(email_to_update): Path, @@ -1459,6 +1592,12 @@ async fn delete_user( .execute(&mut *tx) .await?; } + + // Remove user from all instance groups email_to_igroup + sqlx::query!("DELETE FROM email_to_igroup WHERE email = $1", &email_to_delete) + .execute(&mut *tx) + .await?; + audit_log( &mut *tx, &authed, @@ -2191,8 +2330,8 @@ async fn get_all_runnables( .collect::>(), ); let scripts = sqlx::query!( - "SELECT workspace_id as workspace, path, summary, description, schema FROM script as o - WHERE created_at = (select max(created_at) from script where o.path = path and workspace_id = $1 AND archived = false) + "SELECT workspace_id as workspace, path, summary, description, schema FROM script as o + WHERE created_at = (select max(created_at) from script where o.path = path and workspace_id = $1 AND archived = false) AND workspace_id = $1 and archived = false", workspace ) .fetch_all(&mut *tx) diff --git a/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte b/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte index 48dc119171..89b8c3f1af 100644 --- a/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte +++ b/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte @@ -68,6 +68,25 @@ (filteredUsers || users || []).some((user: User) => user.added_via?.source === 'instance_group' || user.added_via?.source === 'domain') ) + // Function to check if a manual user can be converted to a group user + function canConvertToGroup(user: User): boolean { + // User must be manually added (not via instance group or domain) + if (user.added_via?.source === 'instance_group' || user.added_via?.source === 'domain') { + return false + } + + // Check if user's email is in any configured instance group + const userEmail = user.email + for (const groupName of autoAddInstanceGroups) { + const group = instanceGroups.find(g => g.name === groupName) + if (group && group.emails && group.emails.includes(userEmail)) { + return true + } + } + + return false + } + async function loadSettings(): Promise { const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! }) @@ -195,6 +214,20 @@ } } + async function convertUserToGroup(username: string): Promise { + try { + await UserService.convertUserToGroup({ + workspace: $workspaceStore ?? '', + username + }) + sendUserToast('User converted to group user') + listUsers() + } catch (e) { + console.error('Failed to convert user:', e) + sendUserToast('Failed to convert user', true) + } + } + let domain = $derived($userStore?.email.split('@')[1]) $effect(() => { @@ -220,6 +253,7 @@ let deleteConfirmedCallback: (() => void) | undefined = $state(undefined) let removeInstanceGroupConfirmedCallback: (() => void) | undefined = $state(undefined) + let convertConfirmedCallback: (() => void) | undefined = $state(undefined) async function removeAllInvitesFromDomain() { await Promise.all( @@ -634,7 +668,8 @@ {#if filteredUsers} - {#each sortedUsers().slice(0, nbDisplayed) as { email, username, is_admin, operator, disabled, added_via }, index (email)} + {#each sortedUsers().slice(0, nbDisplayed) as user, index (user.email)} + {@const { email, username, is_admin, operator, disabled, added_via } = user} {#if hasNonManualUsers && index > 0 && sortedUsers()[index - 1]?.added_via?.source !== 'instance_group' && added_via?.source === 'instance_group'} @@ -774,6 +809,40 @@ Cannot remove users synced from instance groups. Either disable the user or remove them from the SCIM group. + {:else if canConvertToGroup(user)} + + {:else} - {/if} - + {#if allowKindChange} + + {/if} + + {:else} +
+ {JSON.stringify(enum_[i])} is not a string, remove it +
+ {/if} {/each} {/if} From 0ad5618aae4c8789648fa5c0aa1ea793986d3657 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 08:02:26 +0000 Subject: [PATCH 23/33] nit dynamic enums --- frontend/src/lib/components/apps/editor/component/components.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/lib/components/apps/editor/component/components.ts b/frontend/src/lib/components/apps/editor/component/components.ts index dbbeba0452..6b1041832a 100644 --- a/frontend/src/lib/components/apps/editor/component/components.ts +++ b/frontend/src/lib/components/apps/editor/component/components.ts @@ -3588,7 +3588,7 @@ See date-fns format for more information. By default, it is 'dd.MM.yyyy HH:mm' fieldType: 'object', value: {}, tooltip: - 'This enables setting form enum values dynamically using an object: keys are field names, and values are arrays of strings.' + 'This enables setting form enum values dynamically using an object: keys are field names, and values are arrays of strings or { "label": "myLabel", "value": "myValue" }.' }, displayType: { From dff499484bbce888553d8671a1d50b8f645db9d9 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 08:04:17 +0000 Subject: [PATCH 24/33] nit svelte 5 --- frontend/src/lib/components/FlowMetadata.svelte | 12 ++++++++---- frontend/src/lib/components/JobStatus.svelte | 6 +++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/components/FlowMetadata.svelte b/frontend/src/lib/components/FlowMetadata.svelte index ef090c9afb..bcb880dd0e 100644 --- a/frontend/src/lib/components/FlowMetadata.svelte +++ b/frontend/src/lib/components/FlowMetadata.svelte @@ -10,9 +10,13 @@ import { Clock, MemoryStick, Calendar, Bot, User, Code2 } from 'lucide-svelte' import BarsStaggered from '$lib/components/icons/BarsStaggered.svelte' - export let job: Job const SMALL_ICON_SIZE = 14 - export let scheduleEditor: ScheduleEditor + interface Props { + job: Job + scheduleEditor: ScheduleEditor + } + + let { job, scheduleEditor }: Props = $props()
Schedule: - + + onclick={() => scheduleEditor?.openEdit(job.schedule_path ?? '', job.job_kind == 'flow')} > {truncateRev(job.schedule_path, 40)} diff --git a/frontend/src/lib/components/JobStatus.svelte b/frontend/src/lib/components/JobStatus.svelte index e97fea156b..a4881df6e8 100644 --- a/frontend/src/lib/components/JobStatus.svelte +++ b/frontend/src/lib/components/JobStatus.svelte @@ -10,7 +10,11 @@ const SMALL_ICON_SIZE = 12 - export let job: QueuedJob | CompletedJob | undefined + interface Props { + job: QueuedJob | CompletedJob | undefined + } + + let { job }: Props = $props() {#if job && 'success' in job && job.success} From 7add57499c02ac53a7f7adbabbb279d7c41ab275 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 08:23:09 +0000 Subject: [PATCH 25/33] fix: improve graph rendering performances --- .../lib/components/graph/renderers/nodes/AIToolNode.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/lib/components/graph/renderers/nodes/AIToolNode.svelte b/frontend/src/lib/components/graph/renderers/nodes/AIToolNode.svelte index 36235add24..da2a1f1500 100644 --- a/frontend/src/lib/components/graph/renderers/nodes/AIToolNode.svelte +++ b/frontend/src/lib/components/graph/renderers/nodes/AIToolNode.svelte @@ -183,8 +183,9 @@ } } - const sortedNewNodes = clone(nodes) + const sortedNewNodes = nodes .filter((n) => n.type !== 'asset') + .map((n) => ({ id: n.id, position: $state.snapshot(n.position) })) .sort((a, b) => a.position.y - b.position.y) let currentYOffset = 0 let prevYPos = NaN @@ -234,7 +235,6 @@ import { MessageCircle, Play, Wrench, X } from 'lucide-svelte' import { twMerge } from 'tailwind-merge' import { getContext } from 'svelte' - import { clone } from '$lib/utils' import type { Edge, Node } from '@xyflow/svelte' import type { Writable } from 'svelte/store' From 311b410f2f65c3bdfc483c80cc5ef72b6864118a Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 08:48:01 +0000 Subject: [PATCH 26/33] fix: improve performance of flow viewer --- .../FlowLoopIterationPreview.svelte | 6 +- .../lib/components/FlowStatusViewer.svelte | 2 +- .../components/flows/FlowProgressBar.svelte | 50 ++++--- .../graph/renderers/edges/EmptyEdge.svelte | 45 ++++--- .../components/progressBar/ProgressBar.svelte | 123 ++++++++++-------- 5 files changed, 132 insertions(+), 94 deletions(-) diff --git a/frontend/src/lib/components/FlowLoopIterationPreview.svelte b/frontend/src/lib/components/FlowLoopIterationPreview.svelte index 6c296d0804..7c9bc78fa0 100644 --- a/frontend/src/lib/components/FlowLoopIterationPreview.svelte +++ b/frontend/src/lib/components/FlowLoopIterationPreview.svelte @@ -67,7 +67,7 @@ let selectedJobStep: string | undefined = $state(undefined) let isRunning: boolean = $state(false) - let jobProgressReset: (() => void) | undefined = $state(undefined) + let progressBar: FlowProgressBar | undefined = $state(undefined) export function test() { runPreview(previewArgs, undefined) @@ -80,7 +80,7 @@ args: Record, restartedFrom: RestartedFrom | undefined ) { - jobProgressReset?.() + progressBar?.reset() const newFlow = { value: { modules }, summary: '' } jobId = await runFlowPreview(args, newFlow, $pathStore, restartedFrom) isRunning = true @@ -161,7 +161,7 @@
- +
diff --git a/frontend/src/lib/components/FlowStatusViewer.svelte b/frontend/src/lib/components/FlowStatusViewer.svelte index 7d5c1c4a12..70c331a80b 100644 --- a/frontend/src/lib/components/FlowStatusViewer.svelte +++ b/frontend/src/lib/components/FlowStatusViewer.svelte @@ -88,7 +88,7 @@ retryStatus.val = {} suspendStatus.val = {} globalRefreshes = {} - flowState.val = {} + flowState = {} localDurationStatuses = {} localModuleStates = {} } diff --git a/frontend/src/lib/components/flows/FlowProgressBar.svelte b/frontend/src/lib/components/flows/FlowProgressBar.svelte index 39ccbbf424..4837db5e43 100644 --- a/frontend/src/lib/components/flows/FlowProgressBar.svelte +++ b/frontend/src/lib/components/flows/FlowProgressBar.svelte @@ -2,18 +2,27 @@ import { type Job } from '$lib/gen' import ProgressBar from '../progressBar/ProgressBar.svelte' - export let job: Job | undefined = undefined - export let currentSubJobProgress: number | undefined = undefined + interface Props { + job?: Job | undefined + currentSubJobProgress?: number | undefined + class?: string + } - let error: number | undefined = undefined - let index = 0 - let subIndex: number | undefined = undefined - let subLength: number | undefined = undefined - let length = 1 - let nextInProgress = false - let subIndexIsPercent: boolean = false + let { + job = undefined, + currentSubJobProgress = $bindable(undefined), + class: className + }: Props = $props() - $: if (job) updateJobProgress(job) + let error: number | undefined = $state(undefined) + let index = $state(0) + let subIndex: number | undefined = $state(undefined) + let subLength: number | undefined = $state(undefined) + let length = $state(1) + let nextInProgress = $state(false) + let subIndexIsPercent: boolean = $state(false) + + let progressBar = $state(undefined) function updateJobProgress(job: Job) { const modules = job?.flow_status?.modules @@ -40,8 +49,8 @@ newError = maxDone maxDone = maxDone + 1 } - } - subIndexIsPercent = false; + } + subIndexIsPercent = false // Loop is still iterating if (module?.iterator) { @@ -54,12 +63,12 @@ } else if (module?.branchall) { subStepIndex = module.branchall.branch subStepLength = module.branchall.len - } else if (module?.progress) { + } else if (module?.progress) { const clamp = (num, min, max) => Math.min(Math.max(num, min), max) subStepIndex = clamp(module?.progress, subIndex ?? 0, 99) // Jitter protection >^^^^^^^^ subStepLength = 100 - subIndexIsPercent = true; + subIndexIsPercent = true currentSubJobProgress = subStepIndex } else { currentSubJobProgress = undefined @@ -73,26 +82,27 @@ nextInProgress = newNextInProgress } - let resetP: any - export function reset() { - resetP?.() + progressBar?.resetP() error = undefined subIndex = undefined subLength = undefined length = 1 index = 0 } + $effect(() => { + job && updateJobProgress(job) + }) diff --git a/frontend/src/lib/components/graph/renderers/edges/EmptyEdge.svelte b/frontend/src/lib/components/graph/renderers/edges/EmptyEdge.svelte index 6c8c70d9ed..47a099b7c8 100644 --- a/frontend/src/lib/components/graph/renderers/edges/EmptyEdge.svelte +++ b/frontend/src/lib/components/graph/renderers/edges/EmptyEdge.svelte @@ -4,28 +4,43 @@ import type { Writable } from 'svelte/store' import { twMerge } from 'tailwind-merge' - export let sourceX: number - export let sourceY: number - export let sourcePosition: Position - export let targetX: number - export let targetY: number - export let targetPosition: Position - export let markerEnd: string | undefined = undefined - export let data: { class?: string } = {} + interface Props { + sourceX: number + sourceY: number + sourcePosition: Position + targetX: number + targetY: number + targetPosition: Position + markerEnd?: string | undefined + data?: { class?: string } + } - const { useDataflow } = getContext<{ - useDataflow: Writable - }>('FlowGraphContext') - - $: [edgePath] = getBezierPath({ + let { sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition, - curvature: 0.25 - }) + markerEnd = undefined, + data = {} + }: Props = $props() + + const { useDataflow } = getContext<{ + useDataflow: Writable + }>('FlowGraphContext') + + let [edgePath] = $derived( + getBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + curvature: 0.25 + }) + ) - import { tweened } from 'svelte/motion' + import { Tween } from 'svelte/motion' import { linear } from 'svelte/easing' import { twMerge } from 'tailwind-merge' - function getTween(initialValue = 0, duration = 200) { - return tweened(initialValue, { - duration, - easing: linear - }) + // Remove padding/margin, border radius and titles + + interface Props { + error?: number | undefined + index: number + subIndex: number | undefined + subLength: number | undefined + nextInProgress?: boolean + // Used for displaying progress of subjob of flow + subIndexIsPercent?: boolean + // Used in individual job test runs + compact?: boolean + // Removes `Step 1` and replaces it with `Running` + hideStepTitle?: boolean + length: number + class?: string } - export let error: number | undefined = undefined - export let index: number - export let subIndex: number | undefined - export let subLength: number | undefined - export let nextInProgress: boolean = false - // Used for displaying progress of subjob of flow - export let subIndexIsPercent: boolean = false - // Remove padding/margin, border radius and titles - // Used in individual job test runs - export let compact: boolean = false - // Removes `Step 1` and replaces it with `Running` - export let hideStepTitle: boolean = false - - export let length: number + let { + error = undefined, + index, + subIndex, + subLength, + nextInProgress = false, + subIndexIsPercent = false, + compact = false, + hideStepTitle = false, + length, + class: className = '' + }: Props = $props() let duration = 200 - let percent = getTween(0, duration) + let percent = new Tween(0, { duration, easing: linear }) export function resetP() { - percent = getTween(0, duration) + percent.set(0, { duration: 0 }) } - $: percent.set( - (length - ? index / length + (subIndex && subLength ? subIndex / (subLength ?? 1) / length : 0) - : 0) * 100 - ) + $effect(() => { + percent.set( + (length + ? index / length + (subIndex && subLength ? subIndex / (subLength ?? 1) / length : 0) + : 0) * 100 + ) + }) function getPercent(partIndex: number, _pct: number) { if (!length) { return 0 } - const res = Math.min(($percent - (partIndex / length) * 100) * length, 100) + const res = Math.min((percent.current - (partIndex / length) * 100) * length, 100) return res } - $: finished = index == length + let finished = $derived(index == length) -
+
{#if !compact} -
- - {error != undefined - ? 'Error occured' - : finished - ? 'Done' - : hideStepTitle - ? `Running` - : subIndexIsPercent - ? `Step ${index + 1} (${subIndex !== undefined ? `${subIndex}%)` : ''}` - : `Step ${index + 1}${subIndex !== undefined ? `.${subIndex + 1}` : ''}`} - - - {$percent.toFixed(0)}% - -
+
+ + {error != undefined + ? 'Error occured' + : finished + ? 'Done' + : hideStepTitle + ? `Running` + : subIndexIsPercent + ? `Step ${index + 1} (${subIndex !== undefined ? `${subIndex}%)` : ''}` + : `Step ${index + 1}${subIndex !== undefined ? `.${subIndex + 1}` : ''}`} + + + {percent.current.toFixed(0)}% + +
{/if} -
+
{#each new Array(length) as _, partIndex (partIndex)}
{#if partIndex == index && nextInProgress} @@ -101,7 +114,7 @@ class="absolute left-0 bottom-0 h-full {error == partIndex ? 'bg-red-400' : 'bg-blue-400'}" - style="width: {getPercent(partIndex, $percent)}%" + style="width: {getPercent(partIndex, percent.current)}%" >
{/if}
From f28ed9a5f5c6032c49734e6770c2f2c9e2e4a001 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 09:40:33 +0000 Subject: [PATCH 27/33] fix: limit auto data tables to tables of col < 100 --- .../src/lib/components/DisplayResult.svelte | 38 +++++++++------- frontend/src/lib/components/table/Cell.svelte | 45 +++++++++++++------ frontend/src/lib/utils.ts | 39 ++++++++-------- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/frontend/src/lib/components/DisplayResult.svelte b/frontend/src/lib/components/DisplayResult.svelte index 1c03241e00..4b5b22e34d 100644 --- a/frontend/src/lib/components/DisplayResult.svelte +++ b/frontend/src/lib/components/DisplayResult.svelte @@ -36,7 +36,7 @@ import Tooltip from './Tooltip.svelte' import HighlightTheme from './HighlightTheme.svelte' import type { DisplayResultUi } from './custom_ui' - import { getContext, hasContext, createEventDispatcher, onDestroy } from 'svelte' + import { getContext, hasContext, createEventDispatcher, onDestroy, untrack } from 'svelte' import { toJsonStr } from '$lib/utils' import { userStore } from '$lib/stores' import ResultStreamDisplay from './ResultStreamDisplay.svelte' @@ -139,24 +139,25 @@ function isTableRowObject(json) { // check array of objects (with possible a first row of headers) + return ( + isTableRowObjectWithoutHeaders(json, true) || + (Array.isArray(json[0]) && + json[0].length > 0 && + json[0].length <= 100 && + json[0].every((item) => typeof item === 'string') && + isTableRowObjectWithoutHeaders(json.slice(1), false)) + ) + } + + function isTableRowObjectWithoutHeaders(json: any, checkFirst) { return ( Array.isArray(json) && json.length > 0 && - (json.every( - (item) => - item && typeof item === 'object' && Object.keys(item).length > 0 && !Array.isArray(item) - ) || - (Array.isArray(json[0]) && - json[0].every((item) => typeof item === 'string') && - json - .slice(1) - .every( - (item) => - item && - typeof item === 'object' && - Object.keys(item).length > 0 && - !Array.isArray(item) - ))) + (!checkFirst || + (json[0] && typeof json[0] === 'object' && Object.keys(json[0]).length <= 100)) && + json.every((item) => { + return typeof item === 'object' && Object.keys(item).length > 0 && !Array.isArray(item) + }) ) } @@ -200,6 +201,7 @@ } let size = roughSizeOfObject(result) + console.debug('size of object', size) // Otherwise, check if the result is too large (10kb) for json if (size > TABLE_MAX_SIZE) { @@ -479,7 +481,9 @@ $effect(() => { ;[result] - resultKind = inferResultKind(result) + untrack(() => { + resultKind = inferResultKind(result) + }) }) $effect(() => { chooseToolbarLocation( diff --git a/frontend/src/lib/components/table/Cell.svelte b/frontend/src/lib/components/table/Cell.svelte index ab43a10c2f..d5fc30f811 100644 --- a/frontend/src/lib/components/table/Cell.svelte +++ b/frontend/src/lib/components/table/Cell.svelte @@ -3,25 +3,42 @@ import { twMerge } from 'tailwind-merge' import type { DatatableContext } from './DataTable.svelte' - export let first: boolean = false - export let last: boolean = false - export let numeric: boolean = false - export let head: boolean = false - export let shouldStopPropagation: boolean = false - export let selected = false - export let sticky: boolean = false - export let wrap: boolean = false + interface Props { + first?: boolean + last?: boolean + numeric?: boolean + head?: boolean + shouldStopPropagation?: boolean + selected?: boolean + sticky?: boolean + wrap?: boolean + children?: import('svelte').Snippet + [key: string]: any + } + + let { + first = false, + last = false, + numeric = false, + head = false, + shouldStopPropagation = false, + selected = false, + sticky = false, + wrap = false, + children, + ...rest + }: Props = $props() let Tag = head ? 'th' : 'td' const { size } = getContext('datatable') - + { + {...rest} + onclick={(e) => { if (shouldStopPropagation) e.stopPropagation() }} class={twMerge( @@ -41,14 +58,14 @@ size === 'xs' ? 'px-1 py-1.5' : '', selected ? 'bg-blue-50 dark:bg-blue-900/50' : '', 'transition-all', - $$restProps.class + rest.class )} > {#if sticky}
- + {@render children?.()}
{:else} - + {@render children?.()} {/if}
diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 1f4c65d897..8e84df9a9f 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -172,9 +172,9 @@ export function displayDate( } const dateChoices: Intl.DateTimeFormatOptions = displayDate ? { - day: 'numeric', - month: 'numeric' - } + day: 'numeric', + month: 'numeric' + } : {} return date.toLocaleString(undefined, { ...timeChoices, @@ -1065,25 +1065,24 @@ export async function tryEvery({ try { await tryCode() break - } catch (err) {} + } catch (err) { } i++ } if (i >= times) { timeoutCode() } } - -export function roughSizeOfObject(object: object | string) { - if (typeof object == 'string') { +export function roughSizeOfObject(object: object | string | any) { + if (typeof object === 'string') { return object.length * 2 } - var objectList: any[] = [] - var stack = [object] - var bytes = 0 + const visited = new Set() + const stack = [object] + let bytes = 0 while (stack.length) { - let value: any = stack.pop() + const value = stack.pop() if (typeof value === 'boolean') { bytes += 4 @@ -1091,12 +1090,12 @@ export function roughSizeOfObject(object: object | string) { bytes += value.length * 2 } else if (typeof value === 'number') { bytes += 8 - } else if (typeof value === 'object' && objectList.indexOf(value) === -1) { - objectList.push(value) + } else if (typeof value === 'object' && value !== null && !visited.has(value)) { + visited.add(value) - for (var i in value) { - bytes += 2 * i.length - stack.push(value[i]) + for (const key in value) { + bytes += 2 * key.length + stack.push(value[key]) } } } @@ -1332,7 +1331,7 @@ export function conditionalMelt(node: HTMLElement, meltItem: AnyMeltElement | un if (meltItem) { return meltItem(node) } - return { destroy: () => {} } + return { destroy: () => { } } } export type Item = { @@ -1538,9 +1537,9 @@ export type S3Uri = `s3://${string}/${string}` export type S3Object = | S3Uri | { - s3: string - storage?: string - } + s3: string + storage?: string + } export function parseS3Object(s3Object: S3Object): { s3: string; storage?: string } { if (typeof s3Object === 'object') return s3Object From 2421bdb8399e3510eddecb64035849d28351a4da Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 09:45:31 +0000 Subject: [PATCH 28/33] nit --- frontend/src/lib/components/DisplayResult.svelte | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/frontend/src/lib/components/DisplayResult.svelte b/frontend/src/lib/components/DisplayResult.svelte index 4b5b22e34d..f27c821139 100644 --- a/frontend/src/lib/components/DisplayResult.svelte +++ b/frontend/src/lib/components/DisplayResult.svelte @@ -149,14 +149,20 @@ ) } - function isTableRowObjectWithoutHeaders(json: any, checkFirst) { + function isTableRowObjectWithoutHeaders(json: any, skipMaxColCheck: boolean) { return ( Array.isArray(json) && json.length > 0 && - (!checkFirst || - (json[0] && typeof json[0] === 'object' && Object.keys(json[0]).length <= 100)) && json.every((item) => { - return typeof item === 'object' && Object.keys(item).length > 0 && !Array.isArray(item) + if (item && typeof item === 'object') { + let keys = Object.keys(item) + if (keys.length > 0 && !Array.isArray(item)) { + if (skipMaxColCheck || keys.length <= 100) { + return true + } + } + } + return false }) ) } From 2a62f8ff8f99314f6360a380a5b80183aeee08a0 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 09:50:58 +0000 Subject: [PATCH 29/33] nit --- .../src/lib/components/DisplayResult.svelte | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/frontend/src/lib/components/DisplayResult.svelte b/frontend/src/lib/components/DisplayResult.svelte index f27c821139..6eb2aea055 100644 --- a/frontend/src/lib/components/DisplayResult.svelte +++ b/frontend/src/lib/components/DisplayResult.svelte @@ -139,25 +139,26 @@ function isTableRowObject(json) { // check array of objects (with possible a first row of headers) - return ( - isTableRowObjectWithoutHeaders(json, true) || - (Array.isArray(json[0]) && - json[0].length > 0 && - json[0].length <= 100 && - json[0].every((item) => typeof item === 'string') && - isTableRowObjectWithoutHeaders(json.slice(1), false)) - ) + const hasHeaders = + Array.isArray(json[0]) && + json[0].length > 0 && + json[0].length <= 100 && + json[0].every((item) => typeof item === 'string') + return isTableRowObjectInner(json, hasHeaders) } - function isTableRowObjectWithoutHeaders(json: any, skipMaxColCheck: boolean) { + function isTableRowObjectInner(json: any, hasHeaders: boolean) { return ( Array.isArray(json) && - json.length > 0 && - json.every((item) => { + json.length > (hasHeaders ? 1 : 0) && + json.every((item, index) => { + if (hasHeaders && index === 0) { + return true + } if (item && typeof item === 'object') { let keys = Object.keys(item) if (keys.length > 0 && !Array.isArray(item)) { - if (skipMaxColCheck || keys.length <= 100) { + if (hasHeaders || keys.length <= 100) { return true } } From a1db857be4c239b79bfda16de8be53cbfb034148 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 10:10:27 +0000 Subject: [PATCH 30/33] nit perf --- .../src/lib/components/DisplayResult.svelte | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/frontend/src/lib/components/DisplayResult.svelte b/frontend/src/lib/components/DisplayResult.svelte index 6eb2aea055..3b962a4eee 100644 --- a/frontend/src/lib/components/DisplayResult.svelte +++ b/frontend/src/lib/components/DisplayResult.svelte @@ -142,7 +142,7 @@ const hasHeaders = Array.isArray(json[0]) && json[0].length > 0 && - json[0].length <= 100 && + json[0].length <= 50 && json[0].every((item) => typeof item === 'string') return isTableRowObjectInner(json, hasHeaders) } @@ -158,7 +158,7 @@ if (item && typeof item === 'object') { let keys = Object.keys(item) if (keys.length > 0 && !Array.isArray(item)) { - if (hasHeaders || keys.length <= 100) { + if (hasHeaders || keys.length <= 50) { return true } } @@ -372,28 +372,23 @@ json.length > 0 && Array.isArray(json[0]) && json[0].length > 0 && - json[0].every((item) => typeof item === 'string') && - json - .slice(1) - .every( - (item) => - item && typeof item === 'object' && Object.keys(item).length > 0 && !Array.isArray(item) - ) + json[0].every((item) => typeof item === 'string') ) { const headers = json[0] - const rows = json.slice(1) + const rows: { [key: string]: string }[] = new Array(json.length - 1) - const result = rows.map((row) => { + for (let i = 1; i < json.length; i++) { const obj: { [key: string]: string } = {} + const row = json[i] - for (const header of headers) { - obj[header] = row[header] + for (let j = 0; j < headers.length; j++) { + obj[headers[j]] = row[headers[j]] } - return obj - }) + rows[i - 1] = obj + } - return result + return rows } return json From 9b13dd99ba831d8db708622e7e743f2b2b3ae67e Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 10:15:38 +0000 Subject: [PATCH 31/33] nit perf --- .../lib/components/jobs/JobProgressBar.svelte | 77 +++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/frontend/src/lib/components/jobs/JobProgressBar.svelte b/frontend/src/lib/components/jobs/JobProgressBar.svelte index 4b4684e32a..875b36d494 100644 --- a/frontend/src/lib/components/jobs/JobProgressBar.svelte +++ b/frontend/src/lib/components/jobs/JobProgressBar.svelte @@ -2,38 +2,45 @@ import { type Job } from '$lib/gen' import ProgressBar from '../progressBar/ProgressBar.svelte' - export let job: Job | undefined = undefined - export let compact: boolean = false; - /// Progress of currently running job - export let scriptProgress: number | undefined = undefined; - // Removes `Step 1` and replaces it with `Running` - export let hideStepTitle: boolean = false - - let error: number | undefined = undefined - let index = 0 - let subIndex: number = 0 - let subLength: number = 100 - let length = 1 - let nextInProgress = false - - $: if (job) updateJobProgress(job); - $: subIndex = scriptProgress ?? 0; - - function updateJobProgress(job: Job) { - if (!job['running'] && !job['success']){ - error = 0; - } else { - error = undefined; - } - // Anything that is success automatically gets 100% progress - if (job['success'] && scriptProgress) - index = 1, subLength = 0, subIndex = 0, scriptProgress = 100; + interface Props { + job?: Job | undefined + compact?: boolean + /// Progress of currently running job + scriptProgress?: number | undefined + // Removes `Step 1` and replaces it with `Running` + hideStepTitle?: boolean + class?: string } - let resetP: any + let { + job = undefined, + compact = $bindable(false), + scriptProgress = $bindable(undefined), + hideStepTitle = $bindable(false), + class: className = $bindable('') + }: Props = $props() + + let error: number | undefined = $state(undefined) + let index = $state(0) + let subIndex: number = $state(0) + let subLength: number = $state(100) + let length = $state(1) + let nextInProgress = false + + let progressBar: ProgressBar | undefined = $state(undefined) + function updateJobProgress(job: Job) { + if (!job['running'] && !job['success']) { + error = 0 + } else { + error = undefined + } + // Anything that is success automatically gets 100% progress + if (job['success'] && scriptProgress) + ((index = 1), (subLength = 0), (subIndex = 0), (scriptProgress = 100)) + } export function reset() { - resetP?.() + progressBar?.resetP() error = undefined subIndex = 0 subLength = 100 @@ -42,17 +49,23 @@ scriptProgress = undefined } + $effect(() => { + if (job) updateJobProgress(job) + }) + $effect(() => { + subIndex = scriptProgress ?? 0 + }) From 7df13b3e7bb095475d0fe54b7f635e3861fb0f73 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 27 Sep 2025 11:19:58 +0000 Subject: [PATCH 32/33] fix: restore set_progress feature with sse --- backend/windmill-api/src/jobs.rs | 25 +++++++--- frontend/src/lib/components/JobArgs.svelte | 36 +++++++------ frontend/src/lib/components/JobLoader.svelte | 50 +++---------------- .../flows/content/FlowModuleComponent.svelte | 1 - .../lib/components/jobs/JobProgressBar.svelte | 17 +++++-- .../(root)/(logged)/run/[...run]/+page.svelte | 6 ++- 6 files changed, 64 insertions(+), 71 deletions(-) diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 10edb6ff94..e370896b52 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -6702,7 +6702,7 @@ async fn get_job_update( &job_id, log_offset, stream_offset, - get_progress, + get_progress.unwrap_or(false), running, true, false, @@ -6806,6 +6806,7 @@ fn start_job_update_sse_stream( // Send initial update immediately let mut running = running; let mut mem_peak = 0; + match get_job_update_data( &opt_authed, &opt_tokened, @@ -6814,7 +6815,7 @@ fn start_job_update_sse_stream( &job_id, log_offset, stream_offset, - get_progress, + false, running, true, true, @@ -6871,11 +6872,12 @@ fn start_job_update_sse_stream( } } + let mut get_progress_m: bool = false; // Poll for updates every 1 second let mut i = 0; let start = Instant::now(); let mut last_ping = Instant::now(); - + let mut last_progress_check = Instant::now(); loop { i += 1; @@ -6918,6 +6920,10 @@ fn start_job_update_sse_stream( } tokio::time::sleep(std::time::Duration::from_millis(ms_duration)).await; + // Check progress if the user requested it, and check periodically if the job has progress + // Once it has progress, we always check progress + let check_progress = get_progress.unwrap_or(false) + && (get_progress_m || last_progress_check.elapsed().as_secs() > 5); match get_job_update_data( &opt_authed, &opt_tokened, @@ -6926,7 +6932,7 @@ fn start_job_update_sse_stream( &job_id, log_offset, stream_offset, - get_progress, + check_progress, running, false, true, @@ -6947,6 +6953,13 @@ fn start_job_update_sse_stream( if update.new_logs.as_ref().is_some_and(|x| x.is_empty()) { update.new_logs = None; } + if check_progress { + if update.progress.is_some() { + get_progress_m = true; + } else { + last_progress_check = Instant::now(); + } + } // if !only_result.unwrap_or(false) { // tracing::error!("update {:?}", update); @@ -7044,7 +7057,7 @@ async fn get_job_update_data( job_id: &Uuid, log_offset: Option, stream_offset: Option, - get_progress: Option, + get_progress: bool, running: Option, log_view: bool, get_full_job_on_completion: bool, @@ -7281,7 +7294,7 @@ async fn get_job_update_data( log_offset, w_id, job_id, - get_progress.unwrap_or(false), + get_progress, running, tags.as_ref().map(|v| v.as_slice()) as Option<&[&str]>, no_logs.unwrap_or(false), diff --git a/frontend/src/lib/components/JobArgs.svelte b/frontend/src/lib/components/JobArgs.svelte index 67435ebc09..00211790bc 100644 --- a/frontend/src/lib/components/JobArgs.svelte +++ b/frontend/src/lib/components/JobArgs.svelte @@ -14,14 +14,18 @@ import { deepEqual } from 'fast-equals' import { isWindmillTooBigObject } from './job_args' - export let id: string | undefined = undefined - export let args: any - export let argLabel: string | undefined = undefined - export let workspace: string | undefined = undefined + interface Props { + id?: string | undefined + args: any + argLabel?: string | undefined + workspace?: string | undefined + } - let jsonViewer: Drawer - let runLocally: Drawer - let jsonStr = '' + let { id = undefined, args, argLabel = undefined, workspace = undefined }: Props = $props() + + let jsonViewer: Drawer | undefined = $state() + let runLocally: Drawer | undefined = $state() + let jsonStr = $state('') function pythonCode() { return ` @@ -53,9 +57,9 @@ ${Object.entries(args) } -{#if args && typeof args === 'object' && deepEqual( Object.keys(args), ['reason'] ) && args['reason'] == 'PREPROCESSOR_ARGS_ARE_DISCARDED'} +{#if args && typeof args === 'object' && deepEqual( Object.keys(args ?? {}), ['reason'] ) && args['reason'] == 'PREPROCESSOR_ARGS_ARE_DISCARDED'} Preprocessor args are discarded -{:else if id && workspace && args && typeof args === 'object' && deepEqual( Object.keys(args), ['reason'] ) && args['reason'] == 'WINDMILL_TOO_BIG'} +{:else if id && workspace && args && typeof args === 'object' && deepEqual( Object.keys(args ?? {}), ['reason'] ) && args['reason'] == 'WINDMILL_TOO_BIG'} The args are too big in size to be able to fetch alongside job. Please download the JSON file to view them{argLabel ?? 'Arg'} Value - + {#snippet headerAction()} - + {/snippet} - {#if args && typeof args === 'object' && Object.keys(args).length > 0} - {#each Object.entries(args).sort((a, b) => a[0].localeCompare(b[0])) as [arg, value]} + {#if args && typeof args === 'object' && Object.keys(args ?? {}).length > 0} + {#each Object.entries(args ?? {}).sort( (a, b) => a?.[0]?.localeCompare(b?.[0]) ) as [arg, value]} {arg} @@ -124,7 +128,7 @@ ${Object.entries(args) Download