Skip to content

fix(ui): ensure oneOf choices inherit type and properties for forms#3535

Open
QuantumBreakz wants to merge 3 commits into
KaotoIO:mainfrom
QuantumBreakz:fix/3508-oneof-choices-non-object-elements
Open

fix(ui): ensure oneOf choices inherit type and properties for forms#3535
QuantumBreakz wants to merge 3 commits into
KaotoIO:mainfrom
QuantumBreakz:fix/3508-oneof-choices-non-object-elements

Conversation

@QuantumBreakz

@QuantumBreakz QuantumBreakz commented Jul 21, 2026

Copy link
Copy Markdown

Fixes #3508

Problem:
The Kaoto form engine (@kaoto/forms) relies on each choice in a oneOf JSON schema array having type: 'object' and its own properties structure. When a oneOf schema choice only defines constraints (e.g. {"required": ["data"]}) on properties defined in the parent object, the form engine cannot render an appropriate choice option for it.

Solution:
Created a normalizeOneOfChoices utility that intercepts JSON schemas before they are handed to KaotoForm. When traversing the schema, if a oneOf choice contains constraints but no type/properties, it now implicitly assumes type: 'object' and inherits the corresponding property definitions (and titles) from the parent schema.

Summary by CodeRabbit

  • Bug Fixes

    • Improved visualization forms so alternative field configurations are interpreted correctly.
    • Ensured nested choices inherit relevant field details and titles, resulting in more complete and accurate form rendering.
    • Reduced cases where valid visualization components could be incorrectly treated as unsupported.
  • Tests

    • Added coverage for nested schema choices, inherited properties, titles, and primitive values.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c0df7276-2ea4-4b02-a18e-9ada7263a06d

📥 Commits

Reviewing files that changed from the base of the PR and between dd649ef and 9c10bdb.

📒 Files selected for processing (4)
  • packages/ui/src/components/Visualization/Canvas/Form/CanvasFormBody.tsx
  • packages/ui/src/utils/index.ts
  • packages/ui/src/utils/normalize-oneof-choices.test.ts
  • packages/ui/src/utils/normalize-oneof-choices.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/ui/src/utils/index.ts
  • packages/ui/src/utils/normalize-oneof-choices.test.ts
  • packages/ui/src/components/Visualization/Canvas/Form/CanvasFormBody.tsx

📝 Walkthrough

Walkthrough

normalizeOneOfChoices normalizes incomplete nested oneOf schemas, exports the utility, applies it to visualization form schemas, and adds tests for passthrough and nested-choice behavior.

Changes

OneOf schema normalization

Layer / File(s) Summary
Normalize and apply oneOf choices
packages/ui/src/utils/normalize-oneof-choices.ts, packages/ui/src/utils/index.ts, packages/ui/src/components/Visualization/Canvas/Form/CanvasFormBody.tsx, packages/ui/src/utils/normalize-oneof-choices.test.ts
The new utility deep-clones and recursively normalizes schema combinators, infers missing object types and properties, propagates titles, exposes the utility through the barrel, applies it before form rendering, and tests passthrough and nested schemas.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

I’m a rabbit with schemas to mend,
OneOf choices now properly blend.
Through nested paths they hop,
With titles on top—
And forms bloom where the branches all end.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: fixing oneOf choice handling in UI forms.
Linked Issues check ✅ Passed The normalization utility and CanvasFormBody integration address #3508's oneOf rendering issue.
Out of Scope Changes check ✅ Passed The added utility, export, and tests are all directly related to the oneOf form-rendering fix.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@QuantumBreakz
QuantumBreakz force-pushed the fix/3508-oneof-choices-non-object-elements branch from dd649ef to cf1928b Compare July 21, 2026 18:00

@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: 3

🧹 Nitpick comments (2)
packages/ui/src/services/document/xml-schema/xml-schema-document.service.test.ts (1)

1192-1193: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Run Prettier on the added test block.

Remove the trailing whitespace and format the callback consistently.

Proposed formatting
-      
-      const orderElement = rootElement.fields.find(f => f.name === 'Order');
+
+      const orderElement = rootElement.fields.find((f) => f.name === 'Order');

As per coding guidelines, packages/ui/**/*.{ts,tsx} must use Prettier formatting.

🤖 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/services/document/xml-schema/xml-schema-document.service.test.ts`
around lines 1192 - 1193, Format the added test block according to Prettier,
specifically removing trailing whitespace and applying consistent formatting to
the callback used in the rootElement.fields.find expression.

Source: Coding guidelines

packages/ui/src/utils/normalize-oneof-choices.test.ts (1)

10-55: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a regression test for recursive schemas.

The current fixture only exercises anyOfoneOf where parentProperties is available, so it does not detect the discarded-clone bug. Add a case with oneOf nested under properties or items, and assert both normalization and preservation of the original input schema.

🤖 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/utils/normalize-oneof-choices.test.ts` around lines 10 - 55,
Extend the test in normalizeOneOfChoices with a recursive schema containing
oneOf nested under a property or items, where parentProperties is unavailable.
Assert the nested choices are normalized as expected and verify the original
schema remains unchanged after normalization, covering the discarded-clone
regression.
🤖 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/services/document/xml-schema/xml-schema-document.service.ts`:
- Around line 514-529: Update the reachability traversal around the queue in the
filterUnreachable logic to treat xs:include relationships bidirectionally: from
each schema, enqueue included schemas and the schemas that include it, while
excluding xs:import relationships. Preserve the existing reachableSchemas
tracking and add a regression covering a root in one included file referencing a
type in a sibling included file.

In `@packages/ui/src/utils/normalize-oneof-choices.ts`:
- Around line 47-58: Update normalizeOneOfChoices so recursive calls in the
properties and generic object branches preserve and assign each returned
normalized child schema instead of discarding it. Ensure nested
oneOf/anyOf/allOf structures under properties, items, or combinators are
replaced with the normalized clones while retaining the existing
currentProperties behavior.
- Around line 27-31: Update the required-property handling in the loop over
choice.required to check currentProperties with
Object.prototype.hasOwnProperty.call before reading or copying values,
preventing inherited keys from being accepted. Ensure choice.properties is a
null-prototype object or copy properties via Object.defineProperty so keys such
as __proto__ cannot trigger the object prototype setter, while preserving title
propagation.

---

Nitpick comments:
In
`@packages/ui/src/services/document/xml-schema/xml-schema-document.service.test.ts`:
- Around line 1192-1193: Format the added test block according to Prettier,
specifically removing trailing whitespace and applying consistent formatting to
the callback used in the rootElement.fields.find expression.

In `@packages/ui/src/utils/normalize-oneof-choices.test.ts`:
- Around line 10-55: Extend the test in normalizeOneOfChoices with a recursive
schema containing oneOf nested under a property or items, where parentProperties
is unavailable. Assert the nested choices are normalized as expected and verify
the original schema remains unchanged after normalization, covering the
discarded-clone regression.
🪄 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: 7098c8d7-4bb9-45bd-aa5d-b37692613a6b

📥 Commits

Reviewing files that changed from the base of the PR and between 468c9b8 and dd649ef.

📒 Files selected for processing (6)
  • packages/ui/src/components/Visualization/Canvas/Form/CanvasFormBody.tsx
  • packages/ui/src/services/document/xml-schema/xml-schema-document.service.test.ts
  • packages/ui/src/services/document/xml-schema/xml-schema-document.service.ts
  • packages/ui/src/utils/index.ts
  • packages/ui/src/utils/normalize-oneof-choices.test.ts
  • packages/ui/src/utils/normalize-oneof-choices.ts

Comment on lines +514 to +529
if (filterUnreachable && document.rootElement) {
const parentSchema = document.rootElement.getParent();
const reachableSchemas = new Set<XmlSchema>();
const queue: XmlSchema[] = [parentSchema];

while (queue.length > 0) {
const current = queue.shift()!;
if (!reachableSchemas.has(current)) {
reachableSchemas.add(current);
const externals = current.getExternals();
for (const ext of externals) {
const schema = ext.getSchema();
if (schema) {
queue.push(schema);
}
}

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 | 🟠 Major | 🏗️ Heavy lift

Traverse xs:include relationships in both directions.

When the selected root is declared in an included schema, this only follows that schema’s outgoing references. Sibling schemas included by the same parent are filtered out, so referenced types can retain namedTypeFragmentRefs with no fragment. Treat xs:include edges as bidirectional (but not xs:import) and add a regression with a root in one included file referencing a type in another.

🤖 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/services/document/xml-schema/xml-schema-document.service.ts`
around lines 514 - 529, Update the reachability traversal around the queue in
the filterUnreachable logic to treat xs:include relationships bidirectionally:
from each schema, enqueue included schemas and the schemas that include it,
while excluding xs:import relationships. Preserve the existing reachableSchemas
tracking and add a regression covering a root in one included file referencing a
type in a sibling included file.

Comment thread packages/ui/src/utils/normalize-oneof-choices.ts
Comment thread packages/ui/src/utils/normalize-oneof-choices.ts Outdated
…aotoIO#3508)

Problem:
The Kaoto form engine (uniforms via `@kaoto/forms`) relies on each choice in a `oneOf` JSON schema array having `type: 'object'` and its own `properties` structure. When a `oneOf` schema choice only defines constraints (e.g. `{"required": ["data"]}`) on properties defined in the parent object, the form engine cannot render an appropriate choice option for it, leading to visual bugs and empty form sections.

Solution:
Created a `normalizeOneOfChoices` utility that intercepts JSON schemas before they are handed to `KaotoForm`. When traversing the schema, if a `oneOf` choice contains constraints but no type/properties, it now implicitly assumes `type: 'object'` and inherits the corresponding property definitions (and titles) from the parent schema.

Fixes KaotoIO#3508
@QuantumBreakz
QuantumBreakz force-pushed the fix/3508-oneof-choices-non-object-elements branch from 1fc5a14 to 1bcbb8a Compare July 21, 2026 18:08
@sonarqubecloud

Copy link
Copy Markdown

@PVinaches

Copy link
Copy Markdown
Member

Hello! Thanks for raising this PR, it is a very interesting solution. Have you checked if it could be solved directly from camel-catalog repo (here)?

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.

OneOf choice is not rendered properly with non-object typed elements

2 participants