This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch feature/CAMEL-23530-dataweave-component in repository https://gitbox.apache.org/repos/asf/camel.git
commit 908a6dd8cfe46b02fe10d363abb2f85e5d384911 Author: Claus Ibsen <[email protected]> AuthorDate: Wed Aug 5 16:19:10 2026 +0200 CAMEL-23530: Move DataWeave parser to camel-dataweave component for reuse Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- bom/camel-bom/pom.xml | 5 ++ catalog/camel-allcomponents/pom.xml | 5 ++ .../org/apache/camel/catalog/others.properties | 1 + .../org/apache/camel/catalog/others/dataweave.json | 15 ++++ components/camel-dataweave/pom.xml | 54 +++++++++++++++ .../services/org/apache/camel/other.properties | 7 ++ .../src/generated/resources/dataweave.json | 15 ++++ .../camel/component/dataweave}/DataWeaveAst.java | 2 +- .../component/dataweave}/DataWeaveConverter.java | 20 +++--- .../camel/component/dataweave}/DataWeaveLexer.java | 2 +- .../component/dataweave}/DataWeaveParser.java | 16 ++--- .../dataweave}/DataWeaveConverterTest.java | 45 ++++++------ .../component/dataweave}/DataWeaveLexerTest.java | 21 +----- .../component/dataweave}/DataWeaveParserTest.java | 80 +++++++++------------- .../test/resources/dataweave/collection-map.dwl | 0 .../src/test/resources/dataweave/event-message.dwl | 0 .../src/test/resources/dataweave/null-handling.dwl | 0 .../src/test/resources/dataweave/simple-rename.dwl | 0 .../src/test/resources/dataweave/string-ops.dwl | 0 .../src/test/resources/dataweave/type-coercion.dwl | 0 components/pom.xml | 1 + .../modules/others/examples/json/dataweave.json | 1 + dsl/camel-jbang/camel-jbang-core/pom.xml | 4 ++ .../jbang/core/commands/TransformDataWeave.java | 2 +- parent/pom.xml | 5 ++ 25 files changed, 190 insertions(+), 111 deletions(-) diff --git a/bom/camel-bom/pom.xml b/bom/camel-bom/pom.xml index ea5383026c76..f6f06b4f00b6 100644 --- a/bom/camel-bom/pom.xml +++ b/bom/camel-bom/pom.xml @@ -707,6 +707,11 @@ <artifactId>camel-datasonnet</artifactId> <version>4.22.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-dataweave</artifactId> + <version>4.22.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-debezium-common</artifactId> diff --git a/catalog/camel-allcomponents/pom.xml b/catalog/camel-allcomponents/pom.xml index 9ff95f7750ff..0afdc8f168ad 100644 --- a/catalog/camel-allcomponents/pom.xml +++ b/catalog/camel-allcomponents/pom.xml @@ -597,6 +597,11 @@ <artifactId>camel-datasonnet</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-dataweave</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-debezium-common</artifactId> diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties index e1c80fb21928..20197d007d61 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others.properties @@ -7,6 +7,7 @@ cluster componentdsl console csimple-joor +dataweave debug diagram dsl-modeline diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/dataweave.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/dataweave.json new file mode 100644 index 000000000000..c41beea0ddf9 --- /dev/null +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/others/dataweave.json @@ -0,0 +1,15 @@ +{ + "other": { + "kind": "other", + "name": "dataweave", + "title": "Dataweave", + "description": "Camel DataWeave support", + "deprecated": false, + "firstVersion": "4.22.0", + "label": "transformation", + "supportLevel": "Preview", + "groupId": "org.apache.camel", + "artifactId": "camel-dataweave", + "version": "4.22.0-SNAPSHOT" + } +} diff --git a/components/camel-dataweave/pom.xml b/components/camel-dataweave/pom.xml new file mode 100644 index 000000000000..492acf2f43ce --- /dev/null +++ b/components/camel-dataweave/pom.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.camel</groupId> + <artifactId>components</artifactId> + <version>4.22.0-SNAPSHOT</version> + </parent> + + <artifactId>camel-dataweave</artifactId> + <packaging>jar</packaging> + + <name>Camel :: DataWeave</name> + <description>Camel DataWeave support</description> + + <properties> + <firstVersion>4.22.0</firstVersion> + <label>transformation</label> + </properties> + + <dependencies> + <!-- test dependencies --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project> diff --git a/components/camel-dataweave/src/generated/resources/META-INF/services/org/apache/camel/other.properties b/components/camel-dataweave/src/generated/resources/META-INF/services/org/apache/camel/other.properties new file mode 100644 index 000000000000..40bc48ca1d26 --- /dev/null +++ b/components/camel-dataweave/src/generated/resources/META-INF/services/org/apache/camel/other.properties @@ -0,0 +1,7 @@ +# Generated by camel build tools - do NOT edit this file! +name=dataweave +groupId=org.apache.camel +artifactId=camel-dataweave +version=4.22.0-SNAPSHOT +projectName=Camel :: DataWeave +projectDescription=Camel DataWeave support diff --git a/components/camel-dataweave/src/generated/resources/dataweave.json b/components/camel-dataweave/src/generated/resources/dataweave.json new file mode 100644 index 000000000000..c41beea0ddf9 --- /dev/null +++ b/components/camel-dataweave/src/generated/resources/dataweave.json @@ -0,0 +1,15 @@ +{ + "other": { + "kind": "other", + "name": "dataweave", + "title": "Dataweave", + "description": "Camel DataWeave support", + "deprecated": false, + "firstVersion": "4.22.0", + "label": "transformation", + "supportLevel": "Preview", + "groupId": "org.apache.camel", + "artifactId": "camel-dataweave", + "version": "4.22.0-SNAPSHOT" + } +} diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveAst.java b/components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveAst.java similarity index 98% rename from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveAst.java rename to components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveAst.java index 4b75a0569aff..f1ef34b9b40b 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveAst.java +++ b/components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveAst.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.dsl.jbang.core.commands.transform; +package org.apache.camel.component.dataweave; import java.util.List; diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveConverter.java b/components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveConverter.java similarity index 96% rename from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveConverter.java rename to components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveConverter.java index 3842f29b2c5e..6bc12820c8ea 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveConverter.java +++ b/components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveConverter.java @@ -14,13 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.dsl.jbang.core.commands.transform; +package org.apache.camel.component.dataweave; import java.util.ArrayList; import java.util.List; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.*; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveLexer.Token; +import org.apache.camel.component.dataweave.DataWeaveAst.*; +import org.apache.camel.component.dataweave.DataWeaveLexer.Token; /** * Converts DataWeave 2.0 scripts to DataSonnet. Parses the DataWeave input, walks the AST, and emits equivalent @@ -84,7 +84,7 @@ public class DataWeaveConverter { return emitNode(ast); } - // ── Emission ── + // -- Emission -- private String emit(DataWeaveAst node) { if (node instanceof Script script) { @@ -352,7 +352,7 @@ public class DataWeaveConverter { case "Boolean" -> "cml.toBoolean(" + expr + ")"; default -> { todoCount++; - yield expr + (includeComments ? " // TODO: manual conversion needed — as " + tc.type() : ""); + yield expr + (includeComments ? " // TODO: manual conversion needed -- as " + tc.type() : ""); } }; } @@ -410,9 +410,9 @@ public class DataWeaveConverter { yield "c.max(" + argStr + ")"; } case "read" -> "std.parseJson(" + argStr + ")" - + (includeComments ? " // NOTE: assumes JSON input — DW read() supports multiple formats" : ""); + + (includeComments ? " // NOTE: assumes JSON input -- DW read() supports multiple formats" : ""); case "write" -> "std.manifestJsonEx(" + argStr + ", \" \")" - + (includeComments ? " // NOTE: outputs JSON — DW write() supports multiple formats" : ""); + + (includeComments ? " // NOTE: outputs JSON -- DW write() supports multiple formats" : ""); default -> fc.name() + "(" + argStr + ")"; }; } @@ -436,7 +436,7 @@ public class DataWeaveConverter { List<String> paramNames = lambdaParamNames(lam); String body = emitNode(lam.body()); if (paramNames.size() == 2) { - // DW: map ((item, index) -> body) — DS: std.mapWithIndex(function(index, item) body, collection) + // DW: map ((item, index) -> body) -- DS: std.mapWithIndex(function(index, item) body, collection) // Parameter order is swapped: DW is (item, index), DS is (index, item) return "std.mapWithIndex(function(" + paramNames.get(1) + ", " + paramNames.get(0) + ") " + body + ", " + collection + ")"; @@ -498,7 +498,7 @@ public class DataWeaveConverter { if (dbe.lambda() instanceof Lambda lam) { List<String> paramNames = lambdaParamNames(lam); String body = emitNode(lam.body()); - // distinctBy keeps first occurrence per key — use distinctBy helper + // distinctBy keeps first occurrence per key -- use distinctBy helper return "c.distinctBy(" + collection + ", function(" + paramNames.get(0) + ") " + body + ")"; } return "c.distinct(" + collection + ")"; @@ -593,7 +593,7 @@ public class DataWeaveConverter { todoCount++; convertedCount--; return includeComments - ? "// TODO: manual conversion needed — " + u.reason() + ": " + u.originalText() + "\nnull" + ? "// TODO: manual conversion needed -- " + u.reason() + ": " + u.originalText() + "\nnull" : "null"; } diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveLexer.java b/components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveLexer.java similarity index 99% rename from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveLexer.java rename to components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveLexer.java index 1d74d45d7382..703424acaeb6 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveLexer.java +++ b/components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveLexer.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.dsl.jbang.core.commands.transform; +package org.apache.camel.component.dataweave; import java.util.ArrayList; import java.util.List; diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveParser.java b/components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveParser.java similarity index 98% rename from dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveParser.java rename to components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveParser.java index 8c2f62c29dfb..e91187d23f17 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveParser.java +++ b/components/camel-dataweave/src/main/java/org/apache/camel/component/dataweave/DataWeaveParser.java @@ -14,13 +14,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.dsl.jbang.core.commands.transform; +package org.apache.camel.component.dataweave; import java.util.ArrayList; import java.util.List; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveLexer.Token; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveLexer.TokenType; +import org.apache.camel.component.dataweave.DataWeaveLexer.Token; +import org.apache.camel.component.dataweave.DataWeaveLexer.TokenType; /** * Recursive descent parser for DataWeave 2.0 scripts producing {@link DataWeaveAst} nodes. @@ -45,7 +45,7 @@ public class DataWeaveParser { return parseExpression(); } - // ── Header parsing ── + // -- Header parsing -- private DataWeaveAst.Header parseHeader() { String version = "2.0"; @@ -57,7 +57,7 @@ public class DataWeaveParser { || checkIdentifier("output") || checkIdentifier("input"); if (!hasHeader) { - // No header section — skip directly to body + // No header section -- skip directly to body return new DataWeaveAst.Header(version, null, inputs); } @@ -118,7 +118,7 @@ public class DataWeaveParser { return sb.toString(); } - // ── Expression parsing (precedence climbing) ── + // -- Expression parsing (precedence climbing) -- private DataWeaveAst parseExpression() { // Handle var/fun declarations at expression level @@ -442,7 +442,7 @@ public class DataWeaveParser { expr = new DataWeaveAst.ReplaceExpr(expr, target, replacement); } else if (checkIdentifier("match")) { advance(); // match - // Capture the match block as unsupported — skip braces + // Capture the match block as unsupported -- skip braces StringBuilder matchText = new StringBuilder("match "); if (check(TokenType.LBRACE)) { int depth = 1; @@ -717,7 +717,7 @@ public class DataWeaveParser { return new DataWeaveAst.ArrayLit(elements); } - // ── Token helpers ── + // -- Token helpers -- private Token current() { return pos < tokens.size() ? tokens.get(pos) : tokens.get(tokens.size() - 1); diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveConverterTest.java b/components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveConverterTest.java similarity index 93% rename from dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveConverterTest.java rename to components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveConverterTest.java index c2424bd1e66a..e435af0e483f 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveConverterTest.java +++ b/components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveConverterTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.dsl.jbang.core.commands.transform; +package org.apache.camel.component.dataweave; import java.io.IOException; import java.io.InputStream; @@ -34,7 +34,7 @@ class DataWeaveConverterTest { converter = new DataWeaveConverter(); } - // ── Header conversion ── + // -- Header conversion -- @Test void testHeaderConversion() { @@ -51,7 +51,7 @@ class DataWeaveConverterTest { assertTrue(result.contains("*/")); } - // ── Field access ── + // -- Field access -- @Test void testPayloadToBody() { @@ -83,7 +83,7 @@ class DataWeaveConverterTest { assertEquals("cml.header('page')", result); } - // ── Operators ── + // -- Operators -- @Test void testStringConcat() { @@ -109,7 +109,7 @@ class DataWeaveConverterTest { assertEquals("body.active && body.verified", result); } - // ── Default operator ── + // -- Default operator -- @Test void testDefault() { @@ -117,7 +117,7 @@ class DataWeaveConverterTest { assertEquals("cml.defaultVal(body.currency, \"USD\")", result); } - // ── Type coercion ── + // -- Type coercion -- @Test void testAsNumber() { @@ -143,7 +143,7 @@ class DataWeaveConverterTest { assertEquals("cml.toBoolean(body.active)", result); } - // ── Built-in functions ── + // -- Built-in functions -- @Test void testSizeOf() { @@ -194,7 +194,7 @@ class DataWeaveConverterTest { assertTrue(converter.needsCamelLib()); } - // ── String operations ── + // -- String operations -- @Test void testContains() { @@ -221,7 +221,7 @@ class DataWeaveConverterTest { assertEquals("std.strReplace(body.text, \"old\", \"new\")", result); } - // ── Collection operations ── + // -- Collection operations -- @Test void testMap() { @@ -250,10 +250,8 @@ class DataWeaveConverterTest { @Test void testReduceParamSwap() { - // Verify that acc and item params are swapped for std.foldl String result = converter.convertExpression( "payload.items reduce ((item, acc = 0) -> acc + item.price)"); - // In std.foldl, it should be function(acc, item) not function(item, acc) assertTrue(result.contains("function(acc, item)")); } @@ -265,7 +263,7 @@ class DataWeaveConverterTest { assertTrue(result.contains("item.tags")); } - // ── If/else ── + // -- If/else -- @Test void testIfElse() { @@ -274,7 +272,7 @@ class DataWeaveConverterTest { assertEquals("if body.age >= 18 then \"adult\" else \"minor\"", result); } - // ── Object and array literals ── + // -- Object and array literals -- @Test void testObjectLiteral() { @@ -289,7 +287,7 @@ class DataWeaveConverterTest { assertEquals("[1, 2, 3]", result); } - // ── Full script tests ── + // -- Full script tests -- @Test void testSimpleRenameScript() throws IOException { @@ -382,7 +380,7 @@ class DataWeaveConverterTest { assertTrue(result.contains("name: body.name")); } - // ── startsWith / endsWith ── + // -- startsWith / endsWith -- @Test void testStartsWith() { @@ -398,7 +396,7 @@ class DataWeaveConverterTest { assertTrue(converter.needsCamelLib()); } - // ── Math functions ── + // -- Math functions -- @Test void testAbs() { @@ -427,17 +425,16 @@ class DataWeaveConverterTest { assertTrue(converter.needsCamelLib()); } - // ── mapWithIndex parameter order ── + // -- mapWithIndex parameter order -- @Test void testMapWithIndex() { String result = converter.convertExpression( "payload.items map ((item, idx) -> { index: idx, name: item.name })"); - // DataSonnet std.mapWithIndex uses function(index, item), so params must be swapped assertTrue(result.contains("std.mapWithIndex(function(idx, item)")); } - // ── distinctBy ── + // -- distinctBy -- @Test void testDistinctBy() { @@ -448,7 +445,7 @@ class DataWeaveConverterTest { assertTrue(converter.needsCamelLib()); } - // ── Lambda shorthand ── + // -- Lambda shorthand -- @Test void testLambdaShorthand() { @@ -456,7 +453,7 @@ class DataWeaveConverterTest { assertTrue(result.contains("function(x) x.name")); } - // ── match expression ── + // -- match expression -- @Test void testMatchExpressionUnsupported() { @@ -466,7 +463,7 @@ class DataWeaveConverterTest { assertTrue(converter.getTodoCount() > 0); } - // ── Multi-value selector ── + // -- Multi-value selector -- @Test void testMultiValueSelector() { @@ -475,7 +472,7 @@ class DataWeaveConverterTest { assertEquals(0, converter.getTodoCount()); } - // ── Escape handling ── + // -- Escape handling -- @Test void testStringEscapesPreserved() { @@ -483,7 +480,7 @@ class DataWeaveConverterTest { assertTrue(result.contains("\"\\n\""), "Newline escape should be preserved, got: " + result); } - // ── Helpers ── + // -- Helpers -- private String loadResource(String path) throws IOException { try (InputStream is = getClass().getClassLoader().getResourceAsStream(path)) { diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveLexerTest.java b/components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveLexerTest.java similarity index 79% rename from dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveLexerTest.java rename to components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveLexerTest.java index 9395ac07424c..76a66320039a 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveLexerTest.java +++ b/components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveLexerTest.java @@ -14,23 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.dsl.jbang.core.commands.transform; +package org.apache.camel.component.dataweave; import java.util.List; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveLexer.Token; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveLexer.TokenType; +import org.apache.camel.component.dataweave.DataWeaveLexer.Token; +import org.apache.camel.component.dataweave.DataWeaveLexer.TokenType; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; -/** - * Unit tests for {@link DataWeaveLexer}. - * - * These assert the concrete token stream (types, values and positions) for representative inputs, focusing on the - * tricky, ambiguous cases where a tokenizer typically goes wrong: multi-character operators, negative numbers vs the - * minus operator, the {@code ---} header separator, and comment skipping. - */ class DataWeaveLexerTest { private static List<Token> tokenize(String input) { @@ -50,7 +43,6 @@ class DataWeaveLexerTest { @Test void shouldClassifyKeywordsAndIdentifiers() { - // true/false -> BOOLEAN, null -> NULL_LIT, and/or/not -> logical ops, everything else -> IDENTIFIER assertEquals( List.of(TokenType.BOOLEAN, TokenType.BOOLEAN, TokenType.NULL_LIT, TokenType.AND, TokenType.OR, TokenType.NOT, @@ -65,7 +57,6 @@ class DataWeaveLexerTest { assertEquals("hello", tokens.get(0).value()); assertEquals(TokenType.STRING, tokens.get(1).type()); assertEquals("world", tokens.get(1).value()); - // the escaped quote is preserved verbatim inside the string value assertEquals("a\\\"b", tokens.get(2).value()); } @@ -78,7 +69,6 @@ class DataWeaveLexerTest { @Test void shouldTreatLeadingMinusAsNegativeNumberWhenNotAfterValue() { - // at the start of input the minus binds to the number List<Token> tokens = tokenize("-5"); assertEquals(TokenType.NUMBER, tokens.get(0).type()); assertEquals("-5", tokens.get(0).value()); @@ -86,7 +76,6 @@ class DataWeaveLexerTest { @Test void shouldTreatMinusBetweenValuesAsOperator() { - // "5 - 3": the minus follows a value, so it is the subtraction operator, not a sign assertEquals( List.of(TokenType.NUMBER, TokenType.MINUS, TokenType.NUMBER, TokenType.EOF), types("5 - 3")); @@ -94,7 +83,6 @@ class DataWeaveLexerTest { @Test void shouldTreatMinusAfterOpeningParenAsNegativeNumber() { - // "(-3)": previous char is '(', not value-like, so the minus binds to the number assertEquals( List.of(TokenType.LPAREN, TokenType.NUMBER, TokenType.RPAREN, TokenType.EOF), types("(-3)")); @@ -112,7 +100,6 @@ class DataWeaveLexerTest { @Test void shouldRecognizeHeaderSeparatorAtLineStart() { - // the --- on its own line is a header separator, while the negative number afterwards stays a number List<TokenType> ts = types("%dw 2.0\n---\n-5"); assertEquals( List.of(TokenType.PERCENT, TokenType.IDENTIFIER, TokenType.NUMBER, @@ -133,7 +120,6 @@ class DataWeaveLexerTest { @Test void shouldTrackLineAndColumnPositions() { - // "ab\n cd": first token at 1:1, second at line 2 after two spaces -> 2:3 List<Token> tokens = tokenize("ab\n cd"); assertEquals(1, tokens.get(0).line()); assertEquals(1, tokens.get(0).col()); @@ -145,7 +131,6 @@ class DataWeaveLexerTest { @Test void shouldSkipUnknownCharactersInsteadOfFailing() { - // '@' is not a recognized token; the lexer drops it and keeps going assertEquals( List.of(TokenType.IDENTIFIER, TokenType.IDENTIFIER, TokenType.EOF), types("a @ b")); diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveParserTest.java b/components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveParserTest.java similarity index 74% rename from dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveParserTest.java rename to components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveParserTest.java index 8802531c2932..dd8688ed8cb3 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/transform/DataWeaveParserTest.java +++ b/components/camel-dataweave/src/test/java/org/apache/camel/component/dataweave/DataWeaveParserTest.java @@ -14,31 +14,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.camel.dsl.jbang.core.commands.transform; +package org.apache.camel.component.dataweave; import java.util.List; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.ArrayLit; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.BinaryOp; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.DefaultExpr; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.FieldAccess; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.FunctionCall; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.Identifier; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.IfElse; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.IndexAccess; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.Lambda; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.LambdaShorthand; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.MapExpr; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.NumberLit; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.ObjectLit; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.Parens; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.Script; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.StringLit; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.TypeCoercion; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.UnaryOp; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.Unsupported; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveAst.VarDecl; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveLexer.Token; +import org.apache.camel.component.dataweave.DataWeaveAst.ArrayLit; +import org.apache.camel.component.dataweave.DataWeaveAst.BinaryOp; +import org.apache.camel.component.dataweave.DataWeaveAst.DefaultExpr; +import org.apache.camel.component.dataweave.DataWeaveAst.FieldAccess; +import org.apache.camel.component.dataweave.DataWeaveAst.FunctionCall; +import org.apache.camel.component.dataweave.DataWeaveAst.Identifier; +import org.apache.camel.component.dataweave.DataWeaveAst.IfElse; +import org.apache.camel.component.dataweave.DataWeaveAst.IndexAccess; +import org.apache.camel.component.dataweave.DataWeaveAst.Lambda; +import org.apache.camel.component.dataweave.DataWeaveAst.LambdaShorthand; +import org.apache.camel.component.dataweave.DataWeaveAst.MapExpr; +import org.apache.camel.component.dataweave.DataWeaveAst.NumberLit; +import org.apache.camel.component.dataweave.DataWeaveAst.ObjectLit; +import org.apache.camel.component.dataweave.DataWeaveAst.Parens; +import org.apache.camel.component.dataweave.DataWeaveAst.Script; +import org.apache.camel.component.dataweave.DataWeaveAst.StringLit; +import org.apache.camel.component.dataweave.DataWeaveAst.TypeCoercion; +import org.apache.camel.component.dataweave.DataWeaveAst.UnaryOp; +import org.apache.camel.component.dataweave.DataWeaveAst.Unsupported; +import org.apache.camel.component.dataweave.DataWeaveAst.VarDecl; +import org.apache.camel.component.dataweave.DataWeaveLexer.Token; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -47,14 +47,6 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -/** - * Unit tests for {@link DataWeaveParser}. - * - * The parser is recursive descent and best-effort (it never throws on malformed input). These tests assert the shape of - * the produced {@link DataWeaveAst} for representative scripts, with emphasis on operator precedence, postfix - * collection operations, header parsing, and graceful handling of unsupported constructs. The AST records are exercised - * transitively here rather than in a separate test, since they carry no logic of their own. - */ class DataWeaveParserTest { private static DataWeaveAst parseExpr(String src) { @@ -67,7 +59,7 @@ class DataWeaveParserTest { return new DataWeaveParser(tokens).parse(); } - // ── Header ── + // -- Header -- @Test void shouldParseHeaderVersionAndOutputType() { @@ -102,14 +94,13 @@ class DataWeaveParserTest { @Test void shouldDefaultVersionWhenNoHeaderPresent() { - // an expression with no %dw header still yields a Script with the default 2.0 version Script script = assertInstanceOf(Script.class, parseScript("payload")); assertEquals("2.0", script.header().version()); assertNull(script.header().outputType()); assertInstanceOf(Identifier.class, script.body()); } - // ── Literals and field access ── + // -- Literals and field access -- @Test void shouldParseLiterals() { @@ -121,7 +112,6 @@ class DataWeaveParserTest { @Test void shouldParseFieldAccessAndIndexAccessChain() { - // payload.items[0] -> IndexAccess(FieldAccess(Identifier(payload), items), NumberLit(0)) IndexAccess idx = assertInstanceOf(IndexAccess.class, parseExpr("payload.items[0]")); assertEquals("0", assertInstanceOf(NumberLit.class, idx.index()).value()); FieldAccess fa = assertInstanceOf(FieldAccess.class, idx.object()); @@ -129,11 +119,10 @@ class DataWeaveParserTest { assertEquals("payload", assertInstanceOf(Identifier.class, fa.object()).name()); } - // ── Precedence ── + // -- Precedence -- @Test void shouldGiveMultiplicationHigherPrecedenceThanAddition() { - // 1 + 2 * 3 -> BinaryOp(+, 1, BinaryOp(*, 2, 3)) BinaryOp add = assertInstanceOf(BinaryOp.class, parseExpr("1 + 2 * 3")); assertEquals("+", add.op()); assertEquals("1", assertInstanceOf(NumberLit.class, add.left()).value()); @@ -145,7 +134,6 @@ class DataWeaveParserTest { @Test void shouldGiveAndHigherPrecedenceThanOr() { - // a and b or c -> BinaryOp(or, BinaryOp(and, a, b), c) BinaryOp or = assertInstanceOf(BinaryOp.class, parseExpr("a and b or c")); assertEquals("or", or.op()); assertEquals("c", assertInstanceOf(Identifier.class, or.right()).name()); @@ -165,16 +153,14 @@ class DataWeaveParserTest { assertEquals("+", assertInstanceOf(BinaryOp.class, parens.expr()).op()); } - // ── Object / array literals ── + // -- Object / array literals -- @Test void shouldParseObjectLiteralWithStaticAndDynamicKeys() { ObjectLit obj = assertInstanceOf(ObjectLit.class, parseExpr("{ name: payload.x, (k): v }")); assertEquals(2, obj.entries().size()); - // static key assertEquals("name", assertInstanceOf(Identifier.class, obj.entries().get(0).key()).name()); assertFalse(obj.entries().get(0).dynamic()); - // dynamic key parsed from a parenthesized expression assertTrue(obj.entries().get(1).dynamic()); assertEquals("k", assertInstanceOf(Identifier.class, obj.entries().get(1).key()).name()); } @@ -186,7 +172,7 @@ class DataWeaveParserTest { assertEquals("2", assertInstanceOf(NumberLit.class, arr.elements().get(1)).value()); } - // ── Collection operations / lambdas ── + // -- Collection operations / lambdas -- @Test void shouldParseMapWithExplicitLambda() { @@ -205,7 +191,7 @@ class DataWeaveParserTest { assertEquals(List.of("name"), sh.fields()); } - // ── Function calls ── + // -- Function calls -- @Test void shouldParseBuiltinFunctionCall() { @@ -215,7 +201,7 @@ class DataWeaveParserTest { assertEquals("payload", assertInstanceOf(Identifier.class, call.args().get(0)).name()); } - // ── Type coercion ── + // -- Type coercion -- @Test void shouldParseTypeCoercionWithoutFormat() { @@ -231,7 +217,7 @@ class DataWeaveParserTest { assertEquals("yyyy-MM-dd", tc.format()); } - // ── Control flow ── + // -- Control flow -- @Test void shouldParseIfElse() { @@ -250,14 +236,13 @@ class DataWeaveParserTest { @Test void shouldParseVarDeclarationWithTrailingBody() { - // "var x = 1 x" -> VarDecl(x, NumberLit(1), body=Identifier(x)) VarDecl var = assertInstanceOf(VarDecl.class, parseExpr("var x = 1 x")); assertEquals("x", var.name()); assertEquals("1", assertInstanceOf(NumberLit.class, var.value()).value()); assertEquals("x", assertInstanceOf(Identifier.class, var.body()).name()); } - // ── Unary ── + // -- Unary -- @Test void shouldParseLogicalNot() { @@ -266,7 +251,7 @@ class DataWeaveParserTest { assertEquals("a", assertInstanceOf(Identifier.class, not.operand()).name()); } - // ── Graceful degradation ── + // -- Graceful degradation -- @Test void shouldRepresentMatchExpressionAsUnsupported() { @@ -278,7 +263,6 @@ class DataWeaveParserTest { @Test void shouldNotThrowOnUnbalancedInput() { - // expect() silently skips a missing RPAREN, so a best-effort AST is still produced List<Token> tokens = new DataWeaveLexer("(a + b").tokenize(); assertEquals(DataWeaveLexer.TokenType.EOF, tokens.get(tokens.size() - 1).type()); assertInstanceOf(Parens.class, parseExpr("(a + b")); diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/collection-map.dwl b/components/camel-dataweave/src/test/resources/dataweave/collection-map.dwl similarity index 100% rename from dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/collection-map.dwl rename to components/camel-dataweave/src/test/resources/dataweave/collection-map.dwl diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/event-message.dwl b/components/camel-dataweave/src/test/resources/dataweave/event-message.dwl similarity index 100% rename from dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/event-message.dwl rename to components/camel-dataweave/src/test/resources/dataweave/event-message.dwl diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/null-handling.dwl b/components/camel-dataweave/src/test/resources/dataweave/null-handling.dwl similarity index 100% rename from dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/null-handling.dwl rename to components/camel-dataweave/src/test/resources/dataweave/null-handling.dwl diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/simple-rename.dwl b/components/camel-dataweave/src/test/resources/dataweave/simple-rename.dwl similarity index 100% rename from dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/simple-rename.dwl rename to components/camel-dataweave/src/test/resources/dataweave/simple-rename.dwl diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/string-ops.dwl b/components/camel-dataweave/src/test/resources/dataweave/string-ops.dwl similarity index 100% rename from dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/string-ops.dwl rename to components/camel-dataweave/src/test/resources/dataweave/string-ops.dwl diff --git a/dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/type-coercion.dwl b/components/camel-dataweave/src/test/resources/dataweave/type-coercion.dwl similarity index 100% rename from dsl/camel-jbang/camel-jbang-core/src/test/resources/dataweave/type-coercion.dwl rename to components/camel-dataweave/src/test/resources/dataweave/type-coercion.dwl diff --git a/components/pom.xml b/components/pom.xml index e09d87cfe482..5c9fca05136b 100644 --- a/components/pom.xml +++ b/components/pom.xml @@ -120,6 +120,7 @@ <module>camel-cyberark-vault</module> <module>camel-dapr</module> <module>camel-datasonnet</module> + <module>camel-dataweave</module> <module>camel-debug</module> <module>camel-diagram</module> <module>camel-debezium</module> diff --git a/docs/components/modules/others/examples/json/dataweave.json b/docs/components/modules/others/examples/json/dataweave.json new file mode 120000 index 000000000000..d2175aae6006 --- /dev/null +++ b/docs/components/modules/others/examples/json/dataweave.json @@ -0,0 +1 @@ +../../../../../../components/camel-dataweave/src/generated/resources/dataweave.json \ No newline at end of file diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml b/dsl/camel-jbang/camel-jbang-core/pom.xml index d369b09415b0..d3066b4c5e6f 100644 --- a/dsl/camel-jbang/camel-jbang-core/pom.xml +++ b/dsl/camel-jbang/camel-jbang-core/pom.xml @@ -45,6 +45,10 @@ <dependencies> <!-- camel --> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-dataweave</artifactId> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-diagram</artifactId> diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformDataWeave.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformDataWeave.java index bc3ff95df84d..e08ed966680f 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformDataWeave.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/TransformDataWeave.java @@ -23,7 +23,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import org.apache.camel.dsl.jbang.core.commands.transform.DataWeaveConverter; +import org.apache.camel.component.dataweave.DataWeaveConverter; import picocli.CommandLine; import picocli.CommandLine.Command; diff --git a/parent/pom.xml b/parent/pom.xml index d10f812f6c4d..1030dea51ba4 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -1247,6 +1247,11 @@ <artifactId>camel-datasonnet</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-dataweave</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-debezium-common</artifactId>
