Skip to content

refactor: migrate CamelCatalogService catalog maps to async DynamicCatalogRegistry - #3447

Draft
lordrip wants to merge 4 commits into
KaotoIO:mainfrom
lordrip:chore/migrate-getNodeValidationText
Draft

refactor: migrate CamelCatalogService catalog maps to async DynamicCatalogRegistry#3447
lordrip wants to merge 4 commits into
KaotoIO:mainfrom
lordrip:chore/migrate-getNodeValidationText

Conversation

@lordrip

@lordrip lordrip commented Jul 7, 2026

Copy link
Copy Markdown
Member

Context

Refactors CamelCatalogService per the issue: removes the unused sync catalog-map helpers and migrates the language-map consumers to the async DynamicCatalogRegistry API, propagating the resulting async change up to the React render layer.

Changes

  • CamelCatalogService: delete unused getDataFormatMap and getLoadBalancerMap, and remove getLanguageMap.
  • ExpressionService: getLanguageNames, parseExpressionModel, and updateExpressionFromModel are now async, sourcing languages from DynamicCatalogRegistry.get().getCatalog(CatalogKind.Language)?.getAll().
  • Validation chain: ModelValidationService.validateNodeStatus / validateRequiredProperties and getNodeValidationText (interfaces + all visual-entity implementations + VisualizationNode) are now async.
  • Render layer:
    • ExpressionField parses the model in an effect and gates rendering on an isModelParsed flag, so the third-party useOneOfField mounts with the resolved model (otherwise it latches an empty selection).
    • Extracted a shared useNodeValidationText hook consumed by CustomNode and CustomGroupExpanded, removing duplicated async boilerplate. The hook keys on lastUpdate, fixing a regression where the validation warning would not refresh after in-place node edits.
  • Tests: updated to await the now-async APIs; added a unit test for useNodeValidationText (including re-validation on edit).

Closes #3442

Summary by CodeRabbit

  • New Features

    • Validation messages and expression fields now load and update asynchronously, improving reliability in the canvas and forms.
    • Group and node warning indicators now use a shared validation flow for more consistent results.
  • Bug Fixes

    • Fixed validation text and expression parsing so they refresh correctly after edits and async updates.
    • Improved handling of loading states to avoid stale or missing validation results.
  • Tests

    • Updated and expanded test coverage for asynchronous validation, parsing, and UI rendering flows.

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f8454a1a-eaae-4a31-bff7-e4c949b96bf4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR migrates getNodeValidationText across visual entities, VisualizationNode, and ModelValidationService to async/await, backed by an async validateRequiredProperties. It converts ExpressionService and ExpressionField to use async DynamicCatalogRegistry-based language resolution, removes unused CamelCatalogService map methods, and adds a useNodeValidationText hook wired into CustomNode/CustomGroupExpanded, with corresponding test updates.

Changes

Async validation and expression parsing migration

Layer / File(s) Summary
Core validation contract and service conversion
base-visual-entity.ts, model-validation.service.ts, model-validation.service.test.ts, camel-catalog.service.ts, camel-catalog.service.test.ts
getNodeValidationText interfaces become async; ModelValidationService.validateNodeStatus/validateRequiredProperties are converted to async with awaited recursion and expression parsing; unused getLanguageMap/getDataFormatMap/getLoadBalancerMap are removed from CamelCatalogService along with their tests.
Visual entity async conversions
abstract-camel-visual-entity.ts, visualization-node.ts, camel-error-handler-visual-entity.ts, camel-intercept-*-visual-entity.ts, camel-on-completion-visual-entity.ts, camel-rest-configuration-visual-entity.ts, camel-rest-visual-entity.test.ts, camel-route-configuration-visual-entity.test.ts, citrus-test-visual-entity.ts, pipe-visual-entity.ts, plus matching test files
Each entity's getNodeValidationText returns `Promise<string
useNodeValidationText hook and wiring
use-node-validation-text.hook.ts, use-node-validation-text.hook.test.ts, CustomNode.tsx, CustomGroupExpanded.tsx, associated test files
New hook resolves validation text asynchronously with cancellation guard and error logging; wired into CustomNode and CustomGroupExpanded replacing direct calls; tests updated with mockResolvedValue.
ExpressionField/Service async migration
expression.service.ts, expression.service.test.ts, ExpressionField.tsx, ExpressionField.test.tsx
parseExpressionModel, getLanguageNames, updateExpressionFromModel become async using DynamicCatalogRegistry; ExpressionField parses via useEffect/useState with a cancellation guard; tests use act/waitFor/findByTestId.
Canvas spy fix
Canvas.test.tsx
Fit/layout spies moved to the graph prototype instead of a specific instance for reliability.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • KaotoIO/kaoto#3088: Also updates citrus-test-visual-entity tests around getNodeValidationText, aligning with this PR's async conversion.

Suggested reviewers: PVinaches, igarashitm

Poem

A hop, a skip, a Promise chain,
getNodeValidationText async again,
Catalogs dance in registries new,
While hooks resolve what nodes construe,
No more sync burrows to hide the wait —
This bunny approves this await-ful update! 🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR removes the unused catalog helpers and converts expression-related lookups to async as requested in #3442.
Out of Scope Changes check ✅ Passed The changes stay focused on async catalog migration, validation, and related test updates; no unrelated scope stands out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the core refactor: migrating catalog map access to the async DynamicCatalogRegistry.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@lordrip
lordrip marked this pull request as draft July 7, 2026 13:37

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/ui/src/components/Visualization/Custom/hooks/use-node-validation-text.hook.test.ts (1)

16-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing coverage for vizNode identity swap.

Tests cover initial resolve, in-place edits via lastUpdate, no-node, and rejection, but not switching renderHook to a different vizNode instance mid-lifecycle — the scenario tied to the stale-state gap flagged in use-node-validation-text.hook.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ui/src/components/Visualization/Custom/hooks/use-node-validation-text.hook.test.ts`
around lines 16 - 73, The `useNodeValidationText` test suite is missing coverage
for swapping to a different `vizNode` instance during the hook lifecycle, which
can hide stale-state bugs. Add a test in `use-node-validation-text.hook.test.ts`
using `renderHook` and `rerender` to replace the original node with a new
`IVisualizationNode` object, then assert the hook re-runs
`getNodeValidationText` and updates `result.current` accordingly. Use the
existing `createNode`, `renderHook`, and `useNodeValidationText` setup so the
new test targets the same identity-change path as the hook logic.
packages/ui/src/models/visualization/flows/support/validators/model-validation.service.ts (1)

71-117: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Sequential await in independent loops could be parallelized.

The oneOf loop (Lines 72-74) and the property-iteration recursion (Lines 84-117) await each recursive call one at a time even though the branches are logically independent of each other (unlike the anyOf loop, which intentionally threads parsedModel sequentially). Since this validation runs per-node and potentially on every render via the upcoming useNodeValidationText hook, sequential awaiting of independent, catalog-backed recursive calls adds unnecessary latency for schemas with many properties/oneOf branches.

♻️ Suggested refactor using Promise.all for independent branches
-    if (Array.isArray(resolvedSchema.oneOf)) {
-      for (const oneOfSchema of resolvedSchema.oneOf) {
-        answer.push(...(await this.validateRequiredProperties(oneOfSchema, model, parentPath, definitions)));
-      }
-    }
+    if (Array.isArray(resolvedSchema.oneOf)) {
+      const oneOfResults = await Promise.all(
+        resolvedSchema.oneOf.map((oneOfSchema) =>
+          this.validateRequiredProperties(oneOfSchema, model, parentPath, definitions),
+        ),
+      );
+      oneOfResults.forEach((result) => answer.push(...result));
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ui/src/models/visualization/flows/support/validators/model-validation.service.ts`
around lines 71 - 117, The validation in model-validation.service.ts is awaiting
independent recursive branches one by one in validateRequiredProperties, which
adds avoidable latency. Refactor the resolvedSchema.oneOf handling and the
schema.properties iteration to build and await those independent recursive calls
in parallel (for example, via Promise.all), while keeping the anyOf-style
sequential logic unchanged if it depends on parsedModel updates. Use
validateRequiredProperties, resolveSchemaWithRef, and the property iteration
logic to locate the affected paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/ui/src/components/Visualization/Canvas/Form/fields/ExpressionField/ExpressionField.tsx`:
- Around line 49-64: The ExpressionField render gate is left stuck when
ExpressionService.parseExpressionModel rejects because the catch path only logs
the error and never resolves the parsed state. Update the useEffect in
ExpressionField to handle failures by clearing or setting parsed state on the
error path as well, so isModelParsed is always settled and the component can
render instead of remaining null. Use the existing
ExpressionService.parseExpressionModel, setParsedModel, and setIsModelParsed
flow as the place to fix both success and failure handling.

In
`@packages/ui/src/components/Visualization/Custom/hooks/use-node-validation-text.hook.ts`:
- Around line 15-36: The hook useNodeValidationText is leaving stale validation
text visible when vizNode changes or becomes undefined because validationText is
only updated after getNodeValidationText resolves. Reset validationText
synchronously at the start of the effect whenever vizNode identity changes (and
before kicking off the async lookup) so CustomNode and CustomGroupExpanded don’t
briefly render the previous node’s warning state. Keep the existing cancellation
logic, and make sure the reset happens in the useEffect that depends on vizNode
and lastUpdate.

---

Nitpick comments:
In
`@packages/ui/src/components/Visualization/Custom/hooks/use-node-validation-text.hook.test.ts`:
- Around line 16-73: The `useNodeValidationText` test suite is missing coverage
for swapping to a different `vizNode` instance during the hook lifecycle, which
can hide stale-state bugs. Add a test in `use-node-validation-text.hook.test.ts`
using `renderHook` and `rerender` to replace the original node with a new
`IVisualizationNode` object, then assert the hook re-runs
`getNodeValidationText` and updates `result.current` accordingly. Use the
existing `createNode`, `renderHook`, and `useNodeValidationText` setup so the
new test targets the same identity-change path as the hook logic.

In
`@packages/ui/src/models/visualization/flows/support/validators/model-validation.service.ts`:
- Around line 71-117: The validation in model-validation.service.ts is awaiting
independent recursive branches one by one in validateRequiredProperties, which
adds avoidable latency. Refactor the resolvedSchema.oneOf handling and the
schema.properties iteration to build and await those independent recursive calls
in parallel (for example, via Promise.all), while keeping the anyOf-style
sequential logic unchanged if it depends on parsedModel updates. Use
validateRequiredProperties, resolveSchemaWithRef, and the property iteration
logic to locate the affected paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f3b7ffdb-367d-4074-aebf-f26fb8964d48

📥 Commits

Reviewing files that changed from the base of the PR and between 2babf9f and 08a362b.

📒 Files selected for processing (34)
  • packages/ui/src/components/Visualization/Canvas/Canvas.test.tsx
  • packages/ui/src/components/Visualization/Canvas/Form/fields/ExpressionField/ExpressionField.test.tsx
  • packages/ui/src/components/Visualization/Canvas/Form/fields/ExpressionField/ExpressionField.tsx
  • packages/ui/src/components/Visualization/Canvas/Form/fields/ExpressionField/expression.service.test.ts
  • packages/ui/src/components/Visualization/Canvas/Form/fields/ExpressionField/expression.service.ts
  • packages/ui/src/components/Visualization/Custom/Group/CustomGroupExpanded.test.tsx
  • packages/ui/src/components/Visualization/Custom/Group/CustomGroupExpanded.tsx
  • packages/ui/src/components/Visualization/Custom/Node/CustomNode.test.tsx
  • packages/ui/src/components/Visualization/Custom/Node/CustomNode.tsx
  • packages/ui/src/components/Visualization/Custom/hooks/use-node-validation-text.hook.test.ts
  • packages/ui/src/components/Visualization/Custom/hooks/use-node-validation-text.hook.ts
  • packages/ui/src/models/visualization/base-visual-entity.ts
  • packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts
  • packages/ui/src/models/visualization/flows/camel-catalog.service.test.ts
  • packages/ui/src/models/visualization/flows/camel-catalog.service.ts
  • packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/camel-error-handler-visual-entity.ts
  • packages/ui/src/models/visualization/flows/camel-intercept-from-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/camel-intercept-send-to-endpoint-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/camel-intercept-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/camel-on-completion-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/camel-rest-configuration-visual-entity.ts
  • packages/ui/src/models/visualization/flows/camel-rest-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/camel-route-configuration-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/citrus-test-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/citrus-test-visual-entity.ts
  • packages/ui/src/models/visualization/flows/pipe-visual-entity.test.ts
  • packages/ui/src/models/visualization/flows/pipe-visual-entity.ts
  • packages/ui/src/models/visualization/flows/support/validators/model-validation.service.test.ts
  • packages/ui/src/models/visualization/flows/support/validators/model-validation.service.ts
  • packages/ui/src/models/visualization/visualization-node.test.ts
  • packages/ui/src/models/visualization/visualization-node.ts
💤 Files with no reviewable changes (2)
  • packages/ui/src/models/visualization/flows/camel-catalog.service.ts
  • packages/ui/src/models/visualization/flows/camel-catalog.service.test.ts

Comment on lines +49 to +64
useEffect(() => {
let cancelled = false;
ExpressionService.parseExpressionModel(originalModel)
.then((model) => {
if (!cancelled) {
setParsedModel(model);
setIsModelParsed(true);
}
})
.catch((error) => {
console.error('Failed to parse expression model:', error);
});
return () => {
cancelled = true;
};
}, [originalModel]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Field permanently hidden if parseExpressionModel rejects.

The .catch() handler only logs the error and never sets isModelParsed. Since rendering returns null until isModelParsed is true (Line 66-68), a rejected parse leaves the field permanently blank with no way to recover or surface the failure to the user. Based on a related learning, async UI state (here the "parsed" gate) should be resolved consistently on both success and failure paths rather than left stuck.

🐛 Suggested fix: unblock rendering on error too
       .catch((error) => {
-        console.error('Failed to parse expression model:', error);
+        if (!cancelled) {
+          console.error('Failed to parse expression model:', error);
+          setIsModelParsed(true);
+        }
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
let cancelled = false;
ExpressionService.parseExpressionModel(originalModel)
.then((model) => {
if (!cancelled) {
setParsedModel(model);
setIsModelParsed(true);
}
})
.catch((error) => {
console.error('Failed to parse expression model:', error);
});
return () => {
cancelled = true;
};
}, [originalModel]);
useEffect(() => {
let cancelled = false;
ExpressionService.parseExpressionModel(originalModel)
.then((model) => {
if (!cancelled) {
setParsedModel(model);
setIsModelParsed(true);
}
})
.catch((error) => {
if (!cancelled) {
console.error('Failed to parse expression model:', error);
setIsModelParsed(true);
}
});
return () => {
cancelled = true;
};
}, [originalModel]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ui/src/components/Visualization/Canvas/Form/fields/ExpressionField/ExpressionField.tsx`
around lines 49 - 64, The ExpressionField render gate is left stuck when
ExpressionService.parseExpressionModel rejects because the catch path only logs
the error and never resolves the parsed state. Update the useEffect in
ExpressionField to handle failures by clearing or setting parsed state on the
error path as well, so isModelParsed is always settled and the component can
render instead of remaining null. Use the existing
ExpressionService.parseExpressionModel, setParsedModel, and setIsModelParsed
flow as the place to fix both success and failure handling.

Source: Learnings

Comment on lines +15 to +36
export const useNodeValidationText = (vizNode?: IVisualizationNode): string | undefined => {
const [validationText, setValidationText] = useState<string | undefined>(undefined);
const lastUpdate = vizNode?.lastUpdate;

useEffect(() => {
let cancelled = false;
vizNode
?.getNodeValidationText()
.then((text) => {
if (!cancelled) setValidationText(text);
})
.catch((error) => {
console.error('Failed to get node validation text:', error);
});
return () => {
cancelled = true;
};
// `lastUpdate` is intentionally included so the text re-resolves when the node is edited in place.
}, [vizNode, lastUpdate]);

return validationText;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Stale validation text can persist briefly when vizNode identity changes.

When vizNode switches to a different node instance (or becomes undefined), vizNode?.getNodeValidationText() short-circuits the whole optional chain, so setValidationText is never called until the new promise resolves. Consumers (CustomNode/CustomGroupExpanded) will render the previous node's warning state for a render or two, which is incorrect if elements/components get reused for a different vizNode. It self-corrects once the new promise settles, but the transient wrong state could still surface a misleading warning badge.

🔧 Proposed fix: reset text synchronously when the node identity changes
   useEffect(() => {
     let cancelled = false;
+    setValidationText(undefined);
     vizNode
       ?.getNodeValidationText()
       .then((text) => {
         if (!cancelled) setValidationText(text);
       })
       .catch((error) => {
         console.error('Failed to get node validation text:', error);
       });
     return () => {
       cancelled = true;
     };
     // `lastUpdate` is intentionally included so the text re-resolves when the node is edited in place.
   }, [vizNode, lastUpdate]);

Note this also resets on lastUpdate bumps for the same node (brief flicker to undefined before re-resolving); if that's undesirable, key the reset off a separate effect scoped only to vizNode identity.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const useNodeValidationText = (vizNode?: IVisualizationNode): string | undefined => {
const [validationText, setValidationText] = useState<string | undefined>(undefined);
const lastUpdate = vizNode?.lastUpdate;
useEffect(() => {
let cancelled = false;
vizNode
?.getNodeValidationText()
.then((text) => {
if (!cancelled) setValidationText(text);
})
.catch((error) => {
console.error('Failed to get node validation text:', error);
});
return () => {
cancelled = true;
};
// `lastUpdate` is intentionally included so the text re-resolves when the node is edited in place.
}, [vizNode, lastUpdate]);
return validationText;
};
export const useNodeValidationText = (vizNode?: IVisualizationNode): string | undefined => {
const [validationText, setValidationText] = useState<string | undefined>(undefined);
const lastUpdate = vizNode?.lastUpdate;
useEffect(() => {
let cancelled = false;
setValidationText(undefined);
vizNode
?.getNodeValidationText()
.then((text) => {
if (!cancelled) setValidationText(text);
})
.catch((error) => {
console.error('Failed to get node validation text:', error);
});
return () => {
cancelled = true;
};
// `lastUpdate` is intentionally included so the text re-resolves when the node is edited in place.
}, [vizNode, lastUpdate]);
return validationText;
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/ui/src/components/Visualization/Custom/hooks/use-node-validation-text.hook.ts`
around lines 15 - 36, The hook useNodeValidationText is leaving stale validation
text visible when vizNode changes or becomes undefined because validationText is
only updated after getNodeValidationText resolves. Reset validationText
synchronously at the start of the effect whenever vizNode identity changes (and
before kicking off the async lookup) so CustomNode and CustomGroupExpanded don’t
briefly render the previous node’s warning state. Keep the existing cancellation
logic, and make sure the reset happens in the useEffect that depends on vizNode
and lastUpdate.

@lordrip
lordrip force-pushed the chore/migrate-getNodeValidationText branch from 08a362b to 0164b2a Compare July 8, 2026 15:10
lordrip and others added 4 commits July 13, 2026 10:15
Completes the sync->async migration by consuming the now-async
getNodeValidationText / parseExpressionModel APIs in the React render
layer and removing the unused sync catalog map helpers.

- ExpressionField: parse the expression model in an effect and gate
  rendering on an `isModelParsed` flag. `useOneOfField` latches its
  selected schema on its mount render only, so mounting the inner field
  before the async parse resolves would latch an empty selection that
  never recovers (no expression controls / clear button). The flag stays
  true afterwards so later model changes re-parse without remounting.
- CustomNode / CustomGroupExpanded: read validation text via
  useState/useEffect over the async getNodeValidationText, with a
  .catch that logs failures.
- CamelCatalogService: delete the now-unused getLanguageMap,
  getDataFormatMap and getLoadBalancerMap helpers.
- Tests: await the async initial render where needed, switch
  getNodeValidationText mocks to mockResolvedValue, spy on the graph
  prototype in Canvas.test so fit(80) is captured regardless of the
  fromModel instance swap or rAF timing, and refresh the snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CustomNode and CustomGroupExpanded carried an identical useState/useEffect
block resolving the async getNodeValidationText. Consolidate it into a
shared `useNodeValidationText` hook.

This also fixes a staleness bug introduced when getNodeValidationText
became async: the effect was keyed on the node reference only, but edits
mutate the node in place and bump `lastUpdate` without changing the
reference, so the warning icon never refreshed after an edit. The hook
keys on `lastUpdate` as well, restoring the pre-migration behaviour where
validation was recomputed on every change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lordrip
lordrip force-pushed the chore/migrate-getNodeValidationText branch from 0164b2a to 97ad93c Compare July 13, 2026 08:15
@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

This pull request is stale because it has been open for 14 days with no activity. It will be closed if there is no activity in the next 14 days.

@github-actions github-actions Bot added the stale Marks issues that were not updated for a long time. label Jul 27, 2026
@lordrip lordrip removed the stale Marks issues that were not updated for a long time. label Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate getNodeValidationText and getLanguageMap methods

1 participant