This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new c323b111ef2e CAMEL-25455: camel-wasm - the wasm predicate matches
Boolean, String and byte[] results (#27592)
c323b111ef2e is described below
commit c323b111ef2e500b857ed4f2fe7e744170fe3d3c
Author: allthingssecurity <[email protected]>
AuthorDate: Sat Oct 10 01:29:00 2026 +0530
CAMEL-25455: camel-wasm - the wasm predicate matches Boolean, String and
byte[] results (#27592)
WasmExpression.matches() only recognised a Jackson BooleanNode or a
non-empty
Collection, so with the default byte[] result type (or Boolean/String) a
wasm
predicate never matched. The result is now evaluated with
ObjectHelper.evaluateValuePredicate, converting a byte[] result to a String
first; a JsonNode result still only matches when it is the boolean true.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../apache/camel/catalog/docs/wasm-language.adoc | 56 ++++++++++++++++++++++
.../camel-wasm/src/main/docs/wasm-language.adoc | 56 ++++++++++++++++++++++
.../apache/camel/language/wasm/WasmExpression.java | 18 +++----
.../camel/language/wasm/WasmLanguageTest.java | 31 ++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc | 7 +++
5 files changed, 160 insertions(+), 8 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/wasm-language.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/wasm-language.adoc
index 340d82b8157b..e78879a52e01 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/wasm-language.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/wasm-language.adoc
@@ -184,6 +184,62 @@ YAML::
====
+== Using Wasm as a predicate
+
+When the function is used as a xref:manual::predicate.adoc[Predicate], for
example with the
+xref:eips:filter-eip.adoc[Filter] EIP, its result is converted to a boolean
depending on the result type:
+
+* With the default result type (`byte[]`) or `String`, the returned bytes are
read as a string and the
+standard Camel rules apply: `true` and `false` (in any case) are parsed, empty
data is `false`, and any
+other value is `true`. So the function can simply return `true` or `false`.
+* With the result type `Boolean`, only `true` and `false` (in any case) are
accepted: any other value,
+including empty data, is `false`.
+* With a Jackson `JsonNode` result type, only the JSON boolean `true` matches.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:in")
+ .filter().wasm("is_valid", "classpath://functions.wasm")
+ .to("direct:valid");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:in"/>
+ <filter>
+ <wasm module="classpath://functions.wasm">is_valid</wasm>
+ <to uri="direct:valid"/>
+ </filter>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:in
+ steps:
+ - filter:
+ expression:
+ wasm:
+ expression: is_valid
+ module: classpath://functions.wasm
+ steps:
+ - to:
+ uri: direct:valid
+----
+====
+
+
== Dependencies
If you use Maven you could add the following to your `pom.xml`,
diff --git a/components/camel-wasm/src/main/docs/wasm-language.adoc
b/components/camel-wasm/src/main/docs/wasm-language.adoc
index 340d82b8157b..e78879a52e01 100644
--- a/components/camel-wasm/src/main/docs/wasm-language.adoc
+++ b/components/camel-wasm/src/main/docs/wasm-language.adoc
@@ -184,6 +184,62 @@ YAML::
====
+== Using Wasm as a predicate
+
+When the function is used as a xref:manual::predicate.adoc[Predicate], for
example with the
+xref:eips:filter-eip.adoc[Filter] EIP, its result is converted to a boolean
depending on the result type:
+
+* With the default result type (`byte[]`) or `String`, the returned bytes are
read as a string and the
+standard Camel rules apply: `true` and `false` (in any case) are parsed, empty
data is `false`, and any
+other value is `true`. So the function can simply return `true` or `false`.
+* With the result type `Boolean`, only `true` and `false` (in any case) are
accepted: any other value,
+including empty data, is `false`.
+* With a Jackson `JsonNode` result type, only the JSON boolean `true` matches.
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:in")
+ .filter().wasm("is_valid", "classpath://functions.wasm")
+ .to("direct:valid");
+----
+
+XML::
++
+[source,xml]
+----
+<route>
+ <from uri="direct:in"/>
+ <filter>
+ <wasm module="classpath://functions.wasm">is_valid</wasm>
+ <to uri="direct:valid"/>
+ </filter>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:in
+ steps:
+ - filter:
+ expression:
+ wasm:
+ expression: is_valid
+ module: classpath://functions.wasm
+ steps:
+ - to:
+ uri: direct:valid
+----
+====
+
+
== Dependencies
If you use Maven you could add the following to your `pom.xml`,
diff --git
a/components/camel-wasm/src/main/java/org/apache/camel/language/wasm/WasmExpression.java
b/components/camel-wasm/src/main/java/org/apache/camel/language/wasm/WasmExpression.java
index 065d5ba13d75..163c2cba3bcf 100644
---
a/components/camel-wasm/src/main/java/org/apache/camel/language/wasm/WasmExpression.java
+++
b/components/camel-wasm/src/main/java/org/apache/camel/language/wasm/WasmExpression.java
@@ -18,9 +18,8 @@ package org.apache.camel.language.wasm;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Collection;
-import com.fasterxml.jackson.databind.node.BooleanNode;
+import com.fasterxml.jackson.databind.JsonNode;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.RuntimeCamelException;
@@ -30,6 +29,7 @@ import org.apache.camel.spi.Resource;
import org.apache.camel.spi.ResourceLoader;
import org.apache.camel.support.ExpressionAdapter;
import org.apache.camel.support.PluginHelper;
+import org.apache.camel.util.ObjectHelper;
import org.apache.camel.wasm.WasmFunction;
import org.apache.camel.wasm.WasmSupport;
import run.endive.wasm.Parser;
@@ -140,16 +140,18 @@ public class WasmExpression extends ExpressionAdapter
implements ExpressionResul
@Override
public boolean matches(Exchange exchange) {
- final Object value = evaluate(exchange, Object.class);
+ Object value = evaluate(exchange, Object.class);
- if (value instanceof BooleanNode) {
- return ((BooleanNode) value).asBoolean();
+ if (value instanceof JsonNode node) {
+ // a JSON result only matches when it is the boolean true
+ return node.isBoolean() && node.asBoolean();
}
- if (value instanceof Collection) {
- return !((Collection<?>) value).isEmpty();
+ if (value instanceof byte[] bytes) {
+ // the default result type: the raw bytes returned by the
function, such as "true" or "false"
+ value = typeConverter.convertTo(String.class, exchange, bytes);
}
- return false;
+ return ObjectHelper.evaluateValuePredicate(value);
}
@Override
diff --git
a/components/camel-wasm/src/test/java/org/apache/camel/language/wasm/WasmLanguageTest.java
b/components/camel-wasm/src/test/java/org/apache/camel/language/wasm/WasmLanguageTest.java
index 4b1bc754781e..86d027d6e78c 100644
---
a/components/camel-wasm/src/test/java/org/apache/camel/language/wasm/WasmLanguageTest.java
+++
b/components/camel-wasm/src/test/java/org/apache/camel/language/wasm/WasmLanguageTest.java
@@ -19,7 +19,9 @@ package org.apache.camel.language.wasm;
import org.apache.camel.CamelContext;
import org.apache.camel.Exchange;
import org.apache.camel.FluentProducerTemplate;
+import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.util.StringHelper;
import org.assertj.core.api.InstanceOfAssertFactories;
@@ -95,4 +97,33 @@ public class WasmLanguageTest {
.hasMessage("this is an error");
}
}
+
+ @ParameterizedTest
+ @ValueSource(classes = { byte[].class, String.class, Boolean.class })
+ public void testPredicate(Class<?> resultType) throws Exception {
+ try (CamelContext cc = new DefaultCamelContext()) {
+
+ cc.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ // the transform function upper-cases the body, so it
returns TRUE, FALSE or nothing
+ from("direct:in")
+ .filter()
+ .wasm("transform", "functions.wasm", resultType)
+ .to("mock:result");
+ }
+ });
+ cc.start();
+
+ MockEndpoint mock = cc.getEndpoint("mock:result",
MockEndpoint.class);
+ mock.expectedBodiesReceived("true");
+
+ ProducerTemplate pt = cc.createProducerTemplate();
+ pt.sendBody("direct:in", "false");
+ pt.sendBody("direct:in", "");
+ pt.sendBody("direct:in", "true");
+
+ mock.assertIsSatisfied();
+ }
+ }
}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index d55a3b583902..b3c7ed0ddba2 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -3048,6 +3048,13 @@ When a Wasm function fails without returning (a trap,
such as a Rust `panic`), t
now discards its module instance and creates a new one for the next message,
instead of reusing an instance whose memory
kept everything the failed call had allocated. State that the module keeps
between messages is reset at that point.
+=== camel-wasm - the wasm predicate
+
+The `wasm` language used as a predicate only matched a Jackson `BooleanNode`
result, so with the default result type
+(`byte[]`) or `resultType` set to `Boolean` or `String` it never matched. The
result is now evaluated with the standard
+Camel rules (`true`/`false` text, a `Boolean`, empty is `false`), so filters
and choices that never matched before can
+now match. A `JsonNode` result still only matches when it is the JSON boolean
`true`.
+
=== camel-xpath
The XPath language now parses the message with the same hardened XML parser
for every `documentType`.