diff --git a/backend/windmill-api/src/triggers/http/handler.rs b/backend/windmill-api/src/triggers/http/handler.rs index a887dc32a7..5d8898f121 100644 --- a/backend/windmill-api/src/triggers/http/handler.rs +++ b/backend/windmill-api/src/triggers/http/handler.rs @@ -62,7 +62,9 @@ pub async fn increase_trigger_version(tx: &mut PgConnection) -> Result<()> { } pub fn generate_route_path_key(route_path: &str) -> String { - ROUTE_PATH_KEY_RE.replace_all(route_path, "/*").to_string() + ROUTE_PATH_KEY_RE + .replace_all(route_path, "${1}${2}key") + .to_string() } pub async fn route_path_key_exists( @@ -322,7 +324,7 @@ async fn check_if_route_exist( workspace_id: &str, trigger_path: Option<&str>, ) -> Result { - let route_path_key = ROUTE_PATH_KEY_RE.replace_all(&config.route_path, ":key"); + let route_path_key = generate_route_path_key(&config.route_path); let exists = route_path_key_exists( &route_path_key, @@ -340,7 +342,7 @@ async fn check_if_route_exist( )); } - Ok(route_path_key.into_owned()) + Ok(route_path_key) } pub struct HttpTrigger; diff --git a/backend/windmill-api/src/triggers/http/mod.rs b/backend/windmill-api/src/triggers/http/mod.rs index 2c5df24913..55844e0fb1 100644 --- a/backend/windmill-api/src/triggers/http/mod.rs +++ b/backend/windmill-api/src/triggers/http/mod.rs @@ -192,8 +192,9 @@ impl<'de> Deserialize<'de> for HttpConfigRequest { // Regex patterns for route validation lazy_static::lazy_static! { - static ref ROUTE_PATH_KEY_RE: regex::Regex = regex::Regex::new(r"/?:[-\w]+").unwrap(); - static ref VALID_ROUTE_PATH_RE: regex::Regex = regex::Regex::new(r"^:?[-\w]+(/:?[-\w]+)*$").unwrap(); + // Matches named params like :id or wildcards like :* or * + static ref ROUTE_PATH_KEY_RE: regex::Regex = regex::Regex::new(r"(/)?(:|\*)[-\w]+").unwrap(); + static ref VALID_ROUTE_PATH_RE: regex::Regex = regex::Regex::new(r"^(\*[-\w]+$|:?[-\w]+)(/(\*[-\w]+$|:?[-\w]+))*$").unwrap(); } #[derive(Deserialize)] diff --git a/backend/windmill-common/src/worker.rs b/backend/windmill-common/src/worker.rs index f2e5da5057..2a346ea2b3 100644 --- a/backend/windmill-common/src/worker.rs +++ b/backend/windmill-common/src/worker.rs @@ -409,10 +409,6 @@ fn format_pull_query(peek: String) -> String { raw_flow, script_entrypoint_override, preprocessed FROM v2_job WHERE id = (SELECT id FROM peek) - ), delete_debounce AS NOT MATERIALIZED ( - DELETE FROM debounce_key - USING j - WHERE j.kind::text != 'flowdependencies' AND j.kind::text != 'appdependencies' AND j.kind::text != 'dependencies' AND debounce_key.job_id = j.id ) SELECT j.id, j.workspace_id, j.parent_job, j.created_by, started_at, scheduled_for, j.runnable_id, j.runnable_path, j.args, canceled_by, canceled_reason, j.kind, j.trigger, j.trigger_kind, j.permissioned_as, @@ -430,7 +426,7 @@ fn format_pull_query(peek: String) -> String { ", peek ); - // tracing::debug!("pull query: {}", r); + tracing::debug!("pull query: {}", r); r } diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index acf0b5df76..6112045b73 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -3622,8 +3622,8 @@ pub async fn push<'c, 'd>( cache_ttl, dedicated_worker, _low_level_priority, - custom_debounce_key, - debounce_delay_s, + mut custom_debounce_key, + mut debounce_delay_s, ) = match job_payload { JobPayload::ScriptHash { hash, @@ -4391,6 +4391,14 @@ pub async fn push<'c, 'd>( ), }; + if custom_debounce_key.is_some() { + tracing::warn!("debouncing has been disabled temporarily, ignoring debounce_key"); + custom_debounce_key = None; + } + if debounce_delay_s.is_some() { + tracing::warn!("debouncing has been disabled temporarily, ignoring debounce_delay_s"); + debounce_delay_s = None; + } // Enforce concurrency limit on all dependency jobs. // TODO: We can ignore this for scripts djobs. The main reason we need all djobs to be sequential is because we have // nodes_to_relock and we need all locks whose corresponding steps aren't in nodes_to_relock be already present. diff --git a/cli/src/commands/script/script.ts b/cli/src/commands/script/script.ts index c35e2c56f7..03c03b4fe9 100644 --- a/cli/src/commands/script/script.ts +++ b/cli/src/commands/script/script.ts @@ -142,7 +142,7 @@ export async function findResourceFile(path: string) { if (validCandidates.length > 1) { throw new Error( "Found two resource files for the same resource" + - validCandidates.join(", ") + validCandidates.join(", ") ); } if (validCandidates.length < 1) { @@ -214,7 +214,8 @@ export async function handleFile( if (codebase.customBundler) { log.info(`Using custom bundler ${codebase.customBundler} for ${path}`); bundleContent = execSync( - codebase.customBundler + " " + path + codebase.customBundler + " " + path, + { maxBuffer: 1024 * 1024 * 50 } ).toString(); log.info("Custom bundler executed for " + path); } else { @@ -273,20 +274,20 @@ export async function handleFile( let typed = opts?.skipScriptsMetadata ? undefined : ( - await parseMetadataFile( - remotePath, - opts - ? { - ...opts, - path, - workspaceRemote: workspace, - schemaOnly: codebase ? true : undefined, - } - : undefined, - globalDeps, - codebases - ) - )?.payload; + await parseMetadataFile( + remotePath, + opts + ? { + ...opts, + path, + workspaceRemote: workspace, + schemaOnly: codebase ? true : undefined, + } + : undefined, + globalDeps, + codebases + ) + )?.payload; const workspaceId = workspace.workspaceId; @@ -373,19 +374,19 @@ export async function handleFile( deepEqual(typed.schema, remote.schema) && typed.tag == remote.tag && (typed.ws_error_handler_muted ?? false) == - remote.ws_error_handler_muted && + remote.ws_error_handler_muted && typed.dedicated_worker == remote.dedicated_worker && typed.cache_ttl == remote.cache_ttl && typed.concurrency_time_window_s == - remote.concurrency_time_window_s && + remote.concurrency_time_window_s && typed.concurrent_limit == remote.concurrent_limit && Boolean(typed.restart_unless_cancelled) == - Boolean(remote.restart_unless_cancelled) && + Boolean(remote.restart_unless_cancelled) && Boolean(typed.visible_to_runner_only) == - Boolean(remote.visible_to_runner_only) && + Boolean(remote.visible_to_runner_only) && Boolean(typed.no_main_func) == Boolean(remote.no_main_func) && Boolean(typed.has_preprocessor) == - Boolean(remote.has_preprocessor) && + Boolean(remote.has_preprocessor) && typed.priority == Boolean(remote.priority) && typed.timeout == remote.timeout && //@ts-ignore @@ -478,8 +479,7 @@ async function createScript( }); } catch (e: any) { throw Error( - `Script creation for ${body.path} with parent ${ - body.parent_hash + `Script creation for ${body.path} with parent ${body.parent_hash } was not successful: ${e.body ?? e.message} ` ); } @@ -505,8 +505,7 @@ async function createScript( }); if (req.status != 201) { throw Error( - `Script snapshot creation was not successful: ${req.status} - ${ - req.statusText + `Script snapshot creation was not successful: ${req.status} - ${req.statusText } - ${await req.text()} ` ); } @@ -518,8 +517,8 @@ export async function findContentFile(filePath: string) { const candidates = filePath.endsWith("script.json") ? exts.map((x) => filePath.replace(".script.json", x)) : filePath.endsWith("script.lock") - ? exts.map((x) => filePath.replace(".script.lock", x)) - : exts.map((x) => filePath.replace(".script.yaml", x)); + ? exts.map((x) => filePath.replace(".script.lock", x)) + : exts.map((x) => filePath.replace(".script.yaml", x)); const validCandidates = ( await Promise.all( @@ -538,7 +537,7 @@ export async function findContentFile(filePath: string) { if (validCandidates.length > 1) { throw new Error( "No content path given and more than one candidate found: " + - validCandidates.join(", ") + validCandidates.join(", ") ); } if (validCandidates.length < 1) { diff --git a/frontend/src/lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte b/frontend/src/lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte index 16f49f7d03..56d965be75 100644 --- a/frontend/src/lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte +++ b/frontend/src/lib/components/common/confirmationModal/UnsavedConfirmationModal.svelte @@ -20,6 +20,7 @@ additionalExitAction?: () => void triggerOnSearchParamsChange?: boolean onDiscardChanges?: () => void + tabMode?: boolean } let { @@ -27,7 +28,8 @@ diffDrawer = undefined, additionalExitAction = () => {}, triggerOnSearchParamsChange = false, - onDiscardChanges = undefined + onDiscardChanges = undefined, + tabMode = false }: Props = $props() let savedValue: Value | undefined = $state(undefined) let modifiedValue: Value | undefined = $state(undefined) @@ -66,7 +68,9 @@ orderedJsonStringify(replaceFalseWithUndefined(draftOrDeployed)) === orderedJsonStringify(replaceFalseWithUndefined(current)) ) { - bypassBeforeNavigate = true + if (!tabMode) { + bypassBeforeNavigate = true + } additionalExitAction?.() } else { await openModal() diff --git a/frontend/src/lib/components/triggers/http/RouteEditorConfigSection.svelte b/frontend/src/lib/components/triggers/http/RouteEditorConfigSection.svelte index 94965d696c..8bf579c34e 100644 --- a/frontend/src/lib/components/triggers/http/RouteEditorConfigSection.svelte +++ b/frontend/src/lib/components/triggers/http/RouteEditorConfigSection.svelte @@ -54,7 +54,11 @@ clearTimeout(validateTimeout) } validateTimeout = setTimeout(async () => { - if (!routePath || !method || !/^:?[-\w]+(\/:?[-\w]+)*$/.test(routePath)) { + if ( + !routePath || + !method || + !/^(\*[-\w]+$|:?[-\w]+)(\/(\*[-\w]+$|:?[-\w]+))*$/.test(routePath) + ) { routeError = 'Endpoint not valid' } else if (await routeExists(routePath, method, workspaced_route)) { routeError = 'Endpoint already taken' @@ -140,7 +144,9 @@ bind:value={route_path} error={routeError !== ''} /> -
':myparam' for path params
+
+ Use ':myparam' for path params and '*mywildcard' for wildcards +
diff --git a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte index fc7fcfcaff..fd4780ebb0 100644 --- a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte @@ -1047,6 +1047,7 @@ getInitialAndModifiedValues={getAllUnsavedChanges} onDiscardChanges={discardAllChanges} triggerOnSearchParamsChange={true} + tabMode={true} />