diff --git a/marble-backoffice/src/components/AstNodeComponent.tsx b/marble-backoffice/src/components/AstNodeComponent.tsx index bec82e428..ebe01073d 100644 --- a/marble-backoffice/src/components/AstNodeComponent.tsx +++ b/marble-backoffice/src/components/AstNodeComponent.tsx @@ -1,8 +1,10 @@ import Typography from "@mui/material/Typography"; -import { type AstNode, type AstNodeEvaluation, NoConstant } from "@/models"; +import { NoConstant } from "@/models"; +import type { AstNode, AstNodeEvaluation, EvaluationError } from "@/models"; import { AstConstantComponent } from "./AstConstantComponent"; import Paper from "@mui/material/Paper"; import Alert from "@mui/material/Alert"; +import Chip from "@mui/material/Chip"; function stringifyAst(node: AstNode): string { if (node.constant !== NoConstant) { @@ -40,11 +42,21 @@ export function AstNodeComponent({ node, evaluation, displaySuccess, + argumentError, }: { node: AstNode; evaluation?: AstNodeEvaluation | null; displaySuccess: boolean; + argumentError?: string; }) { + const chipColor = !evaluation + ? "info" + : evaluation.errors === null + ? "default" + : evaluation.errors.length == 0 + ? "success" + : "error"; + return ( <> - {node.name && ( - name: {node.name} + {node.name && } + {argumentError && ( + )} {evaluation && displaySuccess && @@ -68,10 +81,7 @@ export function AstNodeComponent({ )} {node.constant !== NoConstant && ( - Constant:{" "} - + Constant: )} {!node.name && node.constant === NoConstant && ( @@ -91,6 +101,11 @@ export function AstNodeComponent({ node={child} evaluation={evaluation?.children[i]} displaySuccess={displaySuccess} + argumentError={ + evaluation?.errors + ? findChildArgumentError(evaluation.errors, { index: i }) + : undefined + } /> ))}
@@ -101,6 +116,11 @@ export function AstNodeComponent({ node={child} evaluation={evaluation?.namedChildren[name]} displaySuccess={displaySuccess} + argumentError={ + evaluation?.errors + ? findChildArgumentError(evaluation.errors, { name }) + : undefined + } />
))} @@ -109,3 +129,12 @@ export function AstNodeComponent({ ); } + +function findChildArgumentError( + errors: EvaluationError[], + { index, name }: { index?: number; name?: string } +): string | undefined { + return errors.find( + (e) => e.argumentIndex === index || e.argumentName === name + )?.error; +} diff --git a/marble-backoffice/src/models/ScenarioValidation.ts b/marble-backoffice/src/models/ScenarioValidation.ts index ccc133557..016bbb6f5 100644 --- a/marble-backoffice/src/models/ScenarioValidation.ts +++ b/marble-backoffice/src/models/ScenarioValidation.ts @@ -3,6 +3,8 @@ import type { ConstantOptional } from "./AstExpression"; export interface EvaluationError { error: string; message: string; + argumentIndex: number | null; + argumentName: string | null; } export interface AstNodeEvaluation { diff --git a/marble-backoffice/src/models/ScenarioValidationDto.ts b/marble-backoffice/src/models/ScenarioValidationDto.ts index 84f90f2b0..e3c585fcf 100644 --- a/marble-backoffice/src/models/ScenarioValidationDto.ts +++ b/marble-backoffice/src/models/ScenarioValidationDto.ts @@ -14,6 +14,8 @@ import { MapObjectValues } from "@/MapUtils"; export const EvaluationErrorSchema = yup.object({ error: yup.string().defined(), message: yup.string().defined(), + argument_index: yup.number(), + argument_name: yup.string(), }); type EvaluationErrorDto = yup.InferType; @@ -61,6 +63,8 @@ export function adaptEvaluationError(dto: EvaluationErrorDto): EvaluationError { return { error: dto.error, message: dto.message, + argumentIndex: dto.argument_index ?? null, + argumentName: dto.argument_name ?? null, }; } diff --git a/marble-backoffice/src/services/AstEditorService.ts b/marble-backoffice/src/services/AstEditorService.ts index dd35e8675..32718e47f 100644 --- a/marble-backoffice/src/services/AstEditorService.ts +++ b/marble-backoffice/src/services/AstEditorService.ts @@ -18,6 +18,7 @@ import { adaptAstNodeDto, adaptLitteralAstNode, } from "@/models/AstExpressionDto"; +import { HttpError } from "@/infra/fetchUtils"; export interface AstEditorService { scenariosRepository: ScenariosRepository; @@ -77,24 +78,34 @@ export function useAstEditor( showLoader( loadingDispatcher, (async () => { - if (ruleId === null) { - await patchIteration( - service.scenariosRepository, - scenario.organizationId, - iteration.iterationId, - { - triggerCondition: astNode, + try { + if (ruleId === null) { + await patchIteration( + service.scenariosRepository, + scenario.organizationId, + iteration.iterationId, + { + triggerCondition: astNode, + } + ); + } else { + await updateRule( + service.scenariosRepository, + scenario.organizationId, + ruleId, + { + formula: astNode, + } + ); + } + } catch (e) { + if (e instanceof HttpError) { + if (e.statusCode / 100 == 4) { + const aa = (await e.response.text()).split("\n")[0] + setErrorMessages([aa]); } - ); - } else { - await updateRule( - service.scenariosRepository, - scenario.organizationId, - ruleId, - { - formula: astNode, - } - ); + } + throw e; } const validation = await validateIteration( @@ -117,7 +128,9 @@ export function useAstEditor( return; } - setErrorMessages(flattenNodeEvaluationErrors(validation).map((e) => e.message)); + setErrorMessages( + flattenNodeEvaluationErrors(validation).map((e) => e.message) + ); }, [validation]); return {