You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by di...@apache.org on 2019/08/23 20:45:29 UTC
[camel] branch master updated: CAMEL-13429: Support expressions in
path parameters at REST DSL and REST producer (#3120)
This is an automated email from the ASF dual-hosted git repository.
distomin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/master by this push:
new 932ab9a CAMEL-13429: Support expressions in path parameters at REST DSL and REST producer (#3120)
932ab9a is described below
commit 932ab9a1632c83f21c6f68a0c922ee5a791ac372
Author: Denis Istomin <is...@gmail.com>
AuthorDate: Sat Aug 24 01:45:22 2019 +0500
CAMEL-13429: Support expressions in path parameters at REST DSL and REST producer (#3120)
---
.../apache/camel/component/rest/RestProducer.java | 42 ++++-
.../apache/camel/model/rest/RestDefinition.java | 10 +-
.../rest/FromRestGetPlaceholderParamTest.java | 95 ++++++++++
.../camel/component/rest/RestProducerPathTest.java | 193 +++++++++++++++++++++
docs/user-manual/modules/ROOT/pages/rest-dsl.adoc | 13 +-
5 files changed, 339 insertions(+), 14 deletions(-)
diff --git a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
index 168d2d5..2fe5950 100644
--- a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
+++ b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
@@ -155,15 +155,15 @@ public class RestProducer extends DefaultAsyncProducer {
String[] arr = resolvedUriTemplate.split("\\/");
CollectionStringBuffer csb = new CollectionStringBuffer("/");
for (String a : arr) {
- if (a.startsWith("{") && a.endsWith("}")) {
- String key = a.substring(1, a.length() - 1);
- String value = inMessage.getHeader(key, String.class);
- if (value != null) {
- hasPath = true;
- csb.append(value);
- } else {
- csb.append(a);
- }
+ String resolvedUriParam = resolveHeaderPlaceholders(a, inMessage);
+
+ // Backward compatibility: if one of the path params is fully resolved,
+ // then it is assumed that whole uri is resolved.
+ if (!a.equals(resolvedUriParam)
+ && !resolvedUriParam.contains("{")
+ && !resolvedUriParam.contains("}")) {
+ hasPath = true;
+ csb.append(resolvedUriParam);
} else {
csb.append(a);
}
@@ -223,6 +223,30 @@ public class RestProducer extends DefaultAsyncProducer {
}
}
+ /**
+ * Replaces placeholders "{}" with message header values
+ * @param str string with placeholders
+ * @param msg message with headers
+ * @return filled string
+ */
+ private String resolveHeaderPlaceholders(String str, Message msg) {
+ int startIndex = -1;
+ String res = str;
+ while ((startIndex = res.indexOf("{", startIndex + 1)) >= 0) {
+ int endIndex = res.indexOf("}", startIndex);
+ if (endIndex == -1) {
+ continue;
+ }
+ String key = res.substring(startIndex + 1, endIndex);
+ String headerValue = msg.getHeader(key, String.class);
+ if (headerValue != null) {
+ res = res.substring(0, startIndex) + headerValue + res.substring(endIndex + 1);
+ }
+ }
+
+ return res;
+ }
+
@Override
protected void doStart() throws Exception {
super.doStart();
diff --git a/core/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java b/core/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
index 0bf0d8e..8e25341 100644
--- a/core/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
+++ b/core/camel-core/src/main/java/org/apache/camel/model/rest/RestDefinition.java
@@ -23,6 +23,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
@@ -917,9 +919,11 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
} catch (Exception e) {
throw RuntimeCamelException.wrapRuntimeCamelException(e);
}
- if (a.startsWith("{") && a.endsWith("}")) {
- String key = a.substring(1, a.length() - 1);
- // merge if exists
+
+ Matcher m = Pattern.compile("\\{(.*?)\\}").matcher(a);
+ while (m.find()) {
+ String key = m.group(1);
+ // merge if exists
boolean found = false;
for (RestOperationParamDefinition param : verb.getParams()) {
// name is mandatory
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetPlaceholderParamTest.java b/core/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetPlaceholderParamTest.java
new file mode 100644
index 0000000..a3b47f9
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/component/rest/FromRestGetPlaceholderParamTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.rest;
+
+import java.util.List;
+
+import javax.naming.Context;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.ToDefinition;
+import org.apache.camel.model.rest.RestDefinition;
+import org.apache.camel.model.rest.RestOperationParamDefinition;
+import org.apache.camel.model.rest.RestParamType;
+import org.junit.Test;
+
+public class FromRestGetPlaceholderParamTest extends ContextTestSupport {
+
+ @Override
+ protected Context createJndiContext() throws Exception {
+ Context context = super.createJndiContext();
+ context.bind("dummy-rest", new DummyRestConsumerFactory());
+ return context;
+ }
+
+ @Test
+ public void testFromRestModelSingleParam() {
+ RestDefinition rest = context.getRestDefinitions().get(0);
+ assertNotNull(rest);
+ assertEquals("items/", rest.getPath());
+ assertEquals(1, rest.getVerbs().size());
+ ToDefinition to = assertIsInstanceOf(ToDefinition.class, rest.getVerbs().get(0).getTo());
+ assertEquals("direct:hello", to.getUri());
+
+ // Validate params
+ List<RestOperationParamDefinition> paramDefinitions = rest.getVerbs().get(0).getParams();
+ assertEquals(1, paramDefinitions.size());
+ assertEquals(RestParamType.path, paramDefinitions.get(0).getType());
+ assertEquals("id", paramDefinitions.get(0).getName());
+ }
+
+ @Test
+ public void testFromRestModelMultipleParams() {
+ RestDefinition rest = context.getRestDefinitions().get(1);
+ assertNotNull(rest);
+ assertEquals("items/", rest.getPath());
+ assertEquals(1, rest.getVerbs().size());
+ ToDefinition to = assertIsInstanceOf(ToDefinition.class, rest.getVerbs().get(0).getTo());
+ assertEquals("direct:hello", to.getUri());
+
+ // Validate params
+ List<RestOperationParamDefinition> paramDefinitions = rest.getVerbs().get(0).getParams();
+ assertEquals(3, paramDefinitions.size());
+ assertEquals(RestParamType.path, paramDefinitions.get(0).getType());
+ assertEquals("id", paramDefinitions.get(0).getName());
+ assertEquals(RestParamType.path, paramDefinitions.get(1).getType());
+ assertEquals("filename", paramDefinitions.get(1).getName());
+ assertEquals(RestParamType.path, paramDefinitions.get(2).getType());
+ assertEquals("content-type", paramDefinitions.get(2).getName());
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ restConfiguration().host("localhost");
+ rest("items/")
+ .get("/{id}")
+ .to("direct:hello");
+
+ rest("items/")
+ .get("{id}/{filename}.{content-type}")
+ .to("direct:hello");
+
+ from("direct:hello")
+ .transform().constant("Hello World");
+ }
+ };
+ }
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/rest/RestProducerPathTest.java b/core/camel-core/src/test/java/org/apache/camel/component/rest/RestProducerPathTest.java
new file mode 100644
index 0000000..52e9c5b
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/component/rest/RestProducerPathTest.java
@@ -0,0 +1,193 @@
+/*
+ * 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.rest;
+
+import java.util.HashMap;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.Message;
+import org.apache.camel.impl.DefaultCamelContext;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class RestProducerPathTest {
+ private final RestComponent restComponent;
+
+ public RestProducerPathTest() {
+ DefaultCamelContext context = new DefaultCamelContext();
+ context.addComponent("mock-rest", new RestEndpointTest.MockRest());
+
+ restComponent = new RestComponent();
+ restComponent.setCamelContext(context);
+ }
+
+ private RestProducer createProducer(String uri) throws Exception {
+ final RestEndpoint restEndpoint = (RestEndpoint) restComponent.createEndpoint(uri);
+ restEndpoint.setConsumerComponentName("mock-rest");
+ restEndpoint.setParameters(new HashMap<>());
+ restEndpoint.setHost("http://localhost");
+ restEndpoint.setBindingMode("json");
+
+ return (RestProducer) restEndpoint.createProducer();
+ }
+
+ @Test
+ public void testEmptyParam() throws Exception {
+ RestProducer producer = createProducer("rest:get:list//{id}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertEquals("http://localhost/list//1", actual);
+ }
+
+ @Test
+ public void testNoHeaders() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/{id}_{val}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertNull(actual);
+ }
+
+ @Test
+ public void testMissingHeader() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/{id}/{val}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ // Backward compatibility: if one of the params is resolved
+ Assert.assertEquals("http://localhost/list/1/{val}", actual);
+ }
+
+ @Test
+ public void testMissingHeaderSingleParam() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/{id}_{val}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertNull(actual);
+ }
+
+ @Test
+ public void testMissingStartCurlyBrace() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/{id}_val}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+ message.setHeader("val", "test");
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertNull(actual);
+ }
+
+ @Test
+ public void testSingleMissingStartCurlyBrace() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/id}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertNull(actual);
+ }
+
+ @Test
+ public void testSingleMissingEndCurlyBrace() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/{id");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertNull(actual);
+ }
+
+ @Test
+ public void testMissingEndCurlyBrace() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/{id_{val}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+ message.setHeader("val", "test");
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertNull(actual);
+ }
+
+ @Test
+ public void testSingleParam() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/{id}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertEquals("http://localhost/list/1", actual);
+ }
+
+ @Test
+ public void testUnderscoreSeparator() throws Exception {
+ RestProducer producer = createProducer("rest:get:list/{id}_{val}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("id", 1);
+ message.setHeader("val", "test");
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertEquals("http://localhost/list/1_test", actual);
+ }
+
+ @Test
+ public void testDotSeparator() throws Exception {
+ RestProducer producer = createProducer("rest:get:items/item.{content-type}");
+ Exchange exchange = producer.createExchange();
+ Message message = exchange.getIn();
+ message.setHeader("content-type", "xml");
+
+ producer.process(exchange);
+
+ String actual = (String) message.getHeader(Exchange.REST_HTTP_URI);
+ Assert.assertEquals("http://localhost/items/item.xml", actual);
+ }
+}
diff --git a/docs/user-manual/modules/ROOT/pages/rest-dsl.adoc b/docs/user-manual/modules/ROOT/pages/rest-dsl.adoc
index 33a165b..4b080e9 100644
--- a/docs/user-manual/modules/ROOT/pages/rest-dsl.adoc
+++ b/docs/user-manual/modules/ROOT/pages/rest-dsl.adoc
@@ -19,7 +19,7 @@ others that has native REST integration.
The following Camel components supports the Rest DSL. See the bottom of
this page for how to integrate a component with the Rest DSL.
-* xref:components::rest-component.adoc[came-rest] *required* contains the base rest component needed by Rest DSL
+* xref:components::rest-component.adoc[camel-rest] *required* contains the base rest component needed by Rest DSL
* xref:components::netty-http-component.adoc[camel-netty-http] (also
supports Swagger Java)
* xref:components::jetty-component.adoc[camel-jetty] (also
@@ -149,7 +149,7 @@ And using XML DSL it becomes:
</rest>
----
-TIP:The REST DSL will take care of duplicate path separators when using base
+TIP: The REST DSL will take care of duplicate path separators when using base
path and uri templates. In the example above the rest base path ends
with a slash ( / ) and the verb starts with a slash ( / ). But Apache
Camel will take care of this and remove the duplicated slash.
@@ -173,6 +173,15 @@ only. The example above can be defined as:
</rest>
----
+TIP: You can combine path parameters to build complex expressions.
+For example:
+[source,java]
+----
+ rest("items/")
+ .get("{id}/{filename}.{content-type}")
+ .to("direct:item")
+----
+
== Using Dynamic To in Rest DSL
*Available as of Camel 2.16*