improve flow viewer

This commit is contained in:
Ruben Fiszel
2022-12-05 13:09:50 +01:00
parent 177fc2b421
commit ede6cbea99
9 changed files with 136 additions and 76 deletions
@@ -18,7 +18,8 @@
| 'gif'
| 'error'
| 'approval'
| undefined = inferResultKind(result)
| undefined
$: resultKind = inferResultKind(result)
let forceJson = false
@@ -79,7 +80,7 @@
{#if result != undefined}
{#if resultKind && resultKind != 'json'}
<div class="mb-2 text-gray-500 text-sm bg-gray-50/20">
as JSON <input type="checkbox" bind:checked={forceJson} /></div
as JSON&nbsp;<input type="checkbox" bind:checked={forceJson} /></div
>{/if}{#if typeof result == 'object' && Object.keys(result).length > 0}<div
class="mb-2 text-sm text-gray-700"
>The result keys are: <b>{truncate(Object.keys(result).join(', '), 50)}</b></div
@@ -15,21 +15,21 @@
let stepDetail: FlowModule | undefined = undefined
</script>
<div class="flex flex-col h-full">
<div class="h-full w-full border border-gray-700" class:overflow-auto={overflowAuto}>
<div class="grid grid-cols-3 w-full h-full">
<div class="h-full col-span-2 w-full border border-gray-700" class:overflow-auto={overflowAuto}>
<FlowGraph
modules={flow?.value?.modules}
failureModule={flow?.value?.failure_module}
on:click={(e) => (stepDetail = e.detail)}
/>
</div>
<div class="w-full border-l border-r border-b border-gray-700 min-h-[150px] p-2">
<div class="w-full border-r border-b border-t border-gray-700 min-h-[150px] p-2 overflow-auto">
{#if stepDetail == undefined}
<span class="font-black text-lg w-full my-4">
<span>Click on a step to see its details</span>
</span>
{:else}
<div class="font-black text-lg w-full mb-2 "
<div class="font-black text-lg w-full mb-6"
>Step {stepDetail.id ?? ''}<span class="ml-2 font-normal">{stepDetail.summary || ''}</span
></div
>
@@ -37,7 +37,7 @@
<div> An identity step return as output its input </div>
{:else if stepDetail.value.type == 'rawscript'}
<div class="text-2xs mb-4">
<h3>Step Inputs</h3>
<h3 class="mb-2">Step Inputs</h3>
<InputTransformsViewer
inputTransforms={stepDetail?.value?.input_transforms ??
stepDetail?.input_transforms ??
@@ -45,7 +45,7 @@
/>
</div>
<h3>Code</h3>
<h3 class="mb-2">Code</h3>
<HighlightCode language={stepDetail.value.language} code={stepDetail.value.content} />
{:else if stepDetail.value.type == 'script'}
<div class="mb-4">
@@ -59,7 +59,7 @@
</a>
</div>
<div class="text-2xs mb-4">
<h3>Step Inputs</h3>
<h3 class="mb-2">Step Inputs</h3>
<InputTransformsViewer
inputTransforms={stepDetail?.value?.input_transforms ??
stepDetail?.input_transforms ??
@@ -1,14 +1,17 @@
<script lang="ts">
import type { CompletedJob } from '$lib/gen'
import DisplayResult from './DisplayResult.svelte'
import LogViewer from './LogViewer.svelte'
export let result: any
export let logs: string
export let col: boolean = false
export let noBorder = false
</script>
<div class="grid grid-cols-2 shadow border border-gray-800 h-full">
<div
class:border={!noBorder}
class="grid {!col ? 'grid-cols-2' : 'grid-rows-2'} shadow border-gray-400 h-full"
>
<div class="bg-white max-h-80 h-full p-1 overflow-auto relative">
<span class="text-gray-500">Result</span>
<DisplayResult {result} />
@@ -161,7 +161,7 @@
/>
<div class="h-full pt-4 grow">
{#if jobId}
<FlowStatusViewer bind:flowState={$flowStateStore} {jobId} bind:job />
<FlowStatusViewer isRoot bind:flowState={$flowStateStore} {jobId} bind:job />
{:else}
<div class="italic text-gray-500 h-full grow"> Flow status will be displayed here </div>
{/if}
@@ -8,7 +8,7 @@
import { createEventDispatcher } from 'svelte'
import { onDestroy } from 'svelte'
import type { FlowState } from './flows/flowState'
import { Button, Tab } from './common'
import { Badge, Button, Tab } from './common'
import DisplayResult from './DisplayResult.svelte'
import Tabs from './common/tabs/Tabs.svelte'
import { FlowGraph } from './graph'
@@ -25,9 +25,17 @@
}
| undefined = undefined
export let job: Job | undefined = undefined
export let flowModuleStates: Record<string, FlowStatusModule.type> = {}
export let flowModuleStates: Record<
string,
{ type: FlowStatusModule.type; logs?: string; result?: any }
> = {}
let localFlowModuleStates: Record<string, FlowStatusModule.type> = {}
let localFlowModuleStates: Record<
string,
{ type: FlowStatusModule.type; logs?: string; result?: any }
> = {}
let selectedNode: string | undefined = undefined
let jobResults: any[] = []
let jobFailures: boolean[] = []
@@ -41,7 +49,7 @@
Object.entries(localFlowModuleStates).forEach(([moduleId, state]) => {
if (
flowModuleStates[moduleId] !== state &&
flowModuleStates[moduleId] !== FlowStatusModule.type.FAILURE
flowModuleStates[moduleId]?.type !== FlowStatusModule.type.FAILURE
) {
flowModuleStates[moduleId] = state
}
@@ -63,14 +71,27 @@
: []
) ?? []
$: innerModules.forEach((module, i) => {
if (
module.type === FlowStatusModule.type.WAITING_FOR_EVENTS &&
localFlowModuleStates?.[innerModules?.[i - 1]?.id ?? '']?.type ==
FlowStatusModule.type.SUCCESS
) {
localFlowModuleStates[module.id ?? ''] = { type: module.type }
}
})
let errorCount = 0
async function loadJobInProgress() {
if (jobId != '00000000-0000-0000-0000-000000000000') {
try {
job = await JobService.getJob({
const newJob = await JobService.getJob({
workspace: $workspaceStore ?? '',
id: jobId ?? ''
})
if (JSON.stringify(newJob) !== JSON.stringify(job)) {
job = newJob
}
errorCount = 0
} catch (e) {
errorCount += 1
@@ -186,11 +207,18 @@
jobFailures[j] = e.detail.success === false
}
if (e.detail.type == 'QueuedJob') {
localFlowModuleStates[flowJobIds.moduleId] = FlowStatusModule.type.IN_PROGRESS
localFlowModuleStates[flowJobIds.moduleId] = {
type: FlowStatusModule.type.IN_PROGRESS,
logs: e.detail.logs
}
} else {
localFlowModuleStates[flowJobIds.moduleId] = e.detail.success
? FlowStatusModule.type.SUCCESS
: FlowStatusModule.type.FAILURE
localFlowModuleStates[flowJobIds.moduleId] = {
type: e.detail.success
? FlowStatusModule.type.SUCCESS
: FlowStatusModule.type.FAILURE,
logs: e.detail.logs,
result: e.detail.result
}
}
}
}}
@@ -242,11 +270,18 @@
flowState[mod.id].previewArgs = e.detail.args
}
if (e.detail.type == 'QueuedJob') {
localFlowModuleStates[mod.id] = FlowStatusModule.type.IN_PROGRESS
localFlowModuleStates[mod.id] = {
type: FlowStatusModule.type.IN_PROGRESS,
logs: e.detail.logs
}
} else {
localFlowModuleStates[mod.id] = e.detail.success
? FlowStatusModule.type.SUCCESS
: FlowStatusModule.type.FAILURE
localFlowModuleStates[mod.id] = {
type: e.detail.success
? FlowStatusModule.type.SUCCESS
: FlowStatusModule.type.FAILURE,
logs: e.detail.logs,
result: e.detail.result
}
}
}
}}
@@ -270,14 +305,35 @@
</div>
</div>
{#if job.raw_flow && !isListJob}
<div class="{selected != 'graph' ? 'hidden' : ''} lg:mx-40 mx-10 border border-gray-400 mt-4">
<div class="{selected != 'graph' ? 'hidden' : ''} mx-10 mt-4">
<div class="border" />
<FlowGraph
flowModuleStates={localFlowModuleStates}
notSelectable
modules={job.raw_flow?.modules ?? []}
failureModule={job.raw_flow?.failure_module}
/>
<div class="grid grid-cols-3">
<div class="col-span-2 bg-gray-50">
<FlowGraph
flowModuleStates={localFlowModuleStates}
on:click={(e) => {
if (e.detail.id) {
selectedNode = e.detail.id
}
}}
modules={job.raw_flow?.modules ?? []}
failureModule={job.raw_flow?.failure_module}
/>
</div>
<div class="border-l border-gray-400">
{#if selectedNode}
{#if localFlowModuleStates[selectedNode]}
<Badge>{localFlowModuleStates[selectedNode].type}</Badge>
<FlowJobResult
noBorder
col
result={localFlowModuleStates[selectedNode].result ?? {}}
logs={localFlowModuleStates[selectedNode].logs ?? ''}
/>
{/if}
{:else}<p class="p-2 text-gray-600 italic">Select a node to see its details here</p>{/if}
</div>
</div>
</div>
{/if}
{:else}
@@ -7,10 +7,12 @@
<div class="flex space-x-2 items-center">
{#if path.startsWith('hub/')}
<IconedResourceType name={path.split('/')[2]} silent={true} />
<span>{path}</span>
<div>
<IconedResourceType width="20px" height="20px" name={path.split('/')[2]} silent={true} />
</div>
<span class="text-sm">{path}</span>
{:else}
<Badge color="blue">Workspace</Badge>
<span>{path}</span>
<span class="text-sm">{path}</span>
{/if}
</div>
+19 -13
View File
@@ -43,21 +43,27 @@
</Badge>
</div>
{:else if job && 'running' in job && job.running}
<Badge large color="yellow">
<Icon data={faCircle} scale={SMALL_ICON_SCALE} class="mr-2" />
Running {#if job.flow_status}({job.flow_status?.step ?? ''} of {job.raw_flow?.modules?.length ??
'?'}){/if}
</Badge>
<div>
<Badge large color="yellow">
<Icon data={faCircle} scale={SMALL_ICON_SCALE} class="mr-2" />
Running {#if job.flow_status}({job.flow_status?.step ?? ''} of {job.raw_flow?.modules
?.length ?? '?'}){/if}
</Badge>
</div>
{:else if job && 'running' in job && 'scheduled_for' in job && job.scheduled_for && forLater(job.scheduled_for)}
<Badge>
<Icon data={faCalendar} scale={SMALL_ICON_SCALE} class="mr-2" />
Scheduled for {displayDate(job.scheduled_for)}
</Badge>
<div>
<Badge>
<Icon data={faCalendar} scale={SMALL_ICON_SCALE} class="mr-2" />
Scheduled for {displayDate(job.scheduled_for)}
</Badge>
</div>
{:else if job && 'running' in job}
<Badge>
<Icon data={faClock} scale={SMALL_ICON_SCALE} class="mr-2" />
Queued
</Badge>
<div>
<Badge>
<Icon data={faClock} scale={SMALL_ICON_SCALE} class="mr-2" />
Queued
</Badge>
</div>
{:else}
<Icon class="text-gray-200" data={faCircle} scale={SMALL_ICON_SCALE} />
{/if}
@@ -18,13 +18,14 @@
import { defaultIfEmptyString, truncateRev } from '$lib/utils'
import { createEventDispatcher } from 'svelte'
import { numberToChars } from '../flows/utils'
import type { FlowState } from '../flows/flowState'
export let modules: FlowModule[] | undefined = []
export let failureModule: FlowModule | undefined = undefined
export let minHeight: number = 0
export let notSelectable = false
export let flowModuleStates: Record<string, FlowStatusModule.type> | undefined = undefined
export let flowModuleStates:
| Record<string, { type: FlowStatusModule.type; logs?: string; result?: any }>
| undefined = undefined
let selectedNode: string | undefined = undefined
@@ -147,6 +148,8 @@
return 'rgb(248 113 113)'
case FlowStatusModule.type.IN_PROGRESS:
return 'rgb(253, 240, 176)'
case FlowStatusModule.type.WAITING_FOR_EVENTS:
return 'rgb(229, 176, 253)'
default:
return '#fff'
}
@@ -199,7 +202,7 @@
bgColor:
selectedNode == onClickDetail.id
? '#f5f5f5'
: getStateColor(flowModuleStates?.[onClickDetail.id]),
: getStateColor(flowModuleStates?.[onClickDetail.id]?.type),
parentIds,
clickCallback: (node) => {
if (!notSelectable) {
@@ -391,7 +394,7 @@
}
</script>
<div bind:clientWidth={width} class="w-full h-full overflow-hidden">
<div bind:clientWidth={width} class="w-full h-full overflow-hidden relative">
{#if width && height}
<Svelvet {nodes} {width} {edges} {height} bgColor="rgb(249 250 251)" />
{/if}
@@ -5,27 +5,16 @@
export let job: QueuedJob | CompletedJob
</script>
<div class="overflow-x-auto relative">
<table class="text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
<tr>
<th scope="row" class="py-2 pr-6 font-bold text-gray-900 whitespace-nowrap dark:text-white">
Status
</th>
<td class="py-2 "> <JobStatus {job} /></td>
</tr>
{#if job}
<tr>
<th scope="row" class="py-2 pr-6 font-bold text-gray-900 whitespace-nowrap ">
Job Id
</th>
<td class="py-2">
<a rel="noreferrer" target="_blank" href="/run/{job?.id}?workspace={job?.workspace_id}">
{job?.id}
</a>
</td>
</tr>
{/if}
</tbody>
</table>
<div class="grid grid-cols-2 gap-4 mb-1 text-gray-500 dark:text-gray-400">
<JobStatus {job} />
{#if job}
<div
><div class=" text-gray-900 whitespace-nowrap ">
<span class="font-bold">Job Id</span>
<a rel="noreferrer" target="_blank" href="/run/{job?.id}?workspace={job?.workspace_id}">
{job?.id}
</a>
</div>
</div>
{/if}
</div>