You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2024/02/28 15:07:06 UTC
(camel) branch main updated: CAMEL-20481: camel-core - Rest DSL should resolve placeholder for con… (#13340)
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 653b10d161e CAMEL-20481: camel-core - Rest DSL should resolve placeholder for con… (#13340)
653b10d161e is described below
commit 653b10d161e8fed1a68e6edec46621a59fa5f9f4
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Feb 28 16:07:00 2024 +0100
CAMEL-20481: camel-core - Rest DSL should resolve placeholder for con… (#13340)
CAMEL-20481: camel-core - Rest DSL should resolve placeholder for context-path and others
---
.../apache/camel/model/RouteDefinitionHelper.java | 17 +++---
.../apache/camel/model/rest/RestDefinition.java | 56 +++++++++--------
.../rest/FromRestPathPlaceholderTest.java | 70 ++++++++++++++++++++++
.../ROOT/pages/camel-4x-upgrade-guide-4_5.adoc | 31 +++++++++-
4 files changed, 138 insertions(+), 36 deletions(-)
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
index a372f229340..5b9f9b81bdf 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinitionHelper.java
@@ -19,11 +19,9 @@ package org.apache.camel.model;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
-import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -148,7 +146,7 @@ public final class RouteDefinitionHelper {
} else {
RestDefinition rest = route.getRestDefinition();
if (rest != null && route.isRest()) {
- VerbDefinition verb = findVerbDefinition(rest, route.getInput().getEndpointUri());
+ VerbDefinition verb = findVerbDefinition(context, rest, route.getInput().getEndpointUri());
if (verb != null) {
String id = context.resolvePropertyPlaceholders(verb.getId());
if (verb.hasCustomIdAssigned() && ObjectHelper.isNotEmpty(id) && !customIds.contains(id)) {
@@ -200,9 +198,11 @@ public final class RouteDefinitionHelper {
String endpointUri = fromDefinition.getEndpointUri();
if (ObjectHelper.isNotEmpty(endpointUri)
&& (endpointUri.startsWith("rest:") || endpointUri.startsWith("rest-api:"))) {
- Map<String, Object> options = new HashMap<>(1);
- options.put("routeId", route.getId());
- endpointUri = URISupport.appendParametersToURI(endpointUri, options);
+
+ // append route id as a new option
+ String query = URISupport.extractQuery(endpointUri);
+ String separator = query == null ? "?" : "&";
+ endpointUri += separator + "routeId=" + route.getId();
// replace uri with new routeId
fromDefinition.setUri(endpointUri);
@@ -216,12 +216,13 @@ public final class RouteDefinitionHelper {
/**
* Find verb associated with the route by mapping uri
*/
- private static VerbDefinition findVerbDefinition(RestDefinition rest, String endpointUri) throws Exception {
+ private static VerbDefinition findVerbDefinition(CamelContext camelContext, RestDefinition rest, String endpointUri)
+ throws Exception {
VerbDefinition ret = null;
String preVerbUri = "";
String target = URISupport.normalizeUri(endpointUri);
for (VerbDefinition verb : rest.getVerbs()) {
- String verbUri = URISupport.normalizeUri(rest.buildFromUri(verb));
+ String verbUri = URISupport.normalizeUri(rest.buildFromUri(camelContext, verb));
if (target.startsWith(verbUri) && preVerbUri.length() < verbUri.length()) {
// if there are multiple verb uri match, select the most specific one
// for example if the endpoint Uri is
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
index dc55acc2edb..0f82d3d3bb5 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/rest/RestDefinition.java
@@ -49,6 +49,8 @@ import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.StringHelper;
import org.apache.camel.util.URISupport;
+import static org.apache.camel.support.CamelContextHelper.parseText;
+
/**
* Defines a rest service using the rest-dsl
*/
@@ -722,8 +724,8 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
/**
* Build the from endpoint uri for the verb
*/
- public String buildFromUri(VerbDefinition verb) {
- return "rest:" + verb.asVerb() + ":" + buildUri(verb);
+ public String buildFromUri(CamelContext camelContext, VerbDefinition verb) {
+ return "rest:" + verb.asVerb() + ":" + buildUri(camelContext, verb);
}
// Implementation
@@ -895,50 +897,50 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
throw new IllegalArgumentException("Rest service: " + verb + " must have to endpoint configured.");
}
if (verb.getRouteId() != null) {
- route.routeId(verb.getRouteId());
+ route.routeId(parseText(camelContext, verb.getRouteId()));
}
route.getOutputs().add(verb.getTo());
// add the binding
RestBindingDefinition binding = new RestBindingDefinition();
binding.setComponent(component);
- binding.setType(verb.getType());
+ binding.setType(parseText(camelContext, verb.getType()));
binding.setTypeClass(verb.getTypeClass());
- binding.setOutType(verb.getOutType());
+ binding.setOutType(parseText(camelContext, verb.getOutType()));
binding.setOutTypeClass(verb.getOutTypeClass());
// verb takes precedence over configuration on rest
if (verb.getConsumes() != null) {
- binding.setConsumes(verb.getConsumes());
+ binding.setConsumes(parseText(camelContext, verb.getConsumes()));
} else {
binding.setConsumes(getConsumes());
}
if (verb.getProduces() != null) {
- binding.setProduces(verb.getProduces());
+ binding.setProduces(parseText(camelContext, verb.getProduces()));
} else {
binding.setProduces(getProduces());
}
if (verb.getBindingMode() != null) {
- binding.setBindingMode(verb.getBindingMode());
+ binding.setBindingMode(parseText(camelContext, verb.getBindingMode()));
} else {
binding.setBindingMode(getBindingMode());
}
if (verb.getSkipBindingOnErrorCode() != null) {
- binding.setSkipBindingOnErrorCode(verb.getSkipBindingOnErrorCode());
+ binding.setSkipBindingOnErrorCode(parseText(camelContext, verb.getSkipBindingOnErrorCode()));
} else {
binding.setSkipBindingOnErrorCode(getSkipBindingOnErrorCode());
}
if (verb.getClientRequestValidation() != null) {
- binding.setClientRequestValidation(verb.getClientRequestValidation());
+ binding.setClientRequestValidation(parseText(camelContext, verb.getClientRequestValidation()));
} else {
binding.setClientRequestValidation(getClientRequestValidation());
}
if (verb.getEnableCORS() != null) {
- binding.setEnableCORS(verb.getEnableCORS());
+ binding.setEnableCORS(parseText(camelContext, verb.getEnableCORS()));
} else {
binding.setEnableCORS(getEnableCORS());
}
if (verb.getEnableNoContentResponse() != null) {
- binding.setEnableNoContentResponse(verb.getEnableNoContentResponse());
+ binding.setEnableNoContentResponse(parseText(camelContext, verb.getEnableNoContentResponse()));
} else {
binding.setEnableNoContentResponse(getEnableNoContentResponse());
}
@@ -947,7 +949,7 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
RestParamType type = param.getType();
if ((RestParamType.query == type || RestParamType.header == type)
&& ObjectHelper.isNotEmpty(param.getDefaultValue())) {
- binding.addDefaultValue(param.getName(), param.getDefaultValue());
+ binding.addDefaultValue(param.getName(), parseText(camelContext, param.getDefaultValue()));
}
// register which parameters are required
Boolean required = param.getRequired();
@@ -968,12 +970,12 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
Map<String, Object> options = new HashMap<>();
// verb takes precedence over configuration on rest
if (verb.getConsumes() != null) {
- options.put("consumes", verb.getConsumes());
+ options.put("consumes", parseText(camelContext, verb.getConsumes()));
} else if (getConsumes() != null) {
options.put("consumes", getConsumes());
}
if (verb.getProduces() != null) {
- options.put("produces", verb.getProduces());
+ options.put("produces", parseText(camelContext, verb.getProduces()));
} else if (getProduces() != null) {
options.put("produces", getProduces());
}
@@ -1007,19 +1009,19 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
description = getDescriptionText();
}
if (description != null) {
- options.put("description", description);
+ options.put("description", parseText(camelContext, description));
}
- String path = getPath();
+ String path = parseText(camelContext, getPath());
String s1 = FileUtil.stripTrailingSeparator(path);
- String s2 = FileUtil.stripLeadingSeparator(verb.getPath());
+ String s2 = FileUtil.stripLeadingSeparator(parseText(camelContext, verb.getPath()));
String allPath;
if (s1 != null && s2 != null) {
allPath = s1 + "/" + s2;
} else if (path != null) {
allPath = path;
} else {
- allPath = verb.getPath();
+ allPath = parseText(camelContext, verb.getPath());
}
// each {} is a parameter (url templating)
@@ -1037,7 +1039,7 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
}
if (verb.getType() != null) {
- String bodyType = verb.getType();
+ String bodyType = parseText(camelContext, verb.getType());
if (bodyType.endsWith("[]")) {
bodyType = "List[" + bodyType.substring(0, bodyType.length() - 2) + "]";
}
@@ -1052,7 +1054,7 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
}
// create the from endpoint uri which is using the rest component
- String from = buildFromUri(verb);
+ String from = buildFromUri(camelContext, verb);
// rebuild uri without these query parameters
if (toRemove != null && !toRemove.isEmpty()) {
@@ -1136,16 +1138,18 @@ public class RestDefinition extends OptionalIdentifiedDefinition<RestDefinition>
return params;
}
- private String buildUri(VerbDefinition verb) {
+ private String buildUri(CamelContext camelContext, VerbDefinition verb) {
+ String answer;
if (path != null && verb.getPath() != null) {
- return path + ":" + verb.getPath();
+ answer = path + ":" + verb.getPath();
} else if (path != null) {
- return path;
+ answer = path;
} else if (verb.getPath() != null) {
- return verb.getPath();
+ answer = verb.getPath();
} else {
- return "";
+ answer = "";
}
+ return parseText(camelContext, answer);
}
private ParamDefinition findParam(VerbDefinition verb, String name) {
diff --git a/core/camel-core/src/test/java/org/apache/camel/component/rest/FromRestPathPlaceholderTest.java b/core/camel-core/src/test/java/org/apache/camel/component/rest/FromRestPathPlaceholderTest.java
new file mode 100644
index 00000000000..0cdb9acdecd
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/component/rest/FromRestPathPlaceholderTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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 org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.model.rest.RestDefinition;
+import org.apache.camel.spi.Registry;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class FromRestPathPlaceholderTest extends ContextTestSupport {
+
+ @Override
+ protected Registry createRegistry() throws Exception {
+ Registry jndi = super.createRegistry();
+ jndi.bind("dummy-rest", new DummyRestConsumerFactory());
+ return jndi;
+ }
+
+ protected int getExpectedNumberOfRoutes() {
+ // routes are inlined
+ return 1;
+ }
+
+ @Test
+ public void testPlaceholder() throws Exception {
+ assertEquals(getExpectedNumberOfRoutes(), context.getRoutes().size());
+
+ RestDefinition rest = context.getRestDefinitions().get(0);
+ assertNotNull(rest);
+ assertEquals("/say/{{mypath}}", rest.getPath());
+
+ // placeholder should be resolved, so we can find the rest endpoint that is a dummy (via seda)
+ assertNotNull(context.hasEndpoint("seda://get-say-hello"));
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ context.getPropertiesComponent().addInitialProperty("mypath", "hello");
+
+ restConfiguration().host("localhost");
+
+ rest("/say/{{mypath}}").get().to("direct:hello");
+
+ from("direct:hello").log("Hello");
+
+ }
+ };
+ }
+}
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_5.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_5.adoc
index 3c4353b4af7..1911e336948 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_5.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_5.adoc
@@ -40,9 +40,36 @@ instead.
Camel has changed the default value for `inlineRoutes=false` to `inlineRoutes=false` in `restConfiguration`.
It is very typical to define Rest DSL and for each service api, then call a Camel route via `direct` endpoints.
By inlining these two, then you only have 1 route in Camel instead of 2. This helps reduce the clutter of routes
-that otherwise is in use when using Rest DSL and many services.
+that otherwise is in use when using Rest DSL and many services. You can restore to old behaviour by setting the option back to `inlineRoutes=false`.
+
+Rest DSL will now eager resolve property placeholders that are used during building the `rest:` endpoint.
+
+For example with a Rest DSL using a placeholder (`app.mypath = helloapp`) in the `path`:
+
+[source,yaml]
+----
+- rest:
+ path: "{{app.mypath}}"
+ post:
+ - to: direct:demo
+----
+
+Will not be resolved in the `rest` endpoint which can be seen during startup logging:
+
+[source,text]
+----
+Routes startup (total:2)
+Started demo (rest://post:%7B%7Bapp.mypath%7D%7D)
+----
+
+The placeholder is now resolved eager and you will see _nicer_ startup logs such as:
+
+[source,text]
+----
+Routes startup (total:2)
+Started demo (rest://post:helloapp)
+----
-You can restore to old behaviour by setting the option back to `inlineRoutes=false`.
=== Intercept EIP