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 e42df7d3722 CAMEL-21402: Mock endpoints can now use all the standard languages more easily in fluent expecation builders and also custom functions in Java lined code (#16280) e42df7d3722 is described below commit e42df7d37221110667f3d983e48917bfa55c6c33 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Thu Nov 14 18:54:56 2024 +0100 CAMEL-21402: Mock endpoints can now use all the standard languages more easily in fluent expecation builders and also custom functions in Java lined code (#16280) --- .../camel-mock/src/main/docs/mock-component.adoc | 19 +++ .../camel/component/mock/MockValueBuilder.java | 187 ++++++++++++++++++++- .../camel/issues/MockExpectedHeaderCustomTest.java | 58 +++++++ .../camel/issues/MockExpectedHeaderSimpleTest.java | 66 ++++++++ .../camel/support/builder/ExpressionBuilder.java | 38 +++++ 5 files changed, 364 insertions(+), 4 deletions(-) diff --git a/components/camel-mock/src/main/docs/mock-component.adoc b/components/camel-mock/src/main/docs/mock-component.adoc index 00ea34639a3..5832fae8f43 100644 --- a/components/camel-mock/src/main/docs/mock-component.adoc +++ b/components/camel-mock/src/main/docs/mock-component.adoc @@ -288,6 +288,25 @@ By default, the source is the message body, and therefore is only needed when yo To use any of the Camel languages then do as shown previously with the XPath example. +==== Using a custom inlined function + +You can also use a custom `java.util.Function` as part of mock expectations. This allows you full power +to use Java programming to compute the returned value. + +For example, you can write a custom function that takes an int as input and return the double value. +And then use this in mock as follows: + +[source,java] +---- +mock.message(0).header("num").expression(o -> { + int num = (int) o; + return num * 2; +}).isLessThan(10); +---- + +This example is a bit silly, and in a real use-case you would use custom functions +in advanced testing where you need to do some special coding based on business logic and data. + === Mocking existing endpoints Camel now allows you to automatically mock existing endpoints in your 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 f4b56f9f753..56c4b1f4c08 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 @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; @@ -219,25 +220,203 @@ public class MockValueBuilder implements Expression, Predicate { // Expression builders // ------------------------------------------------------------------------- - public MockValueBuilder xpath(String xpath) { + /** + * Creates an expression using the custom expression + * + * @param function the custom function + * @return a builder with the expression + */ + public MockValueBuilder expression(Function<Object, Object> function) { + Expression newExp = ExpressionBuilder.customExpression(this.expression, function); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the given language + * + * @param language the language + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder language(String language, String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, language, value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the simple language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder simple(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "simple", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the datasonnet language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder datasonnet(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "datasonnet", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the groovy language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder groovy(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "groovy", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the javascript language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder js(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "js", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the jq language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder jq(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "jq", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the jsonpath language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder jsonpath(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "jsonpath", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the mvel language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder mvel(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "mvel", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the ognl language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder ognl(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "ognl", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the python language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder python(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "python", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the spel language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder spel(String value) { + Expression newExp = ExpressionBuilder.languageExpression(expression, "spel", value, Object.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the xpath language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder xpath(String value) { // work with string as result as xpath otherwise will use DOM types - Expression newExp = ExpressionBuilder.languageExpression(expression, "xpath", xpath, String.class); + Expression newExp = ExpressionBuilder.languageExpression(expression, "xpath", value, String.class); + return onNewValueBuilder(newExp); + } + + /** + * Creates an expression using the xquery language + * + * @param value the expression value + * @return a builder with the expression + */ + public MockValueBuilder xquery(String value) { + // work with string as result as xquery otherwise will use DOM types + Expression newExp = ExpressionBuilder.languageExpression(expression, "xquery", value, String.class); return onNewValueBuilder(newExp); } + /** + * Creates an expression using the tokenize language using new-line as tokenizer + * + * @return a builder with the expression + */ public MockValueBuilder tokenize() { return tokenize("\n"); } + /** + * Creates an expression using the tokenize language + * + * @param token the token to use + * @return a builder with the expression + */ public MockValueBuilder tokenize(String token) { Expression newExp = ExpressionBuilder.tokenizeExpression(expression, token); return onNewValueBuilder(newExp); } + /** + * Creates an expression using the tokenize language + * + * @param token the token to use + * @param group number of elements to group + * @param skipFirst whether to skip first element + * @return a builder with the expression + */ public MockValueBuilder tokenize(String token, int group, boolean skipFirst) { return tokenize(token, Integer.toString(group), skipFirst); } + /** + * Creates an expression using the tokenize language + * + * @param token the token to use + * @param group number of elements to group + * @param skipFirst whether to skip first element + * @return a builder with the expression + */ public MockValueBuilder tokenize(String token, String group, boolean skipFirst) { Expression newExp = ExpressionBuilder.tokenizeExpression(expression, token); if (group == null && skipFirst) { @@ -351,8 +530,8 @@ public class MockValueBuilder implements Expression, Predicate { } protected Expression asExpression(Object value) { - if (value instanceof Expression expression1) { - return expression1; + if (value instanceof Expression exp) { + return exp; } else { return ExpressionBuilder.constantExpression(value); } diff --git a/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderCustomTest.java b/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderCustomTest.java new file mode 100644 index 00000000000..57268c8d37b --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderCustomTest.java @@ -0,0 +1,58 @@ +/* + * 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.issues; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.jupiter.api.Test; + +public class MockExpectedHeaderCustomTest extends ContextTestSupport { + + @Test + public void testHeaderCustom() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(2); + + // the header(num) becomes input to the custom expression + mock.message(0).header("num").expression(o -> { + int num = (int) o; + return num * 2; + }).isLessThan(10); + + // the header(num) becomes input to the simple language as "body" + mock.message(1).header("num").expression(o -> { + int num = (int) o; + return num * 3; + }).isGreaterThan(10); + + template.sendBodyAndHeader("direct:test", "message 1", "num", 3); + template.sendBodyAndHeader("direct:test", "message 2", "num", 7); + + mock.assertIsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:test").to("mock:result"); + } + }; + } +} diff --git a/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderSimpleTest.java b/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderSimpleTest.java new file mode 100644 index 00000000000..484c0459d5d --- /dev/null +++ b/core/camel-core/src/test/java/org/apache/camel/issues/MockExpectedHeaderSimpleTest.java @@ -0,0 +1,66 @@ +/* + * 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.issues; + +import org.apache.camel.ContextTestSupport; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.junit.jupiter.api.Test; + +public class MockExpectedHeaderSimpleTest extends ContextTestSupport { + + @Test + public void testSimple() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(2); + + mock.message(0).simple("${header.num}").isEqualTo(3); + mock.message(0).simple("${header.num}").isLessThan(5); + mock.message(1).simple("${header.num}").isEqualTo(7); + mock.message(1).simple("${header.num}").isGreaterThan(5); + + template.sendBodyAndHeader("direct:test", "message 1", "num", "3"); + template.sendBodyAndHeader("direct:test", "message 2", "num", "7"); + + mock.assertIsSatisfied(); + } + + @Test + public void testHeaderSimple() throws Exception { + MockEndpoint mock = getMockEndpoint("mock:result"); + mock.expectedMessageCount(2); + + // the header(num) becomes input to the simple language as "body" + mock.message(0).header("num").simple("${body}${body}").isEqualTo("33"); + mock.message(1).header("num").simple("${body}${body}").isEqualTo("77"); + + template.sendBodyAndHeader("direct:test", "message 1", "num", "3"); + template.sendBodyAndHeader("direct:test", "message 2", "num", "7"); + + mock.assertIsSatisfied(); + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:test").to("mock:result"); + } + }; + } +} 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 7165644a51d..9d27fc8adac 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 @@ -2445,6 +2445,29 @@ public class ExpressionBuilder { }; } + public static Expression beanExpression(final Class<?> bean, final String method) { + return new ExpressionAdapter() { + private Language language; + + @Override + public Object evaluate(Exchange exchange) { + Expression exp = language.createExpression(null, new Object[] { null, null, method, bean }); + exp.init(exchange.getContext()); + return exp.evaluate(exchange, Object.class); + } + + @Override + public void init(CamelContext context) { + super.init(context); + this.language = context.resolveLanguage("bean"); + } + + public String toString() { + return "bean(" + bean + ", " + method + ")"; + } + }; + } + public static Expression propertiesComponentExpression(final String key, final String defaultValue) { return new ExpressionAdapter() { private Expression exp; @@ -2567,6 +2590,21 @@ public class ExpressionBuilder { }; } + public static Expression customExpression(final Expression expression, final Function<Object, Object> function) { + return new ExpressionAdapter() { + + @Override + public Object evaluate(Exchange exchange) { + Object input = expression.evaluate(exchange, Object.class); + return function.apply(input); + } + + public String toString() { + return "custom(" + expression + ")"; + } + }; + } + /** * Returns the expression for the message body as a one-line string */