fix(orchestrator): preserve nested extraErrors and async validation errors#2818
Conversation
Code Review by Qodo
|
Changed Packages
|
Review Summary by QodoFix nested extraErrors and async validation in multi-step forms
WalkthroughsDescription• Fix safeSet to correctly handle deeply nested dotted paths • Preserve all async validation errors by sequentially awaiting promises • Wrap extraErrors with active step key for RJSF root schema matching • Add comprehensive tests for error handling and nested field validation Diagramflowchart LR
A["Deep nested paths<br/>e.g. step.x.y.z"] -->|safeSet fix| B["Correct error tree<br/>built recursively"]
C["Concurrent async<br/>validation calls"] -->|sequential await| D["All errors preserved<br/>no lost updates"]
E["getExtraErrors<br/>returns step-keyed tree"] -->|toRootExtraErrors| F["Root-shaped errors<br/>for RJSF + StepperObjectField"]
B --> G["Multi-step forms<br/>show all inline errors"]
D --> G
F --> G
File Changes1. workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/safeSet.ts
|
ced3c7e to
fc77b77
Compare
|
* fix(orchestrator): preserve nested extraErrors and async validation errors (#2818) * fix(orchestrator-form): Review step with empty form data (#2834) Empty pruned form data (e.g. display-only ActiveText) made generateReviewTableData return undefined and NestedReviewTable throw. Return {} from generateReviewTableData and guard NestedReviewTable. Add regression test and changeset. Made-with: Cursor * fix(orchestrator-form-react): fix ui styling issues in workflow results page (#2808) --------- Co-authored-by: Karthik Jeeyar <karthik@redhat.com>
* fix(orchestrator): preserve nested extraErrors and async validation errors (#2818) * fix(orchestrator-form): Review step with empty form data (#2834) Empty pruned form data (e.g. display-only ActiveText) made generateReviewTableData return undefined and NestedReviewTable throw. Return {} from generateReviewTableData and guard NestedReviewTable. Add regression test and changeset. Made-with: Cursor * fix(orchestrator-form-react): fix ui styling issues in workflow results page (#2808) --------- Co-authored-by: Karthik Jeeyar <karthik@redhat.com>
* fix(orchestrator): preserve nested extraErrors and async validation errors (#2818) * fix(orchestrator-form): Review step with empty form data (#2834) Empty pruned form data (e.g. display-only ActiveText) made generateReviewTableData return undefined and NestedReviewTable throw. Return {} from generateReviewTableData and guard NestedReviewTable. Add regression test and changeset. Made-with: Cursor * fix(orchestrator-form-react): fix ui styling issues in workflow results page (#2808) --------- Co-authored-by: Lokananda Prabhu <102503482+lokanandaprabhu@users.noreply.github.com>




Preserve nested extraErrors and async validation errors
Fixes:
https://redhat.atlassian.net/browse/RHDHBUGS-2970
Fixes cases where only one of several backend validation errors appeared for multi-step workflows, or errors did not show inline on nested fields under a wizard step.
Changes included in the PR:
safeSetbuilds the correct nested error tree for dotted paths.toRootExtraErrorsensures extraErrors passed to the root form match schema.properties (step keys), so RJSF +StepperObjectFieldshow inline errors correctly.How to test
1. Add the Backstage proxy so the browser does not call
127.0.0.1:7070directly. Merge underproxy:inapp-config.yamlorapp-config.local.yaml:2. Run a mock validation HTTP server on
127.0.0.1:7070that answers POST to paths like/validate/fieldAwith 422 and a JSON body such as{ "validation": ["…"] }. Save the script below to any path (e.g.mock-validation-server.cjsin your workspace) and run:Example mock server - `mock-validation-server.cjs`. (Node.js)
3. Register the workflow and input schema using the YAML/JSON in the collapsible sections below.
validate:urlmust use the proxy, e.g.$${{backend.baseUrl}}/api/proxy/local-validation-mock/validate/fieldA(andfieldB,fieldC), nothttp://127.0.0.1:7070/...directly (avoids CORS).Workflow definition (reference)
Input schema JSON (reference)
{ "$id": "classpath:/schemas/extra-errors-repro__main-schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "title": "Extra errors reproduction", "type": "object", "properties": { "my-solution": { "type": "object", "title": "Solution step", "properties": { "workflowParams": { "type": "object", "title": "Workflow params (hidden)", "ui:hidden": true, "additionalProperties": true }, "xParams": { "type": "object", "title": "Cross parameters", "required": ["fieldA", "fieldB", "fieldC"], "properties": { "fieldA": { "type": "string", "title": "Field A", "default": "", "ui:widget": "ActiveTextInput", "ui:props": { "validate:url": "$${{backend.baseUrl}}/api/proxy/local-validation-mock/validate/fieldA", "validate:method": "POST", "validate:body": { "field": "fieldA" } } }, "fieldB": { "type": "string", "title": "Field B", "default": "", "ui:widget": "ActiveTextInput", "ui:props": { "validate:url": "$${{backend.baseUrl}}/api/proxy/local-validation-mock/validate/fieldB", "validate:method": "POST", "validate:body": { "field": "fieldB" } } }, "fieldC": { "type": "string", "title": "Field C", "default": "", "ui:widget": "ActiveTextInput", "ui:props": { "validate:url": "$${{backend.baseUrl}}/api/proxy/local-validation-mock/validate/fieldC", "validate:method": "POST", "validate:body": { "field": "fieldC" } } } } } } } }, "required": ["my-solution"] }4. Run the app and open Execute workflow for
extra-errors-repro.5. What to verify
getExtraErrorsruns (threevalidate:urlrequests).Before:
After:
✔️ Checklist