Files
windmill/frontend/src/lib/components/ModulePreview.svelte
2025-04-20 01:19:02 +02:00

136 lines
3.8 KiB
Svelte

<script lang="ts">
import type { Schema } from '$lib/common'
import { ScriptService, type FlowModule, type Job } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { getScriptByPath } from '$lib/scripts'
import { CornerDownLeft, Loader2 } from 'lucide-svelte'
import { getContext } from 'svelte'
import Button from './common/button/Button.svelte'
import type { FlowEditorContext } from './flows/types'
import TestJobLoader from './TestJobLoader.svelte'
import ModulePreviewForm from './ModulePreviewForm.svelte'
import { evalValue } from './flows/utils'
import type { PickableProperties } from './flows/previousResults'
export let mod: FlowModule
export let schema: Schema | { properties?: Record<string, any> }
export let pickableProperties: PickableProperties | undefined
export let testJob: Job | undefined = undefined
export let testIsLoading = false
export let noEditor = false
export let scriptProgress = undefined
const { flowStore, flowStateStore, testStepStore, pathStore } =
getContext<FlowEditorContext>('FlowEditorContext')
// Test
let testJobLoader: TestJobLoader
let jobProgressReset: () => void = () => {}
let stepArgs: Record<string, any> | undefined = Object.fromEntries(
Object.keys(schema.properties ?? {}).map((k) => [
k,
evalValue(k, mod, $testStepStore, pickableProperties, false)
])
)
$: $testStepStore[mod.id] = stepArgs
export function runTestWithStepArgs() {
runTest(stepArgs)
}
export async function runTest(args: any) {
// Not defined if JobProgressBar not loaded
if (jobProgressReset) jobProgressReset()
const val = mod.value
// let jobId: string | undefined = undefined
if (val.type == 'rawscript') {
await testJobLoader?.runPreview(
val.path ?? ($pathStore ?? '') + '/' + mod.id,
val.content,
val.language,
mod.id === 'preprocessor' ? { _ENTRYPOINT_OVERRIDE: 'preprocessor', ...args } : args,
$flowStore?.tag ?? val.tag
)
} else if (val.type == 'script') {
const script = val.hash
? await ScriptService.getScriptByHash({ workspace: $workspaceStore!, hash: val.hash })
: await getScriptByPath(val.path)
await testJobLoader?.runPreview(
val.path,
script.content,
script.language,
args,
$flowStore?.tag ?? (val.tag_override ? val.tag_override : script.tag),
script.lock,
val.hash ?? script.hash
)
} else if (val.type == 'flow') {
await testJobLoader?.runFlowByPath(val.path, args)
} else {
throw Error('Not supported module type')
}
}
function jobDone() {
if (testJob && !testJob.canceled && testJob.type == 'CompletedJob' && `result` in testJob) {
if ($flowStateStore[mod.id]) {
$flowStateStore[mod.id].previewResult = testJob.result
$flowStateStore[mod.id].previewSuccess = testJob.success
$flowStateStore[mod.id].previewJobId = testJob.id
$flowStateStore[mod.id].previewWorkspaceId = testJob.workspace_id
$flowStateStore = $flowStateStore
}
}
testJob = undefined
}
</script>
<TestJobLoader
toastError={noEditor}
on:done={() => jobDone()}
bind:scriptProgress
bind:this={testJobLoader}
bind:isLoading={testIsLoading}
bind:job={testJob}
/>
<div class="p-4">
{#if $flowStore.value.same_worker}
<div class="mb-1 bg-yellow-100 text-yellow-700 p-1 text-xs"
>The `./shared` folder is not passed across individual "Test this step"</div
>
{/if}
<div class="w-full justify-center flex">
{#if testIsLoading}
<Button size="sm" on:click={testJobLoader?.cancelJob} btnClasses="w-full" color="red">
<Loader2 size={16} class="animate-spin mr-1" />
Cancel
</Button>
{:else}
<Button
color="dark"
btnClasses="truncate"
size="sm"
on:click={() => runTest(stepArgs)}
shortCut={{
Icon: CornerDownLeft
}}
>
Run
</Button>
{/if}
</div>
<ModulePreviewForm {pickableProperties} {mod} {schema} bind:args={stepArgs} />
</div>