fix: improve input history

This commit is contained in:
Ruben Fiszel
2024-07-08 11:41:27 +02:00
parent 729f911b43
commit db19c86a2d
5 changed files with 104 additions and 53 deletions
+8 -2
View File
@@ -7666,6 +7666,14 @@ paths:
required: true
schema:
type: string
- name: input
in: query
schema:
type: boolean
- name: allow_large
in: query
schema:
type: boolean
responses:
"200":
description: args
@@ -9147,8 +9155,6 @@ components:
type: string
name:
type: string
args:
type: object
created_by:
type: string
created_at:
+35 -6
View File
@@ -127,7 +127,7 @@ async fn get_input_history(
let mut tx = user_db.begin(&authed).await?;
let sql = &format!(
"select id, created_at, created_by, CASE WHEN args is null or pg_column_size(args) < 40000 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args, success from completed_job \
"select id, created_at, created_by, 'null'::jsonb as args, success from completed_job \
where {} = $1 and job_kind = $2 and workspace_id = $3 \
order by created_at desc limit $4 offset $5",
r.runnable_type.column_name()
@@ -173,19 +173,48 @@ async fn get_input_history(
Ok(Json(inputs))
}
#[derive(Deserialize)]
struct GetArgs {
input: Option<bool>,
allow_large: Option<bool>,
}
async fn get_args_from_history_or_saved_input(
authed: ApiAuthed,
Extension(user_db): Extension<UserDB>,
Query(g): Query<GetArgs>,
Path((w_id, job_or_input_id)): Path<(String, Uuid)>,
) -> JsonResult<Option<Value>> {
let mut tx = user_db.begin(&authed).await?;
let result_o = sqlx::query_scalar!(
"SELECT args FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT args FROM input WHERE id = $1 AND workspace_id = $2",
let result_o = if let Some(input) = g.input {
if input {
sqlx::query_scalar!(
"SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2",
job_or_input_id,
w_id,
g.allow_large.unwrap_or(true)
)
.fetch_optional(&mut *tx)
.await?
} else {
sqlx::query_scalar!(
"SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM completed_job WHERE id = $1 AND workspace_id = $2",
job_or_input_id,
w_id,
g.allow_large.unwrap_or(true)
)
.fetch_optional(&mut *tx)
.await?
}
} else {
sqlx::query_scalar!(
"SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM completed_job WHERE id = $1 AND workspace_id = $2 UNION ALL SELECT CASE WHEN pg_column_size(args) < 40000 OR $3 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args FROM input WHERE id = $1 AND workspace_id = $2",
job_or_input_id,
w_id
w_id,
g.allow_large.unwrap_or(true)
)
.fetch_optional(&mut *tx)
.await?;
.await?
};
tx.commit().await?;
@@ -206,7 +235,7 @@ async fn list_saved_inputs(
let mut tx = user_db.begin(&authed).await?;
let rows = sqlx::query_as::<_, InputRow>(
"select id, workspace_id, runnable_id, runnable_type, name, CASE WHEN pg_column_size(args) < 40000 THEN args ELSE '\"WINDMILL_TOO_BIG\"'::jsonb END as args, created_at, created_by, is_public from input \
"select id, workspace_id, runnable_id, runnable_type, name, 'null'::jsonb as args, created_at, created_by, is_public from input \
where runnable_id = $1 and runnable_type = $2 and workspace_id = $3 \
and (is_public IS true OR created_by = $4) \
order by created_at desc limit $5 offset $6",
+3 -3
View File
@@ -1466,7 +1466,7 @@ async fn list_jobs(
};
let sql = if lq.success.is_none() && lq.label.is_none() {
let sqlq = list_queue_jobs_query(
let mut sqlq = list_queue_jobs_query(
&w_id,
&ListQueueQuery { order_desc: Some(true), ..lq.into() },
UnifiedJob::queued_job_fields(),
@@ -1482,10 +1482,10 @@ async fn list_jobs(
offset
)
} else {
sqlq.query()?
sqlq.limit(per_page).offset(offset).query()?
}
} else {
sqlc.unwrap().query()?
sqlc.unwrap().limit(per_page).offset(offset).query()?
};
let mut tx = user_db.begin(&authed).await?;
+53 -40
View File
@@ -52,13 +52,21 @@
? 'FlowPath'
: undefined
let hasAlreadyFailed = false
async function loadInputHistory() {
previousInputs = await InputService.getInputHistory({
workspace: $workspaceStore!,
runnableId,
runnableType,
perPage: 10
})
try {
previousInputs = await InputService.getInputHistory({
workspace: $workspaceStore!,
runnableId,
runnableType,
perPage: 10
})
} catch (e) {
console.error(e)
if (hasAlreadyFailed) return
hasAlreadyFailed = true
sendUserToast(`Failed to load input history: ${e}`, true)
}
}
async function loadSavedInputs() {
@@ -139,30 +147,31 @@
}
$: {
if ($workspaceStore && jobs && (scriptHash || scriptPath || flowPath)) {
console.log('loading inputs')
if ($workspaceStore && (scriptHash || scriptPath || flowPath)) {
loadInputHistory()
loadSavedInputs()
}
}
let previewArgs: any = undefined
function selectArgs(selected_args: any) {
dispatch('selected_args', selected_args)
previewArgs = selected_args
}
async function loadLargeArgs(id: string | undefined) {
async function loadLargeArgs(
id: string | undefined,
input: boolean | undefined,
allowLarge: boolean
): Promise<any> {
if (!id) return
largeArgs = await InputService.getArgsFromHistoryOrSavedInput({
return await InputService.getArgsFromHistoryOrSavedInput({
jobOrInputId: id,
workspace: $workspaceStore!
workspace: $workspaceStore!,
input,
allowLarge
})
}
let hasLargeArgs = false
$: hasLargeArgs =
typeof selectedInput?.args === 'string' && selectedInput?.args === 'WINDMILL_TOO_BIG'
let largeArgs: any = undefined
$: hasLargeArgs && loadLargeArgs(selectedInput?.id)
</script>
<JobLoader
@@ -181,6 +190,7 @@
syncQueuedRunsCount={false}
refreshRate={10000}
computeMinAndMax={undefined}
perPage={5}
/>
<div class="min-w-[300px] h-full">
@@ -207,17 +217,17 @@
{/if}
</div>
<div class="w-full flex flex-col gap-2 h-full overflow-y-auto p">
<div class="w-full flex flex-col gap-1 h-full overflow-y-auto p">
{#if savedInputs === undefined}
<Skeleton layout={[[8]]} />
{:else if savedInputs.length > 0}
{#each savedInputs as i}
<button
class={classNames(
`w-full flex items-center group justify-between gap-4 py-2 px-4 text-left border rounded-md hover:bg-surface-hover transition-all`,
`w-full flex items-center text-sm group justify-between gap-4 py-1.5 px-4 text-left border rounded-sm hover:bg-surface-hover transition-all`,
selectedInput === i ? 'border-blue-500 bg-blue-50 dark:bg-blue-900' : ''
)}
on:click={() => {
on:click={async () => {
if (!i.isEditing) {
if (selectedInput === i) {
selectedInput = null
@@ -225,6 +235,7 @@
selectedInput = i
}
}
selectArgs(await loadLargeArgs(i.id, true, false))
}}
>
<div class="w-full h-full items-center justify-between flex gap-1 min-w-0">
@@ -308,8 +319,8 @@
<span class="text-sm font-semibold">Previous runs</span>
<div class="w-full flex flex-col gap-1 p-0 h-full overflow-y-auto">
{#if loading}
<Skeleton layout={[[2]]} />
{#if loading && (jobs == undefined || jobs?.length == 0)}
<div class="text-left text-tertiary text-xs">Loading current runs...</div>
{:else if jobs.length > 0}
{#each jobs as i (i.id)}
<button
@@ -324,7 +335,7 @@
<div class="">
<div class="rounded-full w-2 h-2 bg-orange-400 animate-pulse" />
</div>
<div class="col-span-2">
<div class="col-span-2 truncate">
{i.created_by}
</div>
<div
@@ -345,6 +356,11 @@
</div>
</button>
{/each}
{#if jobs.length == 5}
<div class="text-left text-tertiary text-xs"
>... there may be more runs not displayed here as the limit is 5</div
>
{/if}
{:else}
<div class="text-left text-tertiary text-xs">No running runs</div>
{/if}
@@ -360,12 +376,13 @@
`w-full flex items-center justify-between gap-4 py-2 px-4 text-left border rounded-sm hover:bg-surface-hover transition-a`,
selectedInput === i ? 'border-blue-500 bg-blue-50 dark:bg-blue-900' : ''
)}
on:click={() => {
on:click={async () => {
if (selectedInput === i) {
selectedInput = null
} else {
selectedInput = i
}
selectArgs(await loadLargeArgs(i.id, false, false))
}}
>
<div
@@ -374,7 +391,7 @@
<div class="">
<div class="rounded-full w-2 h-2 {i.success ? 'bg-green-400' : 'bg-red-400'}" />
</div>
<div class="col-span-2">
<div class="col-span-2 truncate" title={i.created_by}>
{i.created_by}
</div>
<div
@@ -410,33 +427,29 @@
<Pane>
<div class="h-full overflow-hidden min-h-0 flex flex-col justify-between">
<div class="w-full flex flex-col min-h-0 gap-2 px-2 py-2 grow">
<div class="text-sm font-semibold">Preview</div>
<div class="w-full flex flex-col">
<Button
color="blue"
btnClasses="w-full"
size="sm"
spacingSize="xl"
on:click={() => selectArgs(hasLargeArgs ? largeArgs : selectedInput?.args)}
disabled={Object.keys(selectedInput?.args || {}).length === 0 ||
(hasLargeArgs && Object.keys(largeArgs || {}).length === 0)}
on:click={async () => {
dispatch('selected_args', await loadLargeArgs(selectedInput?.id, undefined, false))
}}
disabled={!selectedInput}
>
<ArrowLeftIcon class="w-4 h-4 mr-2" />
Use Input
</Button>
</div>
<div class="w-full min-h-0 grow overflow-auto">
{#if hasLargeArgs}
{#if largeArgs}
<div class=" overflow-auto h-full p-2">
<ObjectViewer json={largeArgs} />
</div>
{:else}
<Skeleton layout={[[8]]} />
{/if}
{:else if Object.keys(selectedInput?.args || {}).length > 0}
{#if typeof previewArgs == 'string' && previewArgs == 'WINDMILL_TOO_BIG'}
<div class="text-secondary mt-2">
Payload too big to preview but can still be loaded</div
>
{:else if Object.keys(previewArgs || {}).length > 0}
<div class=" overflow-auto h-full p-2">
<ObjectViewer json={selectedInput?.args} />
<ObjectViewer json={previewArgs} />
</div>
{:else}
<div class="text-center text-tertiary">
@@ -45,6 +45,7 @@
export let allWorkspaces: boolean = false
export let computeMinAndMax: (() => { minTs: string; maxTs: string } | undefined) | undefined
export let lookback: number = 0
export let perPage: number | undefined = undefined
let intervalId: NodeJS.Timeout | undefined
let sync = true
@@ -135,7 +136,8 @@
resultFilter && resultFilter != '{}' && resultFilter != '' && resultError == ''
? resultFilter
: undefined,
allWorkspaces: allWorkspaces ? true : undefined
allWorkspaces: allWorkspaces ? true : undefined,
perPage
})
}
@@ -170,7 +172,8 @@
resultFilter && resultFilter != '{}' && resultFilter != '' && resultError == ''
? resultFilter
: undefined,
allWorkspaces: allWorkspaces ? true : undefined
allWorkspaces: allWorkspaces ? true : undefined,
perPage
})
}