This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new 43ca4b2c70 refactor(frontend): avoid redundant validateOperator call 
in applyOperatorBorder (#5702)
43ca4b2c70 is described below

commit 43ca4b2c70849af57656bc9a777d0e53f617bcc4
Author: Prateek Ganigi <[email protected]>
AuthorDate: Wed Jun 24 22:55:06 2026 -0700

    refactor(frontend): avoid redundant validateOperator call in 
applyOperatorBorder (#5702)
    
    ### What changes were proposed in this PR?
    
    WorkflowEditorComponent.applyOperatorBorder(operatorID)` always
    recomputes validation as its first step:
    
    ```typescript
    const validation = 
this.validationWorkflowService.validateOperator(operatorID);
    ```
    
    This is redundant when the helper is called from the validation-stream
    subscriber in handleOperatorValidation, the stream's emitted event
    already carries the Validation result that was just computed by
    updateValidationState.
    
    This PR:
    - Adds an optional validation?: Validation parameter to
    applyOperatorBorder. The helper uses it when provided, and falls back to
    validateOperator(operatorID) otherwise.
    - Updates handleOperatorValidation to pass value.validation from the
    stream into the helper.
    - Leaves the operator-add stream subscriber unchanged and hence it
    doesn't have a Validation in hand at that point, so it correctly falls
    through to the lazy-fetch path.
    - Functionally identical (the stream emits the same Validation that
    would have been recomputed), purely avoids the duplicate call.
    
    Originally flagged as an optional nit during the PR #5146 review.
    Attempted in PR #5626 but split out as per the reviewer's request so
    that PR could stay scoped to test restructuring; this PR completes the
    follow-up.
    
    ### Any related issues, documentation, discussions?
    
    Closes #5683
    Related: PR #5146 (where the nit was raised), PR #5626 (where this was
    attempted and split out).
    
    ### How was this PR tested?
    
    Added one focused unit test in `workflow-editor.component.spec.ts`
    inside the existing `operator border restoration after navigation`
    describe block: `it("uses the Validation passed in instead of
    recomputing it", ...)`. The test clears the `validateOperator` spy after
    the operator-add validation chain settles, then calls
    `applyOperatorBorder` directly with a `Validation` argument and asserts
    `validateOperator` is not called.
    
    Verified locally:
    - `tsc --noEmit`: clean
    - `eslint`: clean
    - `ng test` (jsdom): 26/26 pass in the editor spec (was 25, new test
    adds one)
    - `ng run gui:test-browser`: 13/13 pass
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    This PR was co-authored using Claude Code (Anthropic Claude Opus 4.7)
---
 .../workflow-editor.component.spec.ts              | 28 ++++++++++++++++++++++
 .../workflow-editor/workflow-editor.component.ts   | 14 +++++++----
 2 files changed, 37 insertions(+), 5 deletions(-)

diff --git 
a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts
 
b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts
index c87ea058e7..81a84081a5 100644
--- 
a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts
+++ 
b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.spec.ts
@@ -910,6 +910,34 @@ describe("WorkflowEditorComponent", () => {
 
         expect(getStroke(mockScanPredicate.operatorID)).toBe("red");
       });
+
+      it("uses the Validation passed in instead of recomputing it", () => {
+        // Let the validation chain settle from the operator-add so the spy
+        // below is created after those calls and starts with a clean slate.
+        workflowActionService.addOperator(mockScanPredicate, mockPoint);
+        fixture.detectChanges();
+
+        const validateSpy = vi.spyOn(validationWorkflowService, 
"validateOperator");
+
+        // Call the helper directly with a Validation argument, mirroring what
+        // the validation-stream subscriber does at runtime
+        // (handleOperatorValidation passes value.validation through).
+        (component as any).applyOperatorBorder(mockScanPredicate.operatorID, { 
isValid: true });
+
+        expect(validateSpy).not.toHaveBeenCalled();
+      });
+
+      it("honors the passed-in Validation result (paints red when it is 
invalid)", () => {
+        // Proves the passed-in value actually drives the border, not just that
+        // the recompute is skipped: an invalid result must paint red.
+        vi.spyOn(workflowStatusService, 
"getCurrentStatus").mockReturnValue({});
+        workflowActionService.addOperator(mockScanPredicate, mockPoint);
+        fixture.detectChanges();
+
+        (component as any).applyOperatorBorder(mockScanPredicate.operatorID, { 
isValid: false, messages: {} });
+
+        expect(getStroke(mockScanPredicate.operatorID)).toBe("red");
+      });
     });
   });
 });
diff --git 
a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts
 
b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts
index 5411ea995a..1a9eb89e05 100644
--- 
a/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts
+++ 
b/frontend/src/app/workspace/component/workflow-editor/workflow-editor.component.ts
@@ -25,7 +25,7 @@ import { DragDropService } from 
"../../service/drag-drop/drag-drop.service";
 import { DynamicSchemaService } from 
"../../service/dynamic-schema/dynamic-schema.service";
 import { ExecuteWorkflowService } from 
"../../service/execute-workflow/execute-workflow.service";
 import { fromJointPaperEvent, JointUIService, linkPathStrokeColor } from 
"../../service/joint-ui/joint-ui.service";
-import { ValidationWorkflowService } from 
"../../service/validation/validation-workflow.service";
+import { Validation, ValidationWorkflowService } from 
"../../service/validation/validation-workflow.service";
 import { WorkflowActionService } from 
"../../service/workflow-graph/model/workflow-action.service";
 import { WorkflowStatusService } from 
"../../service/workflow-status/workflow-status.service";
 import { ExecutionState, OperatorState } from 
"../../types/execute-workflow.interface";
@@ -397,10 +397,14 @@ export class WorkflowEditorComponent implements OnInit, 
AfterViewInit, OnDestroy
    * Centralizing this here avoids the race where the validation pass
    * overwrites a state-derived stroke (or vice versa) for an operator that
    * is both invalid and has a cached execution status.
+   *
+   * Callers that already have a Validation result (the validation stream
+   * subscriber) may pass it in to avoid recomputing it; callers without one
+   * (the operator-add stream subscriber) let the helper fetch it lazily.
    */
-  private applyOperatorBorder(operatorID: string): void {
-    const validation = 
this.validationWorkflowService.validateOperator(operatorID);
-    if (!validation.isValid) {
+  private applyOperatorBorder(operatorID: string, validation?: Validation): 
void {
+    const resolved = validation ?? 
this.validationWorkflowService.validateOperator(operatorID);
+    if (!resolved.isValid) {
       this.jointUIService.changeOperatorColor(this.paper, operatorID, false);
       return;
     }
@@ -1025,7 +1029,7 @@ export class WorkflowEditorComponent implements OnInit, 
AfterViewInit, OnDestroy
     this.validationWorkflowService
       .getOperatorValidationStream()
       .pipe(untilDestroyed(this))
-      .subscribe(value => this.applyOperatorBorder(value.operatorID));
+      .subscribe(value => this.applyOperatorBorder(value.operatorID, 
value.validation));
   }
 
   /**

Reply via email to