diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 93f3afdce0..a412ab2f38 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -3261,6 +3261,7 @@ paths: - $ref: "#/components/parameters/Running" - $ref: "#/components/parameters/ArgsFilter" - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/Tag" responses: "200": description: All available queued jobs @@ -3291,6 +3292,7 @@ paths: - $ref: "#/components/parameters/JobKinds" - $ref: "#/components/parameters/ArgsFilter" - $ref: "#/components/parameters/ResultFilter" + - $ref: "#/components/parameters/Tag" - name: is_skipped description: is the job skipped in: query @@ -3328,6 +3330,7 @@ paths: - $ref: "#/components/parameters/StartedAfter" - $ref: "#/components/parameters/JobKinds" - $ref: "#/components/parameters/ArgsFilter" + - $ref: "#/components/parameters/Tag" - $ref: "#/components/parameters/ResultFilter" - name: is_skipped description: is the job skipped @@ -4886,6 +4889,12 @@ components: in: query schema: type: string + Tag: + name: tag + description: filter on jobs with a given tag/worker group + in: query + schema: + type: string ResultFilter: name: result description: filter on jobs containing those result as a json subset (@> in postgres) diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index 9448a88ff3..4902373500 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -326,6 +326,7 @@ pub struct CompletedJob { pub visible_to_owner: bool, #[serde(skip_serializing_if = "Option::is_none")] pub mem_peak: Option, + pub tag: String, } #[derive(Deserialize, Clone)] @@ -410,6 +411,7 @@ pub struct ListQueueQuery { pub suspended: Option, // filter by matching a subset of the args using base64 encoded json subset pub args: Option, + pub tag: Option, } fn list_queue_jobs_query(w_id: &str, lq: &ListQueueQuery, fields: &[&str]) -> SqlBuilder { @@ -432,6 +434,9 @@ fn list_queue_jobs_query(w_id: &str, lq: &ListQueueQuery, fields: &[&str]) -> Sq if let Some(cb) = &lq.created_by { sqlb.and_where_eq("created_by", "?".bind(cb)); } + if let Some(t) = &lq.tag { + sqlb.and_where_eq("tag", "?".bind(t)); + } if let Some(r) = &lq.running { sqlb.and_where_eq("running", &r); } @@ -564,6 +569,7 @@ async fn list_jobs( job_kinds: lq.job_kinds, suspended: lq.suspended, args: lq.args, + tag: lq.tag, }, &[ "'QueuedJob' as typ", @@ -1106,6 +1112,7 @@ struct UnifiedJob { visible_to_owner: bool, suspend: Option, mem_peak: Option, + tag: String, } impl From for Job { @@ -1141,6 +1148,7 @@ impl From for Job { email: uj.email, visible_to_owner: uj.visible_to_owner, mem_peak: uj.mem_peak, + tag: uj.tag, }), "QueuedJob" => Job::QueuedJob(QueuedJob { workspace_id: uj.workspace_id, @@ -1176,6 +1184,7 @@ impl From for Job { mem_peak: uj.mem_peak, root_job: None, leaf_jobs: None, + tag: uj.tag, }), t => panic!("job type {} not valid", t), } @@ -1865,6 +1874,9 @@ fn list_completed_jobs_query( if let Some(h) = &lq.script_hash { sqlb.and_where_eq("script_hash", "?".bind(h)); } + if let Some(t) = &lq.tag { + sqlb.and_where_eq("tag", "?".bind(t)); + } if let Some(cb) = &lq.created_by { sqlb.and_where_eq("created_by", "?".bind(cb)); } @@ -1922,6 +1934,7 @@ pub struct ListCompletedQuery { pub args: Option, // filter by matching a subset of the result using base64 encoded json subset pub result: Option, + pub tag: Option, } async fn list_completed_jobs( diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index 081294c6f7..2359ce38df 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -79,6 +79,7 @@ pub struct QueuedJob { pub root_job: Option, #[serde(skip_serializing_if = "Option::is_none")] pub leaf_jobs: Option, + pub tag: String, } impl QueuedJob { @@ -140,6 +141,7 @@ impl Default for QueuedJob { mem_peak: None, root_job: None, leaf_jobs: None, + tag: "deno".to_string(), } } } diff --git a/backend/windmill-worker/src/jobs.rs b/backend/windmill-worker/src/jobs.rs index 1f7d342cf5..910385f030 100644 --- a/backend/windmill-worker/src/jobs.rs +++ b/backend/windmill-worker/src/jobs.rs @@ -126,9 +126,10 @@ pub async fn add_completed_job( , email , visible_to_owner , mem_peak + , tag ) VALUES ($1, $2, $3, $4, $5, $6, COALESCE($26, (EXTRACT('epoch' FROM (now())) - EXTRACT('epoch' FROM (COALESCE($6, now()))))*1000), $7, $8, $9,\ - $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $27, $28, $29) + $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $27, $28, $29, $30) ON CONFLICT (id) DO UPDATE SET success = $7, result = $11, logs = concat(cj.logs, $12)", queued_job.workspace_id, queued_job.id, @@ -158,7 +159,8 @@ pub async fn add_completed_job( duration: Option, queued_job.email, queued_job.visible_to_owner, - mem_peak + mem_peak, + queued_job.tag, ) .execute(&mut tx) .await diff --git a/frontend/src/lib/components/jobs/JobPreview.svelte b/frontend/src/lib/components/jobs/JobPreview.svelte index 73a6f760b6..dd814fc25e 100644 --- a/frontend/src/lib/components/jobs/JobPreview.svelte +++ b/frontend/src/lib/components/jobs/JobPreview.svelte @@ -26,35 +26,22 @@ let popupOnTop = true $: open = $openStore === job?.id - $: completed = job?.type === Job.type.COMPLETED_JOB - $: running = job && `running` in job ? job.running : false - $: logs = job?.logs || logs async function instantOpen() { - hovered = true - if (!job) { - return + if (!open) { + hovered = true + if (!job) { + return + } + popupOnTop = wrapper.getBoundingClientRect().top > POPUP_HEIGHT + openStore.set(job.id) + if (!loaded) { + await tick() + watchJob && watchJob(job.id) + } + } else { + timeout && clearTimeout(timeout) } - popupOnTop = wrapper.getBoundingClientRect().top > POPUP_HEIGHT - openStore.set(job.id) - if (!loaded) { - await tick() - watchJob && watchJob(job.id) - } - } - - function staggeredOpen() { - hovered = true - if (timeout) { - clearTimeout(timeout) - } - timeout = setTimeout( - async () => { - timeout = undefined - await instantOpen() - }, - loaded ? 100 : 300 - ) } function close() { @@ -70,6 +57,9 @@ function staggeredClose() { hovered = false + if (timeout) { + clearTimeout(timeout) + } timeout = setTimeout( async () => { timeout = undefined @@ -96,17 +86,15 @@ {/if}
{#if open}
{new Date(job?.['scheduled_for']).toLocaleString()}
{/if} - {#if completed} + {#if job?.type === Job.type.COMPLETED_JOB} - {:else if running} + {:else if job && `running` in job ? job.running : false}
Job is still running
- + {/if} diff --git a/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte b/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte index 5f4406c310..5a6dc39b63 100644 --- a/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/run/[...run]/+page.svelte @@ -284,6 +284,9 @@ {/if} {#if job && 'job_kind' in job}{job.job_kind} {/if} + {#if job.tag && !['deno', 'python3', 'flow', 'other', 'go', 'bash', 'other', 'dependency'].includes(job.tag)} + Worker group: {job.tag} + {/if} {#if !job.visible_to_owner}only visible to you The option to hide this run from the owner of this script or flow was activated