Skip to content

fix(orchestrator): clear validation errors when user edits a form field#3522

Open
lokanandaprabhu wants to merge 1 commit into
redhat-developer:mainfrom
lokanandaprabhu:fix/clear-validation-errors-on-field-edit
Open

fix(orchestrator): clear validation errors when user edits a form field#3522
lokanandaprabhu wants to merge 1 commit into
redhat-developer:mainfrom
lokanandaprabhu:fix/clear-validation-errors-on-field-edit

Conversation

@lokanandaprabhu

Copy link
Copy Markdown
Member

Hey, I just made a Pull Request!

Fixes: https://redhat.atlassian.net/browse/RHDHBUGS-3381

Summary

  • Clear async validation errors when the user edits a workflow form field after clicking Next (field-level and top-level error list).
  • Only the edited field’s error is removed; errors on other invalid fields stay until those fields are edited or Next is run again.
Screen.Recording.2026-06-22.at.12.09.34.PM.mov

For testing, create the below workspaces/orchestrator/packages/backend/src/devMockValidationModule.ts file add in workspaces/orchestrator/packages/backend/src/index.ts

backend.add(import('./devMockValidationModule'));

import {
  coreServices,
  createBackendModule,
} from '@backstage/backend-plugin-api';
import express, { Request, Response } from 'express';

const isBlank = (value: unknown): boolean =>
  value === undefined ||
  value === null ||
  (typeof value === 'string' && value.trim() === '');

/**
 * Local-dev mock validation APIs for devModeTemp workflows (e.g. managed-app-ci
 * stale-error reproduction). Mounted at /api/orchestrator/dev/mock-validate.
 */
export default createBackendModule({
  pluginId: 'orchestrator',
  moduleId: 'dev-mock-validation',
  register(env) {
    env.registerInit({
      deps: { httpRouter: coreServices.httpRouter },
      async init({ httpRouter }) {
        const router = express.Router();
        router.use(express.json());

        router.post('/dev/mock-validate/field', (req: Request, res: Response) => {
          const { value, fieldLabel, requiredMessage } = req.body ?? {};
          if (isBlank(value)) {
            const label = fieldLabel ?? 'field';
            const message =
              requiredMessage ??
              `The property '${label}' in solution 'managed-app-ci' is a blank string and allowNullOrEmpty is false. Please provide a value.`;
            return res.status(422).json({
              issues: [message],
            });
          }
          return res.status(200).json({ ok: true });
        });

        router.post(
          '/dev/mock-validate/openshift-pair',
          (req: Request, res: Response) => {
            const { namespace, cluster } = req.body ?? {};
            const issues: string[] = [];
            if (isBlank(namespace)) {
              issues.push(
                'OpenShift namespace and cluster must both be provided.',
              );
            }
            if (isBlank(cluster)) {
              issues.push(
                'OpenShift namespace and cluster must both be provided.',
              );
            }
            if (issues.length > 0) {
              return res.status(422).json({ issues });
            }
            return res.status(200).json({ ok: true });
          },
        );

        httpRouter.use(router);
        httpRouter.addAuthPolicy({
          path: '/dev/mock-validate',
          allow: 'unauthenticated',
        });
      },
    });
  },
});

Workflow to test

id: test-managed-app-ci-validation
version: '1.0'
specVersion: '0.8'
name: Test Managed App CI Validation
description: |
  Local dev workflow mimicking customer managed-app-ci validation.
  Uses /api/orchestrator/dev/mock-validate to return 422 for blank fields.
  Reproduces stale validation errors after Next — clear field, click Next, then type.
dataInputSchema: 'schemas/test-managed-app-ci-validation.input-schema.json'
start: InjectState
states:
  - name: InjectState
    type: inject
    data:
      message: Hello from test-managed-app-ci-validation
    end: true
{
  "$id": "classpath:/schemas/test-managed-app-ci-validation.input-schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Test Managed App CI Validation",
  "description": "Mimics customer managed-app-ci xParams fields for local validation error testing",
  "type": "object",
  "ui:order": ["managed-app-ci"],
  "properties": {
    "managed-app-ci": {
      "title": "Managed App CI",
      "type": "object",
      "properties": {
        "xParams": {
          "type": "object",
          "title": "Managed App CI Parameters",
          "properties": {
            "containerRegistryNamespace": {
              "title": "Container Registry Namespace",
              "description": "The organization in the container registry where images are pushed.",
              "type": "string",
              "default": "",
              "ui:widget": "ActiveTextInput",
              "ui:props": {
                "validate:method": "POST",
                "validate:url": "$${{backend.baseUrl}}/api/orchestrator/dev/mock-validate/field",
                "validate:body": {
                  "value": "$${{current.managed-app-ci.xParams.containerRegistryNamespace}}",
                  "fieldLabel": "containerRegistryNamespace"
                }
              }
            },
            "fossaTeamName": {
              "title": "Fossa Team Name",
              "type": "string",
              "default": "",
              "ui:widget": "ActiveTextInput",
              "ui:props": {
                "validate:method": "POST",
                "validate:url": "$${{backend.baseUrl}}/api/orchestrator/dev/mock-validate/field",
                "validate:body": {
                  "value": "$${{current.managed-app-ci.xParams.fossaTeamName}}",
                  "fieldLabel": "fossaTeamName",
                  "requiredMessage": "Fossa Team Name is required when scaffolding a sample app."
                }
              }
            },
            "fossaSaUsername": {
              "title": "Fossa Service Account Username",
              "type": "string",
              "default": "",
              "ui:widget": "ActiveTextInput",
              "ui:props": {
                "validate:method": "POST",
                "validate:url": "$${{backend.baseUrl}}/api/orchestrator/dev/mock-validate/field",
                "validate:body": {
                  "value": "$${{current.managed-app-ci.xParams.fossaSaUsername}}",
                  "fieldLabel": "fossaSaUsername",
                  "requiredMessage": "Fossa Service Account Username is required."
                }
              }
            },
            "sonarProjectKey": {
              "title": "Sonar Project Key",
              "type": "string",
              "default": "",
              "ui:widget": "ActiveTextInput",
              "ui:props": {
                "validate:method": "POST",
                "validate:url": "$${{backend.baseUrl}}/api/orchestrator/dev/mock-validate/field",
                "validate:body": {
                  "value": "$${{current.managed-app-ci.xParams.sonarProjectKey}}",
                  "fieldLabel": "sonarProjectKey",
                  "requiredMessage": "Sonar Project Key is required when scaffolding a sample app."
                }
              }
            },
            "openshiftCluster": {
              "title": "OpenShift Cluster",
              "description": "Pair-validated with namespace (both required on Next).",
              "type": "string",
              "default": "",
              "ui:widget": "ActiveTextInput",
              "ui:props": {
                "validate:method": "POST",
                "validate:url": "$${{backend.baseUrl}}/api/orchestrator/dev/mock-validate/openshift-pair",
                "validate:body": {
                  "namespace": "$${{current.managed-app-ci.xParams.openshiftNamespace}}",
                  "cluster": "$${{current.managed-app-ci.xParams.openshiftCluster}}"
                }
              }
            },
            "openshiftNamespace": {
              "title": "OpenShift Namespace",
              "description": "Pair-validated with cluster (both required on Next).",
              "type": "string",
              "default": "",
              "ui:widget": "ActiveTextInput",
              "ui:props": {
                "validate:method": "POST",
                "validate:url": "$${{backend.baseUrl}}/api/orchestrator/dev/mock-validate/openshift-pair",
                "validate:body": {
                  "namespace": "$${{current.managed-app-ci.xParams.openshiftNamespace}}",
                  "cluster": "$${{current.managed-app-ci.xParams.openshiftCluster}}"
                }
              }
            }
          }
        }
      }
    }
  }
}

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

Stale async validation errors from Next stayed visible after typing; clear
only the changed field and forward RJSF field ids through multi-step forms.

Co-authored-by: Cursor <cursoragent@cursor.com>
@rhdh-gh-app

rhdh-gh-app Bot commented Jun 22, 2026

Copy link
Copy Markdown

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-orchestrator-form-react workspaces/orchestrator/plugins/orchestrator-form-react patch v2.9.0
@red-hat-developer-hub/backstage-plugin-orchestrator-form-widgets workspaces/orchestrator/plugins/orchestrator-form-widgets patch v1.11.0

@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 63.63636% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.64%. Comparing base (2e647e2) to head (a692c2a).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3522   +/-   ##
=======================================
  Coverage   53.63%   53.64%           
=======================================
  Files        2260     2261    +1     
  Lines       85972    86022   +50     
  Branches    24186    24152   -34     
=======================================
+ Hits        46112    46146   +34     
- Misses      38302    38318   +16     
  Partials     1558     1558           
Flag Coverage Δ *Carryforward flag
adoption-insights 83.70% <ø> (ø) Carriedforward from 2e647e2
ai-integrations 67.95% <ø> (ø) Carriedforward from 2e647e2
app-defaults 69.79% <ø> (ø) Carriedforward from 2e647e2
augment 46.39% <ø> (ø) Carriedforward from 2e647e2
boost 74.64% <ø> (ø) Carriedforward from 2e647e2
bulk-import 72.46% <ø> (ø) Carriedforward from 2e647e2
cost-management 14.10% <ø> (ø) Carriedforward from 2e647e2
dcm 61.79% <ø> (ø) Carriedforward from 2e647e2
extensions 61.53% <ø> (ø) Carriedforward from 2e647e2
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 2e647e2
global-header 59.71% <ø> (ø) Carriedforward from 2e647e2
homepage 49.84% <ø> (ø) Carriedforward from 2e647e2
install-dynamic-plugins 56.23% <ø> (ø) Carriedforward from 2e647e2
konflux 91.49% <ø> (ø) Carriedforward from 2e647e2
lightspeed 68.57% <ø> (ø) Carriedforward from 2e647e2
mcp-integrations 85.46% <ø> (ø) Carriedforward from 2e647e2
orchestrator 37.99% <63.63%> (+0.24%) ⬆️
quickstart 63.76% <ø> (ø) Carriedforward from 2e647e2
sandbox 79.56% <ø> (ø) Carriedforward from 2e647e2
scorecard 83.96% <ø> (ø) Carriedforward from 2e647e2
theme 61.26% <ø> (ø) Carriedforward from 2e647e2
translations 7.25% <ø> (ø) Carriedforward from 2e647e2
x2a 78.68% <ø> (ø) Carriedforward from 2e647e2

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2e647e2...a692c2a. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant