diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index d1116bee27..3b828b3e00 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -4651,6 +4651,7 @@ paths: parameters: - $ref: "#/components/parameters/WorkspaceId" - $ref: "#/components/parameters/JobId" + - $ref: "#/components/parameters/GetStarted" responses: "200": description: result @@ -4662,6 +4663,8 @@ paths: completed: type: boolean result: {} + started: + type: boolean required: - completed - result @@ -6405,6 +6408,11 @@ components: required: true schema: type: string + GetStarted: + name: get_started + in: query + schema: + type: boolean schemas: $ref: "../../openflow.openapi.yaml#/components/schemas" diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 635f51106e..1613c6a097 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -2758,26 +2758,54 @@ async fn get_completed_job_result( #[derive(Serialize)] struct CompletedJobResult<'c> { + started: Option, completed: bool, result: Option<&'c JsonRawValue>, } +#[derive(Deserialize)] +struct GetCompletedJobQuery { + get_started: Option, +} + async fn get_completed_job_result_maybe( Extension(db): Extension, Path((w_id, id)): Path<(String, Uuid)>, + Query(GetCompletedJobQuery { get_started }): Query, ) -> error::Result { let result_o = sqlx::query("SELECT result FROM completed_job WHERE id = $1 AND workspace_id = $2") .bind(id) - .bind(w_id) + .bind(&w_id) .fetch_optional(&db) .await?; if let Some(result) = result_o { let res = RawResult::from_row(&result)?; - Ok(Json(CompletedJobResult { completed: true, result: Some(res.result) }).into_response()) + Ok(Json(CompletedJobResult { + started: Some(true), + completed: true, + result: Some(res.result), + }) + .into_response()) + } else if get_started.is_some_and(|x| x) { + let started = sqlx::query_scalar!( + "SELECT running FROM queue WHERE id = $1 AND workspace_id = $2", + id, + w_id + ) + .fetch_optional(&db) + .await? + .unwrap_or(false); + Ok( + Json(CompletedJobResult { started: Some(started), completed: false, result: None }) + .into_response(), + ) } else { - Ok(Json(CompletedJobResult { completed: false, result: None }).into_response()) + Ok( + Json(CompletedJobResult { started: None, completed: false, result: None }) + .into_response(), + ) } } diff --git a/frontend/src/lib/components/FlowTimeline.svelte b/frontend/src/lib/components/FlowTimeline.svelte index b8931d42d8..7f063fdcc7 100644 --- a/frontend/src/lib/components/FlowTimeline.svelte +++ b/frontend/src/lib/components/FlowTimeline.svelte @@ -121,14 +121,16 @@ >{#if min && total}
{#each items?.[k] ?? [] as b} - +
+ +
{/each}
{/if} Promise) { try { + running = false isLoading = true clearCurrentJob() const startedAt = Date.now() @@ -106,6 +109,7 @@ export async function cancelJob() { const id = currentId if (id) { + dispatch('cancel', id) currentId = undefined try { await JobService.cancelQueuedJob({ @@ -121,6 +125,7 @@ export async function clearCurrentJob() { if (currentId) { + dispatch('cancel', currentId) job = undefined await cancelJob() } @@ -145,8 +150,13 @@ try { let maybe_job = await JobService.getCompletedJobResultMaybe({ workspace: workspace ?? '', - id + id, + getStarted: isEditor }) + if (maybe_job.started && !running) { + running = true + dispatch('running', id) + } if (maybe_job.completed) { isCompleted = true if (currentId === id) { diff --git a/frontend/src/lib/components/TimelineBar.svelte b/frontend/src/lib/components/TimelineBar.svelte index e43f9bbab7..9e8e8c0fd4 100644 --- a/frontend/src/lib/components/TimelineBar.svelte +++ b/frontend/src/lib/components/TimelineBar.svelte @@ -9,25 +9,31 @@ export let len: number export let id: string export let running: boolean + export let concat: boolean = false + export let gray: boolean = false -{#if min && started_at} -
+{#if min && started_at != undefined} + {#if !concat}
- + {id} - {id} + {#if len > 0} {#if len}{msToSec(len, 1)}s{/if} - -
+ {/if} + {/if} diff --git a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte index 1439bd5169..eb1c6d805e 100644 --- a/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte +++ b/frontend/src/lib/components/apps/components/helpers/RunnableComponent.svelte @@ -344,14 +344,15 @@ ...oldJob, ...(result ? { result } : {}), ...(transformer ? { transformer } : {}), - duration_ms: oldJob?.started_at ? Date.now() - oldJob?.started_at : 1 + error, + duration_ms: oldJob?.started_compute_at ? Date.now() - oldJob?.started_compute_at : 1 } $jobsById[jobId] = job } if (error) { - $errorByComponent[id] = { id, error } + $errorByComponent[id] = { id: jobId, error } } } @@ -449,9 +450,9 @@ let cancellableRun: ((inlineScript?: InlineScript) => CancelablePromise) | undefined = undefined - let didInitialRun = false + let didInitialRun = $initialized.initialized onMount(() => { - didInitialRun = false + didInitialRun = $initialized.initialized cancellableRun = (inlineScript?: InlineScript) => { let rejectCb: (err: Error) => void let p: Partial> = new Promise((resolve, reject) => { @@ -523,7 +524,9 @@ {/if} { + console.log('started', e.detail) loading = true setJobId(e.detail) dispatch('started', e.detail) @@ -537,8 +540,19 @@ on:cancel={(e) => { let jobId = e.detail let job = $jobsById[jobId] - if (job && job.started_at) { - $jobsById[jobId] = { ...job, duration_ms: Date.now() - job.started_at } + if (job && job.started_at && !job.duration_ms) { + $jobsById[jobId] = { + ...job, + duration_ms: Date.now() - (job.started_compute_at ?? job.started_at) + } + } + }} + on:running={(e) => { + console.log('running', e.detail) + let jobId = e.detail + let job = $jobsById[jobId] + if (job && !job.started_compute_at) { + $jobsById[jobId] = { ...job, started_compute_at: Date.now() } } }} on:doneError={(e) => { diff --git a/frontend/src/lib/components/apps/editor/AppEditor.svelte b/frontend/src/lib/components/apps/editor/AppEditor.svelte index 58547c3593..deda1e829c 100644 --- a/frontend/src/lib/components/apps/editor/AppEditor.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditor.svelte @@ -146,6 +146,7 @@ let scale = writable(100) setContext('AppEditorContext', { + refreshComponents: writable(undefined), history, pickVariableCallback, movingcomponents: writable(undefined), diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index f4fe16838f..dbee68e17c 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -37,6 +37,7 @@ Laptop2, Loader2, MoreVertical, + RefreshCw, Smartphone } from 'lucide-svelte' import { getContext } from 'svelte' @@ -106,7 +107,8 @@ openDebugRun } = getContext('AppViewerContext') - const { history, jobsDrawerOpen } = getContext('AppEditorContext') + const { history, jobsDrawerOpen, refreshComponents } = + getContext('AppEditorContext') const loading = { publish: false, @@ -657,9 +659,12 @@ 'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-surface-secondary hover:text-blue-400', selectedJob.error ? 'border border-red-500 text-primary' : '', selectedJob.error && $errorByComponent[selectedJob.component]?.id == id - ? 'bg-red-400' - : '', - selectedJobId == id ? 'bg-surface-secondary text-blue-600' : '' + ? selectedJobId == id + ? 'bg-red-600 !border-blue-600' + : 'bg-red-400' + : selectedJobId == id + ? 'text-blue-600' + : '' )} on:click={() => { selectedJobId = id @@ -827,6 +832,16 @@ + +