gnodet-bot commented on code in PR #27552:
URL: https://github.com/apache/camel/pull/27552#discussion_r4216677566


##########
components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetLanguage.java:
##########
@@ -131,11 +131,13 @@ private static String convertDataWeave(String expression) 
{
             result = converter.convertExpression(expression);
         }
         if (converter.getTodoCount() > 0) {
-            LOG.warn("DataWeave conversion has {} construct(s) that could not 
be auto-converted and were emitted as null",
-                    converter.getTodoCount());
-        } else {
-            LOG.debug("Converted DataWeave to DataSonnet: {} expression(s)", 
converter.getConvertedCount());
+            throw new IllegalArgumentException(

Review Comment:
   ⚠️ **Breaking behavioural change — release note needed:** The previous code 
emitted a `WARN` log and continued with the partially-converted result. This 
change throws unconditionally at expression-creation time. Any user who 
deployed a route that relied on best-effort conversion (accepting `null` 
sub-expressions for unconvertible constructs) will now get a hard failure on 
startup after upgrading. Please add a CHANGELOG/upgrade-notes entry for this 
change in behaviour.



##########
components/camel-datasonnet/src/main/java/org/apache/camel/language/datasonnet/DatasonnetLanguage.java:
##########
@@ -131,11 +131,13 @@ private static String convertDataWeave(String expression) 
{
             result = converter.convertExpression(expression);
         }
         if (converter.getTodoCount() > 0) {
-            LOG.warn("DataWeave conversion has {} construct(s) that could not 
be auto-converted and were emitted as null",
-                    converter.getTodoCount());
-        } else {
-            LOG.debug("Converted DataWeave to DataSonnet: {} expression(s)", 
converter.getConvertedCount());
+            throw new IllegalArgumentException(

Review Comment:
   ⚠️ **Exception type inconsistency:** `DataWeaveParser` throws 
`DataWeaveConversionException` for parse errors, but this path throws 
`IllegalArgumentException` for the unsupported-construct case. Callers cannot 
catch a single type to handle all conversion failures — they'd need to catch 
both, which is unintuitive. Since `DataWeaveConversionException` was introduced 
precisely for this scenario, prefer using it here too:
   
   ```suggestion
               throw new DataWeaveConversionException(
                       "DataWeave expression contains " + 
converter.getTodoCount()
                                                  + " construct(s) that cannot 
be auto-converted to DataSonnet."
                                                  + " Rewrite those constructs 
in DataSonnet directly, or use a .ds script instead."
                                                  + " Converted expression was: 
" + result);
   ```
   
   This also makes the Javadoc on `DataWeaveConversionException` accurate for 
all conversion failures, not just parse errors.



##########
components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/DataWeaveExecutedCorpusTest.java:
##########
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+package org.apache.camel.language.datasonnet;
+
+import com.datasonnet.document.MediaTypes;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Executed test corpus for DataWeave-to-DataSonnet auto-conversion 
(CAMEL-25323).
+ * <p>
+ * Each test case loads a {@code .dwl} resource from {@code corpus/}, sends a 
real input message through a Camel route
+ * backed by the DataSonnet language (which auto-converts the DataWeave), and 
asserts the output against an expected
+ * JSON payload. This ensures that the generated DataSonnet is not only 
syntactically plausible but actually runs and
+ * produces correct results.
+ * <p>
+ * A separate test verifies that routes referencing DataWeave scripts 
containing unsupported constructs fail at
+ * expression creation time with a clear error rather than silently returning 
{@code null}.
+ */
+class DataWeaveExecutedCorpusTest extends CamelTestSupport {
+
+    // 
-------------------------------------------------------------------------
+    // Corpus cases — each sends a JSON body through a .dwl resource and checks
+    // the output JSON.
+    // 
-------------------------------------------------------------------------
+
+    @Test
+    void testFieldAccess() throws Exception {
+        template.sendBody("direct:fieldAccess", 
"{\"name\":\"Widget\",\"qty\":3,\"price\":5}");
+        MockEndpoint mock = getMockEndpoint("mock:fieldAccess");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        
JSONAssert.assertEquals("{\"name\":\"Widget\",\"qty\":3,\"total\":15}", result, 
true);
+    }
+
+    @Test
+    void testStringConcat() throws Exception {
+        template.sendBody("direct:stringConcat", 
"{\"firstName\":\"John\",\"lastName\":\"Doe\"}");
+        MockEndpoint mock = getMockEndpoint("mock:stringConcat");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"greeting\":\"Hello, John Doe\"}", result, 
true);
+    }
+
+    @Test
+    void testIfElseA() throws Exception {
+        template.sendBody("direct:ifElseA", "{\"score\":95}");
+        MockEndpoint mock = getMockEndpoint("mock:ifElseA");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"label\":\"A\"}", result, true);
+    }
+
+    @Test
+    void testIfElseB() throws Exception {
+        template.sendBody("direct:ifElseB", "{\"score\":85}");
+        MockEndpoint mock = getMockEndpoint("mock:ifElseB");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"label\":\"B\"}", result, true);
+    }
+
+    @Test
+    void testIfElseC() throws Exception {
+        template.sendBody("direct:ifElseC", "{\"score\":70}");
+        MockEndpoint mock = getMockEndpoint("mock:ifElseC");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"label\":\"C\"}", result, true);
+    }
+
+    @Test
+    void testMapTransform() throws Exception {
+        template.sendBody("direct:mapTransform",
+                
"{\"items\":[{\"name\":\"A\",\"price\":10},{\"name\":\"B\",\"price\":20}]}");
+        MockEndpoint mock = getMockEndpoint("mock:mapTransform");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        
JSONAssert.assertEquals("[{\"name\":\"A\",\"price\":10},{\"name\":\"B\",\"price\":20}]",
 result, true);
+    }
+
+    @Test
+    void testFilter() throws Exception {
+        template.sendBody("direct:filter",
+                
"{\"items\":[{\"name\":\"A\",\"qty\":1},{\"name\":\"B\",\"qty\":3},{\"name\":\"C\",\"qty\":2}]}");
+        MockEndpoint mock = getMockEndpoint("mock:filter");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        
JSONAssert.assertEquals("[{\"name\":\"B\",\"qty\":3},{\"name\":\"C\",\"qty\":2}]",
 result, true);
+    }
+
+    @Test
+    void testReduce() throws Exception {
+        template.sendBody("direct:reduce", "{\"amounts\":[1,2,3,4]}");
+        MockEndpoint mock = getMockEndpoint("mock:reduce");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"total\":10}", result, true);
+    }
+
+    @Test
+    void testBuiltinFunctions() throws Exception {
+        template.sendBody("direct:builtinFunctions", "{\"text\":\"Hello 
World\"}");
+        MockEndpoint mock = getMockEndpoint("mock:builtinFunctions");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"upper\":\"HELLO WORLD\",\"lower\":\"hello 
world\",\"len\":11}", result, true);
+    }
+
+    @Test
+    void testTypeCoercion() throws Exception {
+        template.sendBody("direct:typeCoercion", "{\"amount\":\"42\"}");
+        MockEndpoint mock = getMockEndpoint("mock:typeCoercion");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"amount\":42,\"label\":\"42\"}", result, 
true);
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Fail-fast test — unsupported construct must throw at expression 
creation time
+    // 
-------------------------------------------------------------------------
+
+    @Test
+    void testUnsupportedConstructFailsFast() {
+        // DataWeave 'match' is not auto-convertible; 
DatasonnetLanguage.convertDataWeave()
+        // must reject the expression with a clear IllegalArgumentException 
rather than
+        // silently emitting null and letting the route start.
+        String withMatch = "%dw 2.0\noutput 
application/json\n---\npayload.status match { case \"a\" -> true else -> false 
}";
+        assertThrows(IllegalArgumentException.class,
+                () -> context.resolveLanguage("datasonnet")
+                        .createExpression(withMatch, new Object[] {
+                                String.class, null,
+                                MediaTypes.APPLICATION_JSON_VALUE, 
MediaTypes.APPLICATION_JSON_VALUE }));
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Strict-parser test — malformed DataWeave must report error at creation 
time
+    // 
-------------------------------------------------------------------------
+
+    @Test
+    void testStrictParserRejectsMalformedScript() {
+        // Inline DataWeave with a missing closing brace — the parser's strict 
expect()
+        // must propagate as an exception from 
DatasonnetLanguage.convertDataWeave()
+        // rather than silently producing a garbled AST.
+        String malformed = "%dw 2.0\noutput application/json\n---\n{ name: 
payload.name";
+        assertThrows(Exception.class,

Review Comment:
   ⚠️ **Assertion too broad:** `Exception.class` matches any throwable 
including `NullPointerException`, `RuntimeException`, etc. This test doesn't 
verify that the parser actually throws a `DataWeaveConversionException`. Use 
the specific type:
   
   ```suggestion
           assertThrows(DataWeaveConversionException.class,
   ```
   
   (You'll also need to add `import 
org.apache.camel.component.dataweave.DataWeaveConversionException;` at the top 
of the file.)



##########
components/camel-datasonnet/src/test/java/org/apache/camel/language/datasonnet/DataWeaveExecutedCorpusTest.java:
##########
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+package org.apache.camel.language.datasonnet;
+
+import com.datasonnet.document.MediaTypes;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.skyscreamer.jsonassert.JSONAssert;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Executed test corpus for DataWeave-to-DataSonnet auto-conversion 
(CAMEL-25323).
+ * <p>
+ * Each test case loads a {@code .dwl} resource from {@code corpus/}, sends a 
real input message through a Camel route
+ * backed by the DataSonnet language (which auto-converts the DataWeave), and 
asserts the output against an expected
+ * JSON payload. This ensures that the generated DataSonnet is not only 
syntactically plausible but actually runs and
+ * produces correct results.
+ * <p>
+ * A separate test verifies that routes referencing DataWeave scripts 
containing unsupported constructs fail at
+ * expression creation time with a clear error rather than silently returning 
{@code null}.
+ */
+class DataWeaveExecutedCorpusTest extends CamelTestSupport {
+
+    // 
-------------------------------------------------------------------------
+    // Corpus cases — each sends a JSON body through a .dwl resource and checks
+    // the output JSON.
+    // 
-------------------------------------------------------------------------
+
+    @Test
+    void testFieldAccess() throws Exception {
+        template.sendBody("direct:fieldAccess", 
"{\"name\":\"Widget\",\"qty\":3,\"price\":5}");
+        MockEndpoint mock = getMockEndpoint("mock:fieldAccess");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        
JSONAssert.assertEquals("{\"name\":\"Widget\",\"qty\":3,\"total\":15}", result, 
true);
+    }
+
+    @Test
+    void testStringConcat() throws Exception {
+        template.sendBody("direct:stringConcat", 
"{\"firstName\":\"John\",\"lastName\":\"Doe\"}");
+        MockEndpoint mock = getMockEndpoint("mock:stringConcat");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"greeting\":\"Hello, John Doe\"}", result, 
true);
+    }
+
+    @Test
+    void testIfElseA() throws Exception {
+        template.sendBody("direct:ifElseA", "{\"score\":95}");
+        MockEndpoint mock = getMockEndpoint("mock:ifElseA");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"label\":\"A\"}", result, true);
+    }
+
+    @Test
+    void testIfElseB() throws Exception {
+        template.sendBody("direct:ifElseB", "{\"score\":85}");
+        MockEndpoint mock = getMockEndpoint("mock:ifElseB");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"label\":\"B\"}", result, true);
+    }
+
+    @Test
+    void testIfElseC() throws Exception {
+        template.sendBody("direct:ifElseC", "{\"score\":70}");
+        MockEndpoint mock = getMockEndpoint("mock:ifElseC");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"label\":\"C\"}", result, true);
+    }
+
+    @Test
+    void testMapTransform() throws Exception {
+        template.sendBody("direct:mapTransform",
+                
"{\"items\":[{\"name\":\"A\",\"price\":10},{\"name\":\"B\",\"price\":20}]}");
+        MockEndpoint mock = getMockEndpoint("mock:mapTransform");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        
JSONAssert.assertEquals("[{\"name\":\"A\",\"price\":10},{\"name\":\"B\",\"price\":20}]",
 result, true);
+    }
+
+    @Test
+    void testFilter() throws Exception {
+        template.sendBody("direct:filter",
+                
"{\"items\":[{\"name\":\"A\",\"qty\":1},{\"name\":\"B\",\"qty\":3},{\"name\":\"C\",\"qty\":2}]}");
+        MockEndpoint mock = getMockEndpoint("mock:filter");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        
JSONAssert.assertEquals("[{\"name\":\"B\",\"qty\":3},{\"name\":\"C\",\"qty\":2}]",
 result, true);
+    }
+
+    @Test
+    void testReduce() throws Exception {
+        template.sendBody("direct:reduce", "{\"amounts\":[1,2,3,4]}");
+        MockEndpoint mock = getMockEndpoint("mock:reduce");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"total\":10}", result, true);
+    }
+
+    @Test
+    void testBuiltinFunctions() throws Exception {
+        template.sendBody("direct:builtinFunctions", "{\"text\":\"Hello 
World\"}");
+        MockEndpoint mock = getMockEndpoint("mock:builtinFunctions");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"upper\":\"HELLO WORLD\",\"lower\":\"hello 
world\",\"len\":11}", result, true);
+    }
+
+    @Test
+    void testTypeCoercion() throws Exception {
+        template.sendBody("direct:typeCoercion", "{\"amount\":\"42\"}");
+        MockEndpoint mock = getMockEndpoint("mock:typeCoercion");
+        mock.assertExchangeReceived(0);
+        String result = 
mock.getExchanges().get(0).getMessage().getBody(String.class);
+        JSONAssert.assertEquals("{\"amount\":42,\"label\":\"42\"}", result, 
true);
+    }
+
+    // 
-------------------------------------------------------------------------
+    // Fail-fast test — unsupported construct must throw at expression 
creation time
+    // 
-------------------------------------------------------------------------
+
+    @Test
+    void testUnsupportedConstructFailsFast() {
+        // DataWeave 'match' is not auto-convertible; 
DatasonnetLanguage.convertDataWeave()
+        // must reject the expression with a clear IllegalArgumentException 
rather than
+        // silently emitting null and letting the route start.
+        String withMatch = "%dw 2.0\noutput 
application/json\n---\npayload.status match { case \"a\" -> true else -> false 
}";
+        assertThrows(IllegalArgumentException.class,

Review Comment:
   💡 **(Advisory — conditional on the IAE→DataWeaveConversionException 
suggestion above being adopted):** If `convertDataWeave()` is changed to throw 
`DataWeaveConversionException`, this assertion should be updated to match:
   
   ```suggestion
           assertThrows(DataWeaveConversionException.class,
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to