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