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 a4723cc09f2 18913: simple language - Add join function that for 
example can be used in camel-http to build multi-value query
a4723cc09f2 is described below

commit a4723cc09f21310cf71f66538bffa995a12e5b7f
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Jan 13 13:01:29 2023 +0100

    18913: simple language - Add join function that for example can be used in 
camel-http to build multi-value query
---
 .../camel/component/http/HttpSetHeaderIdsTest.java | 81 ++++++++++++++++++++++
 .../modules/languages/pages/simple-language.adoc   |  6 ++
 .../language/simple/SimpleExpressionBuilder.java   | 31 +++++++++
 .../simple/ast/SimpleFunctionExpression.java       | 28 ++++++++
 .../apache/camel/language/simple/SimpleTest.java   | 26 +++++++
 .../camel/support/builder/ExpressionBuilder.java   | 47 +++++++++++++
 6 files changed, 219 insertions(+)

diff --git 
a/components/camel-http/src/test/java/org/apache/camel/component/http/HttpSetHeaderIdsTest.java
 
b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpSetHeaderIdsTest.java
new file mode 100644
index 00000000000..09349ed2376
--- /dev/null
+++ 
b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpSetHeaderIdsTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.component.http;
+
+import java.util.List;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.bootstrap.HttpServer;
+import org.apache.http.impl.bootstrap.ServerBootstrap;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class HttpSetHeaderIdsTest extends BaseHttpTest {
+
+    private HttpServer localServer;
+
+    @BeforeEach
+    @Override
+    public void setUp() throws Exception {
+        localServer = 
ServerBootstrap.bootstrap().setHttpProcessor(getBasicHttpProcessor())
+                
.setConnectionReuseStrategy(getConnectionReuseStrategy()).setResponseFactory(getHttpResponseFactory())
+                
.setExpectationVerifier(getHttpExpectationVerifier()).setSslContext(getSSLContext())
+                .registerHandler("/myids", (httpRequest, httpResponse, 
httpContext) -> {
+                    Assertions.assertNull(httpRequest.getFirstHeader("ids"));
+                    String u = httpRequest.getRequestLine().getUri();
+                    httpResponse.setEntity(new StringEntity(u));
+                }).create();
+        localServer.start();
+
+        super.setUp();
+    }
+
+    @AfterEach
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+
+        if (localServer != null) {
+            localServer.stop();
+        }
+    }
+
+    @Override
+    protected RoutesBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                from("direct:myids")
+                        .setHeader(Exchange.HTTP_QUERY, 
simple("${join(&,id=,${header.ids})}"))
+                        .removeHeader("ids")
+                        .to("http://localhost:"; + localServer.getLocalPort() + 
"/myids");
+            }
+        };
+    }
+
+    @Test
+    public void testIds() throws Exception {
+        String o = fluentTemplate.to("direct:myids").withHeader("ids", 
List.of(1, 2, 3)).request(String.class);
+        Assertions.assertEquals("/myids?id=1&id=2&id=3", o);
+    }
+
+}
diff --git 
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
 
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
index 7023b95e8a5..ad3df916128 100644
--- 
a/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
+++ 
b/core/camel-core-languages/src/main/docs/modules/languages/pages/simple-language.adoc
@@ -213,6 +213,12 @@ similar to the collate method in Groovy.
 the first number of items. This can be used with the
 Splitter EIP to split a message body and skip the first N number of items.
 
+|join(separator,prefix,exp) | String | The join function iterates the message 
body (by default) and joins
+the data into a string. The separator is by default a comma. The prefix is 
optional.
+
+The join uses the message body as source by default. It is possible to refer 
to another
+source (simple language) such as a header via the exp parameter. For example 
`join('&','id=','$\{header.ids}')`.
+
 |messageHistory |String |The message history of the current exchange how it has
 been routed. This is similar to the route stack-trace message history
 the error handler logs in case of an unhandled exception.
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
index 14a1139d588..eec05382c10 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/SimpleExpressionBuilder.java
@@ -185,6 +185,37 @@ public final class SimpleExpressionBuilder {
         };
     }
 
+    /**
+     * Joins together the values from the expression
+     */
+    public static Expression joinExpression(final String expression, final 
String separator, final String prefix) {
+        return new ExpressionAdapter() {
+            private Expression exp;
+
+            @Override
+            public void init(CamelContext context) {
+                exp = 
context.resolveLanguage("simple").createExpression(expression);
+                exp.init(context);
+                exp = ExpressionBuilder.joinExpression(exp, separator, prefix);
+                exp.init(context);
+            }
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                return exp.evaluate(exchange, Object.class);
+            }
+
+            @Override
+            public String toString() {
+                if (prefix != null) {
+                    return "join(" + expression + "," + separator + "," + 
prefix + ")";
+                } else {
+                    return "join(" + expression + "," + separator + ")";
+                }
+            }
+        };
+    }
+
     /**
      * Returns a random number between min and max (exclusive)
      */
diff --git 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
index 27388e08b55..c7e0a032822 100644
--- 
a/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
+++ 
b/core/camel-core-languages/src/main/java/org/apache/camel/language/simple/ast/SimpleFunctionExpression.java
@@ -32,6 +32,7 @@ import org.apache.camel.support.builder.ExpressionBuilder;
 import org.apache.camel.util.ObjectHelper;
 import org.apache.camel.util.OgnlHelper;
 import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.StringQuoteHelper;
 import org.apache.camel.util.URISupport;
 
 /**
@@ -545,6 +546,33 @@ public class SimpleFunctionExpression extends 
LiteralExpression {
             return SimpleExpressionBuilder.collateExpression(exp, num);
         }
 
+        // join function
+        remainder = ifStartsWithReturnRemainder("join(", function);
+        if (remainder != null) {
+            String values = StringHelper.before(remainder, ")");
+            String separator = ",";
+            String prefix = null;
+            String exp = "${body}";
+            if (ObjectHelper.isNotEmpty(values)) {
+                String[] tokens = StringQuoteHelper.splitSafeQuote(values, 
',', false);
+                if (tokens.length > 3) {
+                    throw new SimpleParserException(
+                            "Valid syntax: 
${join(separator,prefix,expression)} was: " + function, token.getIndex());
+                }
+                if (tokens.length == 3) {
+                    separator = tokens[0];
+                    prefix = tokens[1];
+                    exp = tokens[2];
+                } else if (tokens.length == 2) {
+                    separator = tokens[0];
+                    prefix = tokens[1];
+                } else {
+                    separator = tokens[0];
+                }
+            }
+            return SimpleExpressionBuilder.joinExpression(exp, separator, 
prefix);
+        }
+
         // messageHistory function
         remainder = ifStartsWithReturnRemainder("messageHistory", function);
         if (remainder != null) {
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
index 57fcbf88bf3..7466326bf11 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/language/simple/SimpleTest.java
@@ -1845,6 +1845,32 @@ public class SimpleTest extends LanguageTestSupport {
         assertEquals("G", chunk3.get(0));
     }
 
+    @Test
+    public void testJoinBody() throws Exception {
+        List<Object> data = new ArrayList<>();
+        data.add("A");
+        data.add("B");
+        data.add("C");
+        exchange.getIn().setBody(data);
+
+        assertExpression("${join()}", "A,B,C");
+        assertExpression("${join(;)}", "A;B;C");
+        assertExpression("${join(' ')}", "A B C");
+        assertExpression("${join(',','id=')}", "id=A,id=B,id=C");
+        assertExpression("${join(&,id=)}", "id=A&id=B&id=C");
+    }
+
+    @Test
+    public void testJoinHeader() throws Exception {
+        List<Object> data = new ArrayList<>();
+        data.add("A");
+        data.add("B");
+        data.add("C");
+        exchange.getIn().setHeader("id", data);
+
+        assertExpression("${join('&','id=','${header.id}')}", 
"id=A&id=B&id=C");
+    }
+
     @Test
     public void testRandomExpression() throws Exception {
         int min = 1;
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 2d8a08ec715..96de5b5e1c4 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
@@ -1439,6 +1439,53 @@ public class ExpressionBuilder {
         };
     }
 
+    public static Expression joinExpression(final Expression expression, final 
String separator, final String prefix) {
+        return new ExpressionAdapter() {
+            private TypeConverter converter;
+
+            @Override
+            public Object evaluate(Exchange exchange) {
+                // evaluate expression as iterator
+                Iterator<?> it = expression.evaluate(exchange, Iterator.class);
+                ObjectHelper.notNull(it, "expression: " + expression + " 
evaluated on " + exchange + " must return an java.util.Iterator");
+
+                StringBuilder sb = new StringBuilder();
+                while (it.hasNext()) {
+                    Object o = it.next();
+                    if (o != null) {
+                        String s = converter.tryConvertTo(String.class, 
exchange, o);
+                        if (s != null) {
+                            if (sb.length() > 0) {
+                                sb.append(separator);
+                            }
+                            if (prefix != null) {
+                                sb.append(prefix);
+                            }
+                            sb.append(s);
+                        }
+                    }
+                }
+
+                return sb.toString();
+            }
+
+            @Override
+            public void init(CamelContext context) {
+                expression.init(context);
+                converter = context.getTypeConverter();
+            }
+
+            @Override
+            public String toString() {
+                if (prefix != null) {
+                    return "join(" + expression + "," + separator + "," + 
prefix + ")";
+                } else {
+                    return "join(" + expression + "," + separator + ")";
+                }
+            }
+        };
+    }
+
     /**
      * Returns a sort expression which will sort the expression with the given 
comparator.
      * <p/>

Reply via email to