diff --git a/frontend/src/lib/components/DisplayResult.svelte b/frontend/src/lib/components/DisplayResult.svelte index 057a4f7744..dcf2c34fe2 100644 --- a/frontend/src/lib/components/DisplayResult.svelte +++ b/frontend/src/lib/components/DisplayResult.svelte @@ -217,7 +217,9 @@ href="data:application/octet-stream;base64,{result.file}">Download - {:else if !forceJson && resultKind == 'error'}
+ {:else if !forceJson && resultKind == 'error' && result?.error}
{#if result.error.name || result.error.message}{result.error.name}: {result.error .message}{:else}{JSON.stringify(result.error, null, 4)}{/if} 100 ? njobs.slice(0, 100) : njobs + await setResult(r, job) + $state = $state } catch (e) { sendUserToast(`Error running frontend script ${id}: ` + e.message, true) - - // Manually add a fake job to the job list to show the error - const job = generateNextFrontendJobId() - const error = e.body ?? e.message - - $errorByComponent[job] = { - error, - componentId: id - } - - $jobs = [{ job, component: id, error }, ...$jobs] + await setResult({ error: { message: e.body ?? e.message } }, job) } loading = false donePromise?.() @@ -227,7 +215,7 @@ } try { - let njob = await resultJobLoader?.abstractRun(async () => { + await resultJobLoader?.abstractRun(async () => { const nonStaticRunnableInputs = {} const staticRunnableInputs = {} for (const k of Object.keys(fields ?? {})) { @@ -272,12 +260,8 @@ requestBody }) }) - if (njob) { - const njobs = [{ job: njob, component: id }, ...$jobs] - $jobs = $jobs?.length > 100 ? njobs.slice(0, 100) : njobs - } } catch (e) { - setResult({ error: e.body ?? e.message }, undefined) + updateResult({ error: e.body ?? e.message }) loading = false } } @@ -290,7 +274,7 @@ executeComponent() } } catch (e) { - setResult({ error: e.body ?? e.message }, undefined) + updateResult({ error: e.body ?? e.message }) } } @@ -305,14 +289,50 @@ outputs.jobId?.set(jobId) } - async function setResult(res: any, jobId: string | undefined) { - dispatch('done') - const hasRes = res !== undefined && res !== null + function recordJob( + jobId: string, + result?: string, + error?: string, + transformer?: { result?: string; error?: string } + ) { + const job = { + ...(result ? { result } : {}), + ...(error ? { error } : {}), + ...(transformer ? { transformer } : {}), + job: jobId, + component: id + } + if (error) { + recordError(error, jobId) + } else if (job?.transformer?.error) { + recordError(job.transformer.error, jobId) + } + + const njobs = [job, ...$jobs] + // Only keep the last 100 jobs + $jobs = $jobs?.length > 100 ? njobs.slice(0, 100) : njobs + } + + function getResultErrors(result: any | any[]): string | undefined { + const errorAsArray = Array.isArray(result) ? result.flat() : [result] + const hasErrors = errorAsArray.some((r) => r?.error) + + if (!hasErrors) { + return undefined + } + + return errorAsArray + .map((r) => r?.error?.message) + .filter(Boolean) + .join('\n') + } + + async function runTransformer(res) { if (transformer) { try { let raw = $worldStore.newOutput(id, 'raw', res) - res = await eval_like( + const transformerResult = await eval_like( transformer.content, computeGlobalContext($worldStore, { iter: iterContext ? $iterContext : undefined, @@ -326,9 +346,10 @@ $worldStore, $runnableComponents ) - raw.set(res) + raw.set(transformerResult) + return transformerResult } catch (err) { - res = { + return { error: { name: 'TransformerError', message: 'An error occured in the transformer', @@ -336,52 +357,51 @@ } } } - - if (hasRes && res === undefined) { - res = { - error: { - name: 'TransformerError', - message: 'An error occured in the transformer', - stack: 'Transformer returned undefined' - } - } - } } + } - // console.log('setr', id) - + function updateResult(res) { outputs.result?.set(res) - result = res + } - // Flows with loops can have multiple results - const errorAsArray = Array.isArray(result) ? result.flat() : [result] + async function setResult(res: any, jobId: string | undefined) { + dispatch('done') + const hasRes = res !== undefined && res !== null - // As soon as we have an error, we consider the component errored - const hasErrors = errorAsArray.some((r) => r?.error) - - if (hasErrors) { - const errorMessages = errorAsArray - .map((r) => r?.error?.message) - .filter(Boolean) - .join('\n') - - jobId && recordError(errorMessages, jobId) - - dispatch('handleError', errorMessages) - } else { - dispatch('success') + if (!jobId && !hasRes) { + return } - const previousJobId = Object.keys($errorByComponent).find( - (key) => $errorByComponent[key].componentId === id - ) + const errors = getResultErrors(res) - if (previousJobId && !hasErrors) { - delete $errorByComponent[previousJobId] - $errorByComponent = $errorByComponent + console.log('errors', errors) + + if (errors) { + const transformerResult = transformer + ? { error: 'Transformer could not be run because of previous errors' } + : undefined + + recordJob(jobId!, undefined, errors, transformerResult) + updateResult(res) + dispatch('handleError', errors) + return } + const transformerResult = await runTransformer(res) + + if (transformerResult?.error) { + recordJob(jobId!, res, undefined, transformerResult) + updateResult(transformerResult) + dispatch('handleError', transformerResult.error) + return + } + + updateResult(transformerResult ?? res) + recordJob(jobId!, result, undefined, transformerResult) + $errorByComponent = clearErrorByComponentId(id, $errorByComponent) + + dispatch('success') donePromise?.() } @@ -477,7 +497,7 @@ loading = false }} on:doneError={(e) => { - setResult({ error: e.detail }, e.detail.id) + setResult({ error: e.detail.error }, e.detail.id) loading = false }} bind:this={resultJobLoader} @@ -514,10 +534,17 @@
An error occured, please contact the app author. + + {#if lastJobId && $errorByComponent[lastJobId]?.error} +
{$errorByComponent[lastJobId]?.error}
+ {/if} Job id: {lastJobId} + Job id: {lastJobId} +
diff --git a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte index 1936c8bd0e..9a6b338344 100644 --- a/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte +++ b/frontend/src/lib/components/apps/editor/AppEditorHeader.svelte @@ -632,6 +632,7 @@ {/if} {:else} + {@const jobResult = $jobs.find((j) => j.job == selectedJobId)}
{#if job?.['running']}
@@ -673,6 +674,28 @@
{/if} + {#if jobResult?.transformer} + +
Transformer results
+ {#if job != undefined && 'result' in job && job.result != undefined} +
+														
+													
+ {:else if testIsLoading} +
+ {:else if job != undefined && 'result' in job && job?.['result'] == undefined} +
Result is undefined
+ {:else} +
+ +
+ {/if} +
+ {/if} {:else}
diff --git a/frontend/src/lib/components/apps/types.ts b/frontend/src/lib/components/apps/types.ts index 595c6184df..2fe48c1e69 100644 --- a/frontend/src/lib/components/apps/types.ts +++ b/frontend/src/lib/components/apps/types.ts @@ -186,7 +186,15 @@ export type AppViewerContext = { workspace: string onchange: (() => void) | undefined isEditor: boolean - jobs: Writable<{ job: string; component: string; result?: string; error?: string }[]> + jobs: Writable< + { + job: string + component: string + result?: string + error?: string + transformer?: { result?: string; error?: string } + }[] + > noBackend: boolean errorByComponent: Writable> openDebugRun: Writable<((componentID: string) => void) | undefined>