diff --git a/backend/sqlx-data.json b/backend/sqlx-data.json index 762d8e9798..23dadadb9d 100644 --- a/backend/sqlx-data.json +++ b/backend/sqlx-data.json @@ -2901,6 +2901,27 @@ }, "query": "SELECT policy from app WHERE path = $1 AND workspace_id = $2" }, + "6f246196df45be4f1860f04c0fb25b1076c9c3fa30d632f4a90cb37a8f4d419b": { + "describe": { + "columns": [ + { + "name": "logs", + "ordinal": 0, + "type_info": "Text" + } + ], + "nullable": [ + true + ], + "parameters": { + "Left": [ + "Uuid", + "Text" + ] + } + }, + "query": "SELECT logs FROM completed_job WHERE id = $1 AND workspace_id = $2" + }, "701f215eb14ba67a79afea15d7effc0dd394ba6c4a72c95d4560c5a377015d4e": { "describe": { "columns": [], diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index e1a4fa7a35..dfc8116bd5 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -3768,6 +3768,23 @@ paths: schema: $ref: "#/components/schemas/Job" + /w/{workspace}/jobs_u/get_logs/{id}: + get: + summary: get job logs + operationId: getJob logs + tags: + - job + parameters: + - $ref: "#/components/parameters/WorkspaceId" + - $ref: "#/components/parameters/JobId" + responses: + "200": + description: job details + content: + text/plain: + schema: + type: string + /w/{workspace}/jobs_u/getupdate/{id}: get: summary: get job updates diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 94e385a0f3..d1760017f6 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -120,6 +120,7 @@ pub fn global_service() -> Router { get(get_suspended_job_flow), ) .route("/get/:id", get(get_job)) + .route("/get_logs/:id", get(get_job_logs)) .route("/completed/get/:id", get(get_completed_job)) .route("/completed/get_result/:id", get(get_completed_job_result)) .route( @@ -276,6 +277,22 @@ async fn get_job( Ok(Json(job)) } +async fn get_job_logs( + Extension(db): Extension, + Path((w_id, id)): Path<(String, Uuid)>, +) -> error::Result { + let text = sqlx::query_scalar!( + "SELECT logs FROM completed_job WHERE id = $1 AND workspace_id = $2", + id, + w_id + ) + .fetch_optional(&db) + .await? + .flatten(); + let text = not_found_if_none(text, "Job Logs", id.to_string())?; + Ok(text) +} + pub async fn get_job_by_id<'c>( mut tx: Transaction<'c, Postgres>, w_id: &str, diff --git a/frontend/src/lib/components/FlowJobResult.svelte b/frontend/src/lib/components/FlowJobResult.svelte index 19382d1078..6a107fe3a5 100644 --- a/frontend/src/lib/components/FlowJobResult.svelte +++ b/frontend/src/lib/components/FlowJobResult.svelte @@ -9,6 +9,7 @@ export let noBorder = false export let loading export let filename: string | undefined = undefined + export let jobId: string | undefined = undefined
- +
diff --git a/frontend/src/lib/components/FlowStatusViewer.svelte b/frontend/src/lib/components/FlowStatusViewer.svelte index 0a7f08b22e..4e244a14af 100644 --- a/frontend/src/lib/components/FlowStatusViewer.svelte +++ b/frontend/src/lib/components/FlowStatusViewer.svelte @@ -218,6 +218,7 @@ {#if `result` in job}
- import { Loader2 } from 'lucide-svelte' - import { Drawer, DrawerContent } from './common' + import { ClipboardCopy, Download, Loader2 } from 'lucide-svelte' + import { Button, Drawer, DrawerContent } from './common' + import { copyToClipboard } from '$lib/utils' + import { workspaceStore } from '$lib/stores' export let content: string | undefined export let isLoading: boolean export let duration: number | undefined = undefined export let mem: number | undefined = undefined export let wrapperClass = '' + export let jobId: string | undefined = undefined let scroll = true let div: HTMLElement | null = null @@ -26,6 +29,16 @@ + + Download + +
{#if content}{content}{:else if isLoading}Waiting for job to start...{:else}No logs are available yet{/if}
(table: Table): Array> { + try { + return table.getHeaderGroups() + } catch (e) { + sendUserToast("Couldn't render table header groups: " + e, true) + console.error(e) + return [] + } + } {#each Object.keys(components['tablecomponent'].initialData.configuration) as key (key)} @@ -241,7 +257,7 @@ )} style={css?.tableHeader?.style ?? ''} > - {#each $table.getHeaderGroups() as headerGroup} + {#each getHeaderGroups($table) as headerGroup} {#each headerGroup.headers as header} {#if header?.column?.columnDef?.header} diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index eddb2045a8..c33bab5678 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -632,7 +632,7 @@ {#if job?.job_kind !== 'flow' && job?.job_kind !== 'flowpreview'} - + {#if job != undefined && 'result' in job && job.result != undefined} diff --git a/frontend/src/lib/components/jobs/JobPreview.svelte b/frontend/src/lib/components/jobs/JobPreview.svelte index 482d4d15e8..6e27b0a10e 100644 --- a/frontend/src/lib/components/jobs/JobPreview.svelte +++ b/frontend/src/lib/components/jobs/JobPreview.svelte @@ -124,6 +124,7 @@ {:else if job && `running` in job ? job.running : false}
Job is still running