feat: add filter jobs by args or result

This commit is contained in:
Ruben Fiszel
2023-03-11 15:30:46 +01:00
parent f586f75f05
commit d3296c958e
5 changed files with 127 additions and 31 deletions
+19
View File
@@ -3174,6 +3174,8 @@ paths:
- $ref: "#/components/parameters/JobKinds"
- $ref: "#/components/parameters/Suspended"
- $ref: "#/components/parameters/Running"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/ResultFilter"
responses:
"200":
description: All available queued jobs
@@ -3202,6 +3204,8 @@ paths:
- $ref: "#/components/parameters/CreatedAfter"
- $ref: "#/components/parameters/Success"
- $ref: "#/components/parameters/JobKinds"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/ResultFilter"
- name: is_skipped
description: is the job skipped
in: query
@@ -3238,6 +3242,8 @@ paths:
- $ref: "#/components/parameters/CreatedBefore"
- $ref: "#/components/parameters/CreatedAfter"
- $ref: "#/components/parameters/JobKinds"
- $ref: "#/components/parameters/ArgsFilter"
- $ref: "#/components/parameters/ResultFilter"
- name: is_skipped
description: is the job skipped
in: query
@@ -4646,6 +4652,19 @@ components:
in: query
schema:
type: boolean
ArgsFilter:
name: args
description: filter on jobs containing those args as a json subset (@> in postgres)
in: query
schema:
type: string
ResultFilter:
name: result
description: filter on jobs containing those result as a json subset (@> in postgres)
in: query
schema:
type: string
After:
name: after
description: filter on created after (exclusive) timestamp
+28 -7
View File
@@ -334,6 +334,8 @@ pub struct ListQueueQuery {
pub order_desc: Option<bool>,
pub job_kinds: Option<String>,
pub suspended: Option<bool>,
// filter by matching a subset of the args using base64 encoded json subset
pub args: Option<String>,
}
fn list_queue_jobs_query(w_id: &str, lq: &ListQueueQuery, fields: &[&str]) -> SqlBuilder {
@@ -376,6 +378,7 @@ fn list_queue_jobs_query(w_id: &str, lq: &ListQueueQuery, fields: &[&str]) -> Sq
sqlb.and_where_eq("suspend", 0);
}
}
if let Some(jk) = &lq.job_kinds {
sqlb.and_where_in(
"job_kind",
@@ -383,6 +386,10 @@ fn list_queue_jobs_query(w_id: &str, lq: &ListQueueQuery, fields: &[&str]) -> Sq
);
}
if let Some(args) = &lq.args {
sqlb.and_where("args @> ?".bind(&args.replace("'", "''")));
}
sqlb
}
@@ -462,6 +469,7 @@ async fn list_jobs(
order_desc: Some(true),
job_kinds: lq.job_kinds,
suspended: lq.suspended,
args: lq.args,
},
&[
"'QueuedJob' as typ",
@@ -1136,15 +1144,15 @@ where
struct InPayload {
payload: Option<String>,
}
fn decode_payload<D: DeserializeOwned, T: AsRef<[u8]>>(t: T) -> anyhow::Result<D> {
let vec = base64::engine::general_purpose::URL_SAFE
.decode(t)
.context("invalid base64")?;
serde_json::from_slice(vec.as_slice()).context("invalid json")
}
}
}
fn decode_payload<D: DeserializeOwned, T: AsRef<[u8]>>(t: T) -> anyhow::Result<D> {
let vec = base64::engine::general_purpose::URL_SAFE
.decode(t)
.context("invalid base64")?;
serde_json::from_slice(vec.as_slice()).context("invalid json")
}
pub async fn run_flow_by_path(
authed: Authed,
Extension(user_db): Extension<UserDB>,
@@ -1706,6 +1714,15 @@ fn list_completed_jobs_query(
);
}
if let Some(args) = &lq.args {
sqlb.and_where("args @> ?".bind(&args.replace("'", "''")));
}
if let Some(result) = &lq.result {
sqlb.and_where("result @> ?".bind(&result.replace("'", "''")));
}
tracing::info!("{:?}", sqlb.sql());
sqlb
}
#[derive(Deserialize, Clone)]
@@ -1723,6 +1740,10 @@ pub struct ListCompletedQuery {
pub is_skipped: Option<bool>,
pub is_flow_step: Option<bool>,
pub suspended: Option<bool>,
// filter by matching a subset of the args using base64 encoded json subset
pub args: Option<String>,
// filter by matching a subset of the result using base64 encoded json subset
pub result: Option<String>,
}
async fn list_completed_jobs(
@@ -2,13 +2,18 @@
import { ChevronDown, ChevronUp } from 'lucide-svelte'
import { slide } from 'svelte/transition'
import Button from './common/button/Button.svelte'
import Tooltip from './Tooltip.svelte'
export let text: string
export let tooltip: string | undefined = undefined
export let view = false
</script>
<Button color="light" on:click={() => (view = !view)} variant="border"
>{text}
{#if tooltip}
<Tooltip wrapperClass="mx-1">{tooltip}</Tooltip>
{/if}
{#if !view}<ChevronDown />{:else}<ChevronUp />{/if}</Button
>
{#if view}
@@ -1,7 +1,8 @@
<script lang="ts">
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
export let code: string
export let value: any
export let value: any = undefined
export let error = ''
function parseJson() {
try {
@@ -11,7 +12,6 @@
error = e.message
}
}
let error = ''
$: code && parseJson()
</script>
@@ -18,6 +18,8 @@
import Icon from 'svelte-awesome'
import AutoComplete from 'simple-svelte-autocomplete'
import NoItemFound from '$lib/components/home/NoItemFound.svelte'
import Slider from '$lib/components/Slider.svelte'
import JsonEditor from '$lib/components/apps/editor/settingsPanel/inputEditor/JsonEditor.svelte'
let jobs: Job[] | undefined
let error: Error | undefined
@@ -55,6 +57,16 @@
$: ($workspaceStore && loadJobs(createdBefore)) || (path && success && isSkipped && jobKinds)
let filterTimeout: NodeJS.Timeout | undefined = undefined
function debounceSyncer() {
filterTimeout && clearTimeout(filterTimeout)
filterTimeout = setTimeout(() => {
loadJobs(createdBefore)
}, 500)
}
$: (true || argFilter || resultFilter) && debounceSyncer()
async function fetchJobs(
createdBefore: string | undefined,
createdAfter: string | undefined
@@ -67,7 +79,13 @@
jobKinds,
success,
isSkipped,
isFlowStep: jobKindsCat != 'all' ? false : undefined
isFlowStep: jobKindsCat != 'all' ? false : undefined,
args:
argFilter && argFilter != '{}' && argFilter != '' && argError == '' ? argFilter : undefined,
result:
resultFilter && resultFilter != '{}' && resultFilter != '' && resultError == ''
? resultFilter
: undefined
})
}
@@ -89,7 +107,7 @@
}
async function syncer() {
if (jobs && createdBefore === undefined) {
if (sync && jobs && createdBefore === undefined) {
const reversedJobs = [...jobs].reverse()
const lastIndex = reversedJobs.findIndex((x) => x.type == Job.type.QUEUED_JOB) - 1
let ts = lastIndex >= 0 ? reversedJobs[lastIndex].created_at : undefined
@@ -110,9 +128,17 @@
}
}
let sync = true
onMount(() => {
loadPaths()
intervalId = setInterval(syncer, 5000)
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
sync = false
} else {
sync = true
}
})
})
let paths: string[] = []
@@ -146,6 +172,12 @@
function onSearchPathChange() {
goto(`/runs/${searchPath}?${$page.url.searchParams.toString()}`)
}
let argFilter: any = undefined
let resultFilter: any = undefined
let argError = ''
let resultError = ''
</script>
<CenteredPage>
@@ -210,28 +242,47 @@
<input type="text" value={maxTs ?? 'zoom x axis to set max'} disabled />
</div>
</div>
<div class="flex flex-row gap-x-2 mb-2 w-full">
{#key path}
<AutoComplete
items={paths}
value={path}
bind:selectedItem={searchPath}
placeholder="Search by path of script/flow"
/>
{/key}
<Button
variant="border"
on:click={() => {
minTs = undefined
maxTs = undefined
goto('/runs?' + $page.url.searchParams.toString())
fetchJobs(createdBefore, undefined)
}}
size="xs"
<div class="grid grid-cols-1 md:grid-cols-3 gap-2 mb-2 w-full flex-wrap">
<div>
<div class="flex flex-row gap-x-2">
{#key path}
<AutoComplete
items={paths}
value={path}
bind:selectedItem={searchPath}
placeholder="Search by path"
/>
{/key}
<Button
variant="border"
on:click={() => {
minTs = undefined
maxTs = undefined
goto('/runs?' + $page.url.searchParams.toString())
fetchJobs(createdBefore, undefined)
}}
size="xs"
>
<Icon data={faSearchMinus} />
</Button>
</div>
</div>
<div
><Slider
text="Filter by args"
tooltip={'Filter by a json being a subset of the args. Try \'{"foo": "bar"}\''}
><JsonEditor bind:error={argError} bind:code={argFilter} /></Slider
></div
>
<div
><Slider
text="Filter by result"
tooltip={'Filter by a json being a subset of the result. Try \'{"foo": "bar"}\''}
><JsonEditor bind:error={resultError} bind:code={resultFilter} /></Slider
></div
>
<Icon data={faSearchMinus} />
</Button>
</div>
<Skeleton loading={!jobs} layout={[[6], 1, [6], 1, [6], 1, [6], 1, [6]]} />
{#if jobs}