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 1ea7eb3a775 CAMEL-21402: camel-mock - Make it easier to use more 
built-in languages in setting expectations using the existing fluent builder. 
Added xpath. (#16142)
1ea7eb3a775 is described below

commit 1ea7eb3a77512a81c5a75fa5cf14dd2b85f93aeb
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 9 09:20:36 2024 +0100

    CAMEL-21402: camel-mock - Make it easier to use more built-in languages in 
setting expectations using the existing fluent builder. Added xpath. (#16142)
---
 .../camel-mock/src/main/docs/mock-component.adoc   | 62 ++++++++++++++++------
 .../camel/component/mock/MockValueBuilder.java     | 14 +++++
 .../camel/issues/MockExpectedHeaderXPathTest.java  | 39 +++++++++++++-
 .../camel/support/builder/ExpressionBuilder.java   | 60 +++++++++++++++++++++
 .../camel/support/builder/PredicateBuilder.java    | 59 ++++++++++++++++++++
 5 files changed, 217 insertions(+), 17 deletions(-)

diff --git a/components/camel-mock/src/main/docs/mock-component.adoc 
b/components/camel-mock/src/main/docs/mock-component.adoc
index 8573900f798..00ea34639a3 100644
--- a/components/camel-mock/src/main/docs/mock-component.adoc
+++ b/components/camel-mock/src/main/docs/mock-component.adoc
@@ -209,7 +209,53 @@ There are some examples of the Mock endpoint in use in the
 
https://github.com/apache/camel/tree/main/core/camel-core/src/test/java/org/apache/camel/processor[`camel-core`
 processor tests].
 
-==== Using any language for expectations
+==== Using built-in language for expectations
+
+When you want to check that a given message body or header is as expected, and 
the format is XML or JSon,
+then you can use the Camel languages to perform the validation.
+
+This section covers the built-in support in the mock component for commonly 
used languages.
+See next section for how to use all the Camel languages using the language 
builder style.
+
+You can use regular expressions as expectations, as follows:
+
+[source,java]
+----
+mock.message(1).header("cheese").regex("value[2,3]");
+mock.message(2).header("cheese").regex("value[2,3]");
+// should not match
+mock.message(0).header("cheese").not().regex("value[2,3]");
+mock.message(3).header("cheese").not().regex("value[2,3]");
+----
+
+Here we use the _built-in_ `regex` function from the mock component, that makes
+coding this easier. There are a limited set of functions out of the box.
+
+You can also use XPath as follows:
+
+[source,java]
+----
+String filter = "/person[@name='James']";
+...
+mock.message(0).header("cheese").xpath(filter).isFalse();
+mock.message(1).header("cheese").xpath(filter).isTrue();
+mock.message(2).header("cheese").xpath(filter).isFalse();
+----
+
+You can also use xpath to check if it matches a given value such as:
+
+[source,java]
+----
+String name = "/person/@name";
+...
+mock.message(0).header("cheese").xpath(name).isEqualTo("Hiram");
+mock.message(1).header("cheese").xpath(name).isEqualTo("James");
+mock.message(2).header("cheese").xpath(name).isEqualTo("Jack");
+----
+
+There are a number of built-in languages you can use, see next section for 
using all the languages.
+
+==== Using all the language for expectations using language builder
 
 When you want to check that a given message body or header is as expected, and 
the format is XML or JSon,
 then you can use the Camel languages to perform the validation.
@@ -240,20 +286,6 @@ 
mock.message(1).predicate(expression().xpath("/person[@name='James']").source("h
 Notice that the xpath language need to use `source` to refer to the value 
should be from the header with key cheese.
 By default, the source is the message body, and therefore is only needed when 
you refer to headers/variables etc.
 
-You can also use regular expressions as expectations, as follows:
-
-[source,java]
-----
-mock.message(1).header("cheese").regex("value[2,3]");
-mock.message(2).header("cheese").regex("value[2,3]");
-// should not match
-mock.message(0).header("cheese").not().regex("value[2,3]");
-mock.message(3).header("cheese").not().regex("value[2,3]");
-----
-
-Here we use the _built-in_ `regex` function from the mock component, that makes
-coding this easier. There are a limited set of functions out of the box.
-
 To use any of the Camel languages then do as shown previously with the XPath 
example.
 
 === Mocking existing endpoints
diff --git 
a/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockValueBuilder.java
 
b/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockValueBuilder.java
index c6dce5841d2..f4b56f9f753 100644
--- 
a/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockValueBuilder.java
+++ 
b/components/camel-mock/src/main/java/org/apache/camel/component/mock/MockValueBuilder.java
@@ -110,6 +110,14 @@ public class MockValueBuilder implements Expression, 
Predicate {
         return answer;
     }
 
+    public Predicate isTrue() {
+        return onNewPredicate(PredicateBuilder.isTrue(expression));
+    }
+
+    public Predicate isFalse() {
+        return onNewPredicate(PredicateBuilder.isFalse(expression));
+    }
+
     public Predicate isNotEqualTo(Object value) {
         Expression right = asExpression(value);
         return onNewPredicate(PredicateBuilder.isNotEqualTo(expression, 
right));
@@ -211,6 +219,12 @@ public class MockValueBuilder implements Expression, 
Predicate {
     // Expression builders
     // 
-------------------------------------------------------------------------
 
+    public MockValueBuilder xpath(String xpath) {
+        // work with string as result as xpath otherwise will use DOM types
+        Expression newExp = ExpressionBuilder.languageExpression(expression, 
"xpath", xpath, String.class);
+        return onNewValueBuilder(newExp);
+    }
+
     public MockValueBuilder tokenize() {
         return tokenize("\n");
     }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderXPathTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderXPathTest.java
index 07d7ade49b0..04c3bf25aff 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderXPathTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderXPathTest.java
@@ -25,17 +25,19 @@ import static 
org.apache.camel.support.builder.PredicateBuilder.not;
 
 public class MockExpectedHeaderXPathTest extends ContextTestSupport {
 
+    protected String filter = "/person[@name='James']";
+    protected String name = "/person/@name";
     protected String matchingBody = "<person name='James' city='London'/>";
     protected String notMatchingBody = "<person name='Hiram' city='Tampa'/>";
     protected String notMatchingBody2 = "<person name='Jack' city='Houston'/>";
 
     @Test
-    public void testHeaderXPath() throws Exception {
+    public void testHeaderXPathBuilderLanguageBuilder() throws Exception {
         MockEndpoint mock = getMockEndpoint("mock:result");
         mock.expectedMessageCount(3);
 
         // xpath that takes input from a header
-        var xpath = 
expression().xpath("/person[@name='James']").source("header:cheese").end();
+        var xpath = expression().xpath(filter).source("header:cheese").end();
 
         // validate that some of the headers match and others do not
         mock.message(0).predicate(not(xpath));
@@ -49,6 +51,39 @@ public class MockExpectedHeaderXPathTest extends 
ContextTestSupport {
         mock.assertIsSatisfied();
     }
 
+    @Test
+    public void testHeaderXPathPredicate() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(3);
+
+        // validate that some of the headers match and others do not
+        mock.message(0).header("cheese").xpath(filter).isFalse();
+        mock.message(1).header("cheese").xpath(filter).isTrue();
+        mock.message(2).header("cheese").xpath(filter).isFalse();
+
+        template.sendBodyAndHeader("direct:test", "message 1", "cheese", 
notMatchingBody);
+        template.sendBodyAndHeader("direct:test", "message 2", "cheese", 
matchingBody);
+        template.sendBodyAndHeader("direct:test", "message 3", "cheese", 
notMatchingBody2);
+
+        mock.assertIsSatisfied();
+    }
+
+    @Test
+    public void testHeaderXPathExpressionAttribute() throws Exception {
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(3);
+
+        mock.message(0).header("cheese").xpath(name).isEqualTo("Hiram");
+        mock.message(1).header("cheese").xpath(name).isEqualTo("James");
+        mock.message(2).header("cheese").xpath(name).isEqualTo("Jack");
+
+        template.sendBodyAndHeader("direct:test", "message 1", "cheese", 
notMatchingBody);
+        template.sendBodyAndHeader("direct:test", "message 2", "cheese", 
matchingBody);
+        template.sendBodyAndHeader("direct:test", "message 3", "cheese", 
notMatchingBody2);
+
+        mock.assertIsSatisfied();
+    }
+
     @Override
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
index 26067f8ccc0..7165644a51d 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/ExpressionBuilder.java
@@ -49,12 +49,14 @@ import org.apache.camel.spi.PropertiesComponent;
 import org.apache.camel.spi.Registry;
 import org.apache.camel.spi.UnitOfWork;
 import org.apache.camel.support.ConstantExpressionAdapter;
+import org.apache.camel.support.DefaultExchange;
 import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.ExpressionAdapter;
 import org.apache.camel.support.GroupIterator;
 import org.apache.camel.support.GroupTokenIterator;
 import org.apache.camel.support.LanguageHelper;
 import org.apache.camel.support.LanguageSupport;
+import org.apache.camel.support.PropertyBindingSupport;
 import org.apache.camel.support.SingleInputTypedLanguageSupport;
 import org.apache.camel.util.InetAddressUtil;
 import org.apache.camel.util.ObjectHelper;
@@ -1012,6 +1014,64 @@ public class ExpressionBuilder {
         };
     }
 
+    /**
+     * Returns an expression for evaluating the expression/predicate using the 
given language
+     *
+     * @param  expression the expression providing the input
+     * @param  language   the language
+     * @param  value      the value to use for evaluation
+     * @return            an expression object which will evaluate the 
expression/predicate using the given language
+     */
+    public static Expression languageExpression(
+            final Expression expression, final String language, final String 
value, Class<?> resultType) {
+        return new ExpressionAdapter() {
+            private Expression expr;
+            private Predicate pred;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                Object result = expression.evaluate(exchange, Object.class);
+                if (result != null) {
+                    Exchange dummy = new DefaultExchange(exchange);
+                    dummy.getMessage().setBody(result);
+                    return expr.evaluate(dummy, resultType);
+                }
+                return null;
+            }
+
+            @Override
+            public boolean matches(Exchange exchange) {
+                Object result = expression.evaluate(exchange, Object.class);
+                if (result != null) {
+                    Exchange dummy = new DefaultExchange(exchange);
+                    dummy.getMessage().setBody(result);
+                    return pred.matches(dummy);
+                }
+                return false;
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                super.init(context);
+                Language lan = context.resolveLanguage(language);
+                if (lan != null) {
+                    pred = lan.createPredicate(value);
+                    pred.init(context);
+                    expr = lan.createExpression(value);
+                    expr.init(context);
+                    PropertyBindingSupport.build().bind(context, expr, 
"resultQName", "string");
+                } else {
+                    throw new NoSuchLanguageException(language);
+                }
+            }
+
+            @Override
+            public String toString() {
+                return language + "(" + expression + ")";
+            }
+        };
+    }
+
     /**
      * Returns an expression for evaluating the expression/predicate using the 
given language
      *
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/PredicateBuilder.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/PredicateBuilder.java
index 48435031045..d87ee46bdc1 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/builder/PredicateBuilder.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/builder/PredicateBuilder.java
@@ -25,6 +25,8 @@ import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.Expression;
 import org.apache.camel.Predicate;
+import org.apache.camel.spi.Language;
+import org.apache.camel.support.ExchangeHelper;
 import org.apache.camel.support.ExpressionToPredicateAdapter;
 import org.apache.camel.support.LanguageHelper;
 import org.apache.camel.support.ObjectHelper;
@@ -178,6 +180,20 @@ public class PredicateBuilder {
         return in(predicates.toArray(new Predicate[0]));
     }
 
+    /**
+     * Is the predicate true
+     */
+    public static Predicate isTrue(final Expression left) {
+        return PredicateBuilder.toPredicate(left);
+    }
+
+    /**
+     * Is the predicate false
+     */
+    public static Predicate isFalse(final Expression left) {
+        return PredicateBuilder.not(PredicateBuilder.toPredicate(left));
+    }
+
     public static Predicate isEqualTo(final Expression left, final Expression 
right) {
         return new BinaryPredicateSupport(left, right) {
 
@@ -546,4 +562,47 @@ public class PredicateBuilder {
             }
         };
     }
+
+    /**
+     * Returns a predicate which is true if the expression matches the given 
language predicate
+     *
+     * @param  expression the expression to evaluate
+     * @param  language   the language such as xpath, jq, groovy, etc.
+     * @param  value      the value as expression for the language
+     * @return            a new predicate
+     */
+    public static Predicate language(final Expression expression, final String 
language, final Object value) {
+        notNull(expression, "expression");
+        notNull(language, "language");
+        notNull(value, "value");
+
+        return new Predicate() {
+
+            private Predicate pred;
+
+            public boolean matches(Exchange exchange) {
+                Object value = expression.evaluate(exchange, Object.class);
+                if (value != null) {
+                    Exchange dummy = 
ExchangeHelper.getDummy(exchange.getContext());
+                    dummy.getMessage().setBody(value);
+                    return pred.matches(dummy);
+                }
+                return false;
+            }
+
+            @Override
+            public void init(CamelContext camelContext) {
+                expression.init(camelContext);
+                Language lan = camelContext.resolveLanguage(language);
+                pred = lan.createPredicate(value.toString());
+                pred.init(camelContext);
+            }
+
+            @Override
+            public String toString() {
+                return language + "(" + expression + ")";
+            }
+        };
+    }
+
 }

Reply via email to