mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 00:03:08 +00:00
6c17a6963e
* migrate FlowPreviewContent to svelte 5 * run background preview from input panel * share local run test * Show approval in graph is testing in graph * use component and props instead of portal for approval in graph * Add a toggle to show module status in graph * open module result after each run * Fix module reactivity issue * Add test flow button * Extract preview run logic from flowPreviewContent * Revert "Extract preview run logic from flowPreviewContent" This reverts commita39c70a920. * nit * lazy load preview content * create component for flow preview button * open preview v0 * open preview v1 * connect open preview button * improve graph run display * enable cancel preview * Run test flow from input panel * nit * wip * Use global context instead of module context for moduleTestState * nit * fix flow preview rendering * Add testJob to modulesTest context * update module status based on individual test data * fix: clear job status on run preview * detatch run buttons from input node * move preview job in FlowEditorContext * move outputPickerOpenFns to FlowEditorContext * add result panel * Add result output picker * add status to loops and branch * add open detail button to result panel * fix test up to * clean unnecessary binding * clean * Make iteration annotation smaller in editmode * detatch test button to and aproval from node * prevent flow edition during execution * Prevent step test run during flow run * Show approval in graph edges * prevent opening output popover if node is outside the graph * fix pointerdownOutside action * fix test up to dropdown not closing * fix test up to * nit * change job status badge display * fix running status * Enable test flow in Dev * fix darkmode * fix node panel display in Dev * fix test flow button positionning * fix suspend in subflows * improve lazy load of preview * prevent preview data unmount on close drawer * clean code * move flowjob into flow context * Revert "move flowjob into flow context" This reverts commit939e9dbaaf. * clean context * nit * fix dark mode status view * fix test button alignment * clean job status on deleted step * fix retry bad status display * Detect flow change * Update frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
164 lines
4.6 KiB
Svelte
164 lines
4.6 KiB
Svelte
<script lang="ts">
|
|
import { mergeSchema } from '$lib/common'
|
|
import { type Job, JobService } from '$lib/gen'
|
|
import { workspaceStore } from '$lib/stores'
|
|
import { sendUserToast } from '$lib/toast'
|
|
import { X } from 'lucide-svelte'
|
|
import DisplayResult from './DisplayResult.svelte'
|
|
import Tooltip from './Tooltip.svelte'
|
|
import { Button } from './common'
|
|
import SchemaForm from './SchemaForm.svelte'
|
|
|
|
export let isOwner: boolean
|
|
export let workspaceId: string | undefined
|
|
export let job: Job
|
|
export let light: boolean = false
|
|
|
|
let default_payload: object = {}
|
|
let resumeUrl: string | undefined = undefined
|
|
let cancelUrl: string | undefined = undefined
|
|
let description: any = undefined
|
|
let hide_cancel = false
|
|
|
|
$: approvalStep = (job?.flow_status?.step ?? 1) - 1
|
|
|
|
let defaultValues = {}
|
|
$: job && getDefaultArgs()
|
|
|
|
let schema = {}
|
|
let lastJobId: string | undefined = undefined
|
|
async function getDefaultArgs() {
|
|
let jobId = job?.flow_status?.modules?.[approvalStep]?.job
|
|
|
|
if (jobId === lastJobId) {
|
|
return
|
|
}
|
|
if (!jobId) {
|
|
return {}
|
|
}
|
|
lastJobId = jobId
|
|
let job_result = (await JobService.getCompletedJobResult({
|
|
workspace: workspaceId ?? $workspaceStore ?? '',
|
|
id: jobId
|
|
})) as any
|
|
const args = job_result?.default_args ?? {}
|
|
description = job_result?.description
|
|
defaultValues = JSON.parse(JSON.stringify(args))
|
|
default_payload = args
|
|
|
|
resumeUrl = job_result?.['resume']
|
|
cancelUrl = job_result?.['cancel']
|
|
hide_cancel = job?.raw_flow?.modules?.[approvalStep]?.suspend?.hide_cancel ?? false
|
|
schema = mergeSchema(
|
|
job?.raw_flow?.modules?.[approvalStep]?.suspend?.resume_form?.schema ?? {},
|
|
job_result?.enums ?? {}
|
|
)
|
|
}
|
|
|
|
async function continu(approve: boolean) {
|
|
if ((resumeUrl && approve) || (cancelUrl && !approve)) {
|
|
let split = (approve ? resumeUrl : cancelUrl)!.split('/')
|
|
let signatureUrl = split.pop() ?? ''
|
|
const regex = /([^?]+)(?:\?[^=]+=(\w+))?/
|
|
|
|
const matches = signatureUrl.match(regex)
|
|
|
|
const signature = matches?.[1]
|
|
if (!signature) {
|
|
sendUserToast(`Could not parse signature: ${signatureUrl}`, true)
|
|
return
|
|
}
|
|
const approver = matches?.[2] || undefined
|
|
|
|
let resumeId = -1
|
|
let parsedResumeId = split.pop() ?? ''
|
|
try {
|
|
resumeId = new Number(parsedResumeId).valueOf()
|
|
} catch (e) {
|
|
console.error(`Could not parse resume id: ${parsedResumeId}`)
|
|
}
|
|
let jobId = split.pop() ?? ''
|
|
if (approve) {
|
|
await JobService.resumeSuspendedJobPost({
|
|
workspace: workspaceId ?? $workspaceStore ?? '',
|
|
id: jobId,
|
|
requestBody: default_payload as any,
|
|
resumeId,
|
|
signature,
|
|
approver
|
|
})
|
|
} else {
|
|
await JobService.cancelSuspendedJobPost({
|
|
workspace: workspaceId ?? $workspaceStore ?? '',
|
|
id: jobId,
|
|
resumeId,
|
|
signature,
|
|
approver,
|
|
requestBody: {}
|
|
})
|
|
}
|
|
} else {
|
|
if (approve) {
|
|
await JobService.resumeSuspendedFlowAsOwner({
|
|
workspace: workspaceId ?? $workspaceStore ?? '',
|
|
id: job?.id ?? '',
|
|
requestBody: default_payload as any
|
|
})
|
|
} else {
|
|
await JobService.cancelQueuedJob({
|
|
workspace: workspaceId ?? $workspaceStore ?? '',
|
|
id: job?.id ?? '',
|
|
requestBody: {}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<div class="w-full h-full mt-2 text-sm text-tertiary">
|
|
{#if !light}
|
|
<p>Waiting to be resumed</p>
|
|
{/if}
|
|
{#if description != undefined}
|
|
<DisplayResult {workspaceId} noControls result={description} language={job?.language} />
|
|
{/if}
|
|
<div>
|
|
{#if isOwner || resumeUrl}
|
|
<div class="flex flex-row gap-2 mt-2">
|
|
{#if cancelUrl && !hide_cancel}
|
|
<div>
|
|
<Button
|
|
color="red"
|
|
title="Cancel the flow"
|
|
iconOnly
|
|
startIcon={{ icon: X }}
|
|
variant="border"
|
|
on:click={() => continu(false)}
|
|
/>
|
|
</div>
|
|
{/if}
|
|
<div>
|
|
<Button color="green" variant="border" on:click={() => continu(true)}
|
|
>Resume <Tooltip
|
|
>Since you are an owner of this flow, you can send resume events without necessarily
|
|
knowing the resume id sent by the approval step</Tooltip
|
|
></Button
|
|
>
|
|
</div>
|
|
|
|
{#if job?.raw_flow?.modules?.[approvalStep]?.suspend?.resume_form?.schema}
|
|
<div class="w-full border rounded-lg p-2">
|
|
<SchemaForm onlyMaskPassword bind:args={default_payload} {defaultValues} {schema} />
|
|
</div>
|
|
{/if}
|
|
<Tooltip
|
|
>The payload is optional, it is passed to the following step through the `resume` variable</Tooltip
|
|
>
|
|
</div>
|
|
{:else}
|
|
You cannot resume the flow yourself without receiving the resume secret since you are not an
|
|
owner of {job.script_path} and the approval step did not contain the resume url at key `resume`
|
|
{/if}
|
|
</div>
|
|
</div>
|