Skip to content

CAMEL-25481: camel-dataweave - convert XML namespaces, attributes and the DataSonnet XML model - #27612

Merged
davsclaus merged 3 commits into
mainfrom
fix/CAMEL-25481
Oct 9, 2026
Merged

davsclaus merged 3 commits into
mainfrom
fix/CAMEL-25481

Conversation

@davsclaus

@davsclaus davsclaus commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

CAMEL-25481: the DataWeave to DataSonnet conversion (camel-dataweave, with the camel-dataweave.libsonnet runtime library of camel-datasonnet) now follows the DataWeave XML semantics. Before this change, XML namespaces and attributes in the output failed the conversion. Worse, several XML input cases converted without error but gave wrong results: a SOAP selector such as payload.Envelope.Body returned null, and payload.order written as JSON included the attributes, namespace declarations and ~ positions of the DataSonnet XML model.

XML input

  • A selector matches an element by its local name, whatever its namespace prefix (SOAP envelopes). .*name and ..*name give the elements of every namespace with that local name, in document order.
  • ns header directives are supported, and .ns#name matches only in that namespace (payload.s#Envelope.o#Body is null, as in DataWeave).
  • .@ gives all the attributes, and ..*name all the descendants (..name takes the first of an element that repeats).
  • An empty element is null. .name of an element that repeats gives the first, .*name gives all of them. Text next to CDATA is read once, and adjacent text and CDATA in mixed content are one __text.
  • Attributes can be read from an element in a variable or given to a lambda (o.@id, $.@id).
  • keysOf, sizeOf, mapObject, pluck, entriesOf (with the attributes), groupBy, orderBy, -, -- and { (x) } see the child elements in document order.
  • An output other than XML writes the elements as DataWeave does (dw.output).

XML output

  • Attributes key @(name: value): a null attribute is written as "null", and is left out with skipNullOn="attributes".
  • Namespace-qualified keys ns0#key and attributes, declared on the root element. An undeclared prefix fails the conversion.
  • null and an empty array are written as an empty element.
  • Arrays, object spreads and keys that repeat are written as elements that repeat, in the order of the script. For example { items: { (payload.items map { item: $.sku }) } } used to keep only the last item.
  • An element of the input is written with its children, attributes and namespaces. Placed under another key, it gets the attributes of that key, as in DataWeave.
  • Writer properties writeDeclaration and skipNullOn (elements, attributes, everywhere) are supported. indent, encoding and inlineCloseOn are formatting only.

Performance fix in the runtime library

DataSonnet 3.0.1.3 does not memoize lazy values: Val.Lazy.force() uses a local lazy val, so every use of a function argument, and every access to an array element, evaluates it again. Nested calls of library functions therefore took exponential time in the depth of the nesting. A 4-level SOAP selection did not finish within 20 s. With 300 items, map, filter and orderBy took more than 120 s with the library on main; it now takes 0.6 s. Every library function that uses an argument more than once now evaluates it once (strict, through the initial value of std.foldl, which DataSonnet does evaluate only once). orderBy computes each key once.

Tests

  • The corpus has 34 new XML entries. H02, H07, O12 and XM04 now have real expectations; before, they were @unsupported or ?. All 46 XML entries match the DataWeave CLI (dw 2.12). DataWeaveCorpusTest compares XML outputs without formatting or namespace-declaration placement.
  • Converter and parser unit tests cover the new constructs.
  • camel-dataweave: 101 tests pass. camel-datasonnet: 450 tests pass, including 365 corpus entries.

Review fixes

  • gnodet-bot: mixed &&/|| conditions parenthesized, and the upgrade guide note rewrapped.
  • oscerd: text next to CDATA read twice, elements with the same local name in other namespaces missed by .* and ..*, an empty array lost in an object with a spread, and nested lambdas with the same parameter name. Each one has a corpus entry (XM33 to XM37).

Known differences (documented)

A DataSonnet object cannot repeat a key. So in a JSON or Java output, elements that repeat under one name, and mixed-content __text, become an array, where DataWeave writes the key repeatedly. The XML output does write repeated elements. ..@ is still not converted: the conversion fails.

Claude Code on behalf of davsclaus

🤖 Generated with Claude Code

… the DataSonnet XML model

The DataWeave to DataSonnet conversion now follows the DataWeave XML semantics.

XML input: a selector matches an element by its local name whatever its namespace
prefix (such as payload.Envelope.Body of a SOAP message), .ns#name only in the namespace
of an ns header directive, .@ gives all the attributes and ..* all the descendants, an
empty element is null, and the object functions see the child elements in document
order. An output other than XML writes the elements without the attributes, namespace
declarations and positions of the DataSonnet XML model (dw.output).

XML output: attributes (key @(name: value)), namespace-qualified keys (ns0#key),
null as an empty element, arrays, object spreads and keys that repeat as elements that
repeat (in the order of the script), elements of the input with their attributes and
namespaces, and the writeDeclaration and skipNullOn writer properties.

DataSonnet does not keep the value of a function argument (each use evaluates it
again), so nested calls of the camel-dataweave.libsonnet functions took exponential time
in the depth of the nesting. The functions now evaluate their arguments once.

The corpus has 29 new XML entries, verified with the DataWeave CLI, and compares XML
outputs without formatting.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@gnodet-bot gnodet-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.

Two minor style findings on this DataWeave conversion PR.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

Comment thread docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc Outdated
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • components/camel-datasonnet
  • components/camel-dataweave
  • docs

🔬 Scalpel shadow comparison — Scalpel: 10 of 704 tested, 25 compile-only — current: 10 all tested

Maveniverse Scalpel detected 10 affected modules (current approach: 10).

Skip-tests mode would test 10 modules (4 direct + 8 downstream), skip tests for 25 (generated code, meta-modules)

Modules Scalpel would test (10)
  • camel-datasonnet ← components/camel-datasonnet/src/main/docs/datasonnet-language.adoc, components/camel-datasonnet/src/main/resources/camel-dataweave.libsonnet, components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/DataWeaveCorpusTest.java, components/camel-datasonnet/src/test/resources/dataweave-corpus/corpus.txt
  • camel-dataweave ← components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveAst.java, components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveConverter.java, components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveLexer.java, components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveParser.java, components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveConverterTest.java, components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveParserTest.java
  • camel-jbang-mcp ← downstream of org.apache.camel:camel-catalog
  • camel-jbang-plugin-mcp ← downstream of org.apache.camel:camel-jbang-core
  • camel-jbang-plugin-route-parser ← downstream of org.apache.camel:camel-route-parser
  • camel-jbang-plugin-tui ← downstream of org.apache.camel:camel-catalog
  • camel-jbang-plugin-validate ← downstream of org.apache.camel:camel-yaml-dsl-validator
  • camel-launcher-container ← downstream of org.apache.camel:camel-launcher
  • camel-yaml-dsl-validator ← downstream of org.apache.camel:camel-catalog
  • camel-yaml-dsl-validator-maven-plugin ← downstream of org.apache.camel:camel-yaml-dsl-validator
Modules with tests skipped (25)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (37 modules, 7m 9s total)

Total reactor time: 7m 9s

Module Duration Status
Camel :: Launcher 52.3s SUCCESS
Camel :: JBang :: Plugin :: TUI 50.8s SUCCESS
Camel :: DataSonnet 46.6s SUCCESS
Camel :: JBang :: MCP 43.0s SUCCESS
Camel :: Component DSL 36.2s SUCCESS
Camel :: Catalog :: Camel Catalog 24.8s SUCCESS
Camel :: YAML DSL 21.6s SUCCESS
Camel :: YAML DSL :: Validator 21.3s SUCCESS
Camel :: JBang :: Plugin :: Validate 18.6s SUCCESS
Camel :: Docs 15.9s SUCCESS
Camel :: JBang :: Plugin :: Kubernetes 13.3s SUCCESS
Camel :: JBang :: Plugin :: Testing 12.9s SUCCESS
Camel :: Kamelet Main 10.1s SUCCESS
Camel :: YAML DSL :: Deserializers 9.3s SUCCESS
Camel :: Catalog :: Camel Route Parser 8.0s SUCCESS
Camel :: Catalog :: Camel Report Maven Plugin 6.9s SUCCESS
Camel :: All Components Sync point 5.2s SUCCESS
Camel :: DataWeave 4.9s SUCCESS
Camel :: YAML DSL :: Validator Maven Plugin 4.8s SUCCESS
Camel :: Catalog :: Maven 3.4s SUCCESS
Camel :: YAML DSL :: Maven Plugins 3.3s SUCCESS
Camel :: Catalog :: Suggest (deprecated) 2.3s SUCCESS
Camel :: Coverage 1.8s SUCCESS
Camel :: JBang :: Plugin :: Edit 1.7s SUCCESS
Camel :: Catalog :: Dummy Component 1.7s SUCCESS
Camel :: Assembly 1.7s SUCCESS
Camel :: JBang :: Plugin :: MCP 1.3s SUCCESS
Camel :: JBang :: Plugin :: Generate 1.2s SUCCESS
Camel :: JBang :: Integration tests 0.9s SUCCESS
Camel :: JBang :: Main 0.9s SUCCESS
Camel :: Catalog :: Console 0.8s SUCCESS
Camel :: Endpoint DSL :: Support 0.8s SUCCESS
Camel :: Launcher :: Container 0.7s SUCCESS
Camel :: JBang :: Plugin :: Route Parser 0.5s SUCCESS
Camel :: Endpoint DSL n/a
Camel :: Integration Tests n/a
Camel :: JBang :: Core n/a

Top 20 slowest modules:

  • Camel :: Launcher (52.3s)
  • Camel :: JBang :: Plugin :: TUI (50.8s)
  • Camel :: DataSonnet (46.6s)
  • Camel :: JBang :: MCP (43.0s)
  • Camel :: Component DSL (36.2s)
  • Camel :: Catalog :: Camel Catalog (24.8s)
  • Camel :: YAML DSL (21.6s)
  • Camel :: YAML DSL :: Validator (21.3s)
  • Camel :: JBang :: Plugin :: Validate (18.6s)
  • Camel :: Docs (15.9s)
  • Camel :: JBang :: Plugin :: Kubernetes (13.3s)
  • Camel :: JBang :: Plugin :: Testing (12.9s)
  • Camel :: Kamelet Main (10.1s)
  • Camel :: YAML DSL :: Deserializers (9.3s)
  • Camel :: Catalog :: Camel Route Parser (8.0s)
  • Camel :: Catalog :: Camel Report Maven Plugin (6.9s)
  • Camel :: All Components Sync point (5.2s)
  • Camel :: DataWeave (4.9s)
  • Camel :: YAML DSL :: Validator Maven Plugin (4.8s)
  • Camel :: Catalog :: Maven (3.4s)

⚙️ View full build and test results

…e guide note

Review feedback.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>

@gnodet-bot gnodet-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.

Both prior findings are addressed in the new commits. The XML namespace/attribute support looks solid: strict() memoization is correct (forces evaluation once via std.foldl init), namespace scoping via inScope propagates declarations through selector chains correctly, and the 29 new XML corpus entries provide thorough coverage (SOAP envelopes, namespace-qualified selectors, XML-to-XML passthrough, repeated elements, skipNullOn variants, writeDeclaration). No issues found.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

oscerd
oscerd previously requested changes Oct 9, 2026

@oscerd oscerd 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.

Strong PR and a legitimate follow-on to the 4.23 fail-fast work (CAMEL-25323 / 25324 / 25471). CI is green on head 6a72ee49, the corpus additions are substantial, and the tests would not even compile against main — so this is well pinned.

Requesting changes for one narrow but real silent-wrong-result bug. It is a one-line fix plus a corpus entry. Everything else below is non-blocking.

I did not rely on the diff alone for these: I extracted datasonnet-mapper-3.0.1.3, ran this PR's own camel-dataweave.libsonnet (fetched at the head SHA) against sample XML through com.datasonnet.Mapper, and compiled the 5-file camel-dataweave converter package standalone to inspect generated scripts. The findings are reproduced, not inferred.

Blocking — CDATA adjacent to plain text silently duplicates the text

camel-dataweave.libsonnet:52-53 (textOf):

local textOf(x) = strict(std.join('', [x[k] for k in std.objectFields(x) if std.startsWith(k, '$')]), ...

The DataSonnet reader emits both a collated $ and numbered $1/$3 segments when an element has several text segments and no child elements. Verified raw model:

<root><b>x<![CDATA[y]]>z</b></root>
  -> {"$":"xyz","$1":"x","#2":"y","$3":"z","~":1}

textOf joins all three $-prefixed keys:

expression actual expected
payload.root.b "xyzxz" "xyz"
dw.fromXml(payload.root) {"b":"xyzxz"} {"b":"xyz"}
dw.entries(...b) 3 __text entries 2

The entries fallout propagates to keysOf, sizeOf, pluck, entriesOf, mapObject, groupBy, orderBy. Comments and entity references do not trigger it (verified: <b>x<!--c-->y</b> → "xy", a&amp;b → "a&b") — only CDATA mixed with text, which is common in SOAP payloads.

This is exactly the failure class the PR sets out to eliminate ("converted without error but gave wrong results"), which is why I'd rather it not ship. Prefer the collated key when present:

local textOf(x) = strict(if std.objectHas(x, '$') then x['$']
                         else std.join('', [x[k] for k in std.objectFields(x) if std.startsWith(k, '$')]),
                         function(t) if t == '' then null else t),

and in entries (:87-89) drop the collated $ when numbered segments exist.

Corpus XM15 covers only pure CDATA (<a><![CDATA[x < y]]></a>), which passes — hence the miss. Worth adding <a>x<![CDATA[y]]>z</a>.

Important (non-blocking)

Two namespaces, same local name: .*name and ..*name return only the first prefix. childKey (:60), used by multiRaw:155 and descendants:163, returns the first field whose local name matches:

<root xmlns:p="urn:p" xmlns:q="urn:q"><p:item>1</p:item><q:item>2</q:item></root>
  dw.multi(root,'item')      -> ["1"]          // drops q:item
  dw.descAll(payload,'item') -> ["1"]          // drops q:item
  dw.entries(root)           -> [item, item]   // but this path sees both

This contradicts the PR's own doc ("a selector matches an element by its local name whatever its namespace prefix", ".*field all of them") and is internally inconsistent with the entries path. A childKeys(x,k) returning every matching field, flattened, would fix it. No corpus entry covers two prefixes sharing a local name.

An empty array disappears once the object goes through xmlObject. fieldsOf flattens [] to zero entries, while ordered's own comment says "an empty array is written as an empty element":

dw.xmlOutput({root: {a: [], b: 1}}, {})                   -> <root><a></a><b>1</b></root>   correct
dw.xmlOutput({root: dw.xmlObject([{a: []},{b: 1}])}, {})  -> <root><b>1</b></root>          key lost

xmlObject engages as soon as the object also has a spread or a repeated key. XM32 covers ea: [] on the plain path and XM27 has a spread but only non-empty arrays, so the combination is untested. Mapping an empty array to a single {k: f.k, v: null} entry would do it.

Nested lambda params with the same name clear the rawNames flag early. In DataWeaveConverter.java, rawNames is a flat Set<String>; emitLambda/emitImplicitLambda add then remove without saving prior membership, and jsonnetName doesn't uniquify across nesting. Reproduced with the standalone-compiled converter:

payload.*order map ((o) -> { id: o.@id, v: o })
  -> function(o, _1) { id: dw.attr(o, "id"), v: dw.text(o) }      correct

payload.*order map ((o) -> { items: (o.*item map ((o) -> o.@sku)), v: o })
  -> function(o, _1) { items: (...), v: o }                       dw.text(o) lost

The bare v: o then leaks the raw DataSonnet element object (with ~, @…) into JSON. Obscure — needs a shadowing same-named inner lambda that also reads attributes — but silently wrong. Save/restore prior membership, or use a counting multiset.

Suggestions

  • Namespace prefixes with - or . fail with a misleading error. DataWeaveLexer NAMESPACE_DIRECTIVE uses [A-Za-z_][A-Za-z0-9_]*, narrower than XML NCName. Verified: ns my-ns http://example.com/x → expected STRING but found MINUS ('-'). Fail-fast so no wrong output, but the message doesn't mention namespace prefixes. Widening to [A-Za-z_][A-Za-z0-9_.-]* is cheap.
  • Untested: default (unprefixed) namespace on input. selNsRaw:144 maps a colon-less field to prefix '$', which I verified is correct — but no corpus entry has xmlns="…", so the branch is unexercised.
  • [Nit] keyName uses prefix#name while staticName uses prefix:name. Harmless today, but a literal key "a#b" would collide with QName(a,b) in hasRepeatedKey.
  • [Nit] DescendantSelector's 2-arg compat constructor is package-private while ObjectEntry's is public — worth making consistent.

Security: no script-injection issue (checked specifically)

The only input-derived values spliced into generated DataSonnet source are the namespace prefix and URI from the script's own ns header — i.e. the route's script, trusted under Camel's threat model. They also pass through DataWeaveConverter.string() (escapes ", \, control chars) or isJsonnetIdentifier(). Untrusted message XML — element names, namespace URIs, attribute names — never reaches generated source; it is handled at runtime by the libsonnet against the already-parsed object model, with no eval or parseJson of message data. Clean.

Also worth noting the new canonicalXml test comparator sets disallow-doctype-decl=true — good XXE hygiene in a helper that could easily have skipped it.

Test strength: would they fail without the change? Decisively yes

DataWeaveParserTest asserts AST types that don't exist on main (AllAttributes, QName, QualifiedFieldAccess, DescendantSelector.multi()) — it wouldn't compile. DataWeaveConverterTest asserts exact generated text for functions that don't exist before. The corpus harness asserts assertThrows(DataWeaveConversionException…) for @unsupported entries, so the 29 new XML entries would throw on main. The coverage gaps map exactly onto findings 1, 2, 3 and the default-namespace note.

Checklist: docs ✅ · catalog mirror ✅ (blob hashes identical on both sides) · upgrade guide ✅ correctly amends the existing "DataWeave auto-conversion now fails fast" 4.23 entry rather than adding a feature note · commit convention ✅ · public-API compat ✅ all churn is within unreleased 4.23 · no FQCNs ✅ (scripted scan of added Java lines: 0) · no Thread.sleep ✅ · assertion style ✅ · CI green on head SHA ✅ · both gnodet-bot threads resolved ✅

Two process notes: the PR has no human approval yet — only COMMENTED from gnodet-bot (AI) and from you as author — and CAMEL-25481 has empty fixVersions, which must be set to 4.23.0 before resolving, since it can't be set once closed.

Could not verify: that DataWeave 2.12 itself returns both elements for .*item across two prefixes — I didn't run the dw CLI. The finding stands on its own, since it contradicts this PR's documented rule and its own entries path. Also could not get a clean strict() memoisation timing (my harness timed out at depth 15, consistent with the exponential-blowup claim).


Reviewed with Claude Code (Claude Opus 5) on behalf of @oscerd. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying. It is a rules-and-conventions review and does not replace CodeRabbit, Sourcery, or SonarCloud.

…h the same local name, empty arrays and nested lambdas

Review fixes:

- The text of an element with text and CDATA segments was read twice (the reader also
  gives it in '$'); adjacent text and CDATA in mixed content are one __text.
- .*name and ..*name give the elements of every namespace with that local name, in
  document order (and .name the first in document order).
- An empty array is an empty element also in an object with a spread or a repeated key.
- A lambda parameter kept as an XML element for its attributes is tracked in its scope,
  so an inner lambda with a parameter of the same name does not change it.

Five corpus entries for these, verified with the DataWeave CLI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
@davsclaus

Copy link
Copy Markdown
Contributor Author

Thanks @oscerd. All of these reproduced, and I checked each against the DataWeave CLI (dw 2.12) before fixing. They're fixed in cc2a6b2, with a corpus entry for each, all verified with dw:

  • CDATA next to text (blocking): textOf now uses the text the reader collates in $. In mixed content, adjacent text and CDATA segments are one __text, as in DataWeave (<c>t<![CDATA[u]]><d/>v</c> gives __text "tu", d, __text "v"). Corpus XM34.
  • Two namespaces, same local name: .*item and ..*item now give the elements of every prefix, in document order. dw gives ["1","2","3"]. .item and ..item give the first in document order. Corpus XM33.
  • Empty array through xmlObject: it is now an empty element (<a/>), as dw writes it. Corpus XM36.
  • Nested lambdas with the same parameter name: whether a parameter is kept as an XML element is now part of its binding in the scope, not a flat name set. So an inner lambda can't change the outer one. Converter test, plus corpus XM37 with XML output, where the outer v: o matters.
  • Default namespace: corpus XM35 (xmlns="…" with ns-qualified selectors, also in the wrong namespace).
  • Prefixes with -: DataWeave rejects ns my-ns http://… too (Invalid input 'h', expected directive), so failing the conversion is consistent. I left the pattern as it is.
  • Nits: keyName now uses prefix:name, the name as written in XML, so a quoted "a:b" and a#b count as the same element. DescendantSelector's 2-arg constructor is public.
  • Process: CAMEL-25481 now has fixVersion 4.23.0.

camel-dataweave: 101 tests pass. camel-datasonnet: 450 tests pass, including 365 corpus entries.

Claude Code on behalf of davsclaus

@davsclaus
davsclaus requested a review from oscerd October 9, 2026 16:36
@davsclaus davsclaus added this to the 4.23.0 milestone Oct 9, 2026
@davsclaus
davsclaus merged commit 3b3e0ae into main Oct 9, 2026
6 checks passed
@davsclaus
davsclaus deleted the fix/CAMEL-25481 branch October 9, 2026 19:52
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.

3 participants