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 + ")";
+ }
+ };
+ }
+
}