You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2022/03/02 19:43:57 UTC

[camel] branch CAMEL-17555/flag-routes created (now 8f76345)

This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a change to branch CAMEL-17555/flag-routes
in repository https://gitbox.apache.org/repos/asf/camel.git.


      at 8f76345  CAMEL-17555: camel-core - Flag to include/exclude routes in route templates

This branch includes the following new commits:

     new 8f76345  CAMEL-17555: camel-core - Flag to include/exclude routes in route templates

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[camel] 01/01: CAMEL-17555: camel-core - Flag to include/exclude routes in route templates

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a commit to branch CAMEL-17555/flag-routes
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 8f76345f741189424bd05d9216735bc96e5b874e
Author: Nicolas Filotto <nf...@talend.com>
AuthorDate: Wed Mar 2 20:38:00 2022 +0100

    CAMEL-17555: camel-core - Flag to include/exclude routes in route templates
---
 .../apache/camel/catalog/schemas/camel-spring.xsd  |   1 +
 .../org/apache/camel/impl/DefaultCamelContext.java |  68 ++++++++++---
 .../org/apache/camel/model/RouteDefinition.java    |  31 ++++++
 .../camel/model/RouteTemplateDefinition.java       |   2 +-
 .../builder/RouteTemplatePreconditionTest.java     |  92 ++++++++++++++++++
 .../camel/processor/RoutePreconditionTest.java     | 106 +++++++++++++++++++++
 .../java/org/apache/camel/xml/in/ModelParser.java  |   1 +
 docs/user-manual/modules/ROOT/pages/routes.adoc    |  12 +++
 .../deserializers/RouteDefinitionDeserializer.java |   4 +
 .../src/generated/resources/camel-yaml-dsl.json    |   3 +
 .../src/generated/resources/camelYamlDsl.json      |   3 +
 .../org/apache/camel/dsl/yaml/RoutesTest.groovy    |  27 ++++++
 12 files changed, 336 insertions(+), 14 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index b600499..c1742f1 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -10762,6 +10762,7 @@ Whether message history is enabled on this route. Default value: true
             ]]></xs:documentation>
           </xs:annotation>
         </xs:attribute>
+        <xs:attribute name="precondition" type="xs:string"/>
         <xs:attribute name="rest" type="xs:boolean"/>
         <xs:attribute name="routeConfigurationId" type="xs:string"/>
         <xs:attribute name="routePolicyRef" type="xs:string">
diff --git a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
index dbe23bf..6b38235 100644
--- a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
+++ b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultCamelContext.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.impl;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -64,6 +65,7 @@ import org.apache.camel.model.RoutesDefinition;
 import org.apache.camel.model.TemplatedRouteDefinition;
 import org.apache.camel.model.cloud.ServiceCallConfigurationDefinition;
 import org.apache.camel.model.language.ExpressionDefinition;
+import org.apache.camel.model.language.SimpleExpression;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.rest.RestsDefinition;
 import org.apache.camel.model.transformer.TransformerDefinition;
@@ -83,6 +85,7 @@ import org.apache.camel.spi.Transformer;
 import org.apache.camel.spi.UuidGenerator;
 import org.apache.camel.spi.Validator;
 import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.support.DefaultExchange;
 import org.apache.camel.support.DefaultRegistry;
 import org.apache.camel.support.LocalBeanRegistry;
 import org.apache.camel.support.SimpleUuidGenerator;
@@ -796,6 +799,7 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
         }
         try {
             RouteDefinitionHelper.forceAssignIds(getCamelContextReference(), routeDefinitions);
+            List<RouteDefinition> routeDefinitionsToRemove = null;
             for (RouteDefinition routeDefinition : routeDefinitions) {
                 // assign ids to the routes and validate that the id's is all unique
                 String duplicate = RouteDefinitionHelper.validateUniqueIds(routeDefinition, routeDefinitions);
@@ -871,28 +875,40 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
                     // need to reset auto assigned ids, so there is no clash when creating routes
                     ProcessorDefinitionHelper.resetAllAutoAssignedNodeIds(routeDefinition);
                 }
+                // Check if the route is included
+                if (includedRoute(routeDefinition)) {
+                    // must ensure route is prepared, before we can start it
+                    if (!routeDefinition.isPrepared()) {
+                        RouteDefinitionHelper.prepareRoute(getCamelContextReference(), routeDefinition);
+                        routeDefinition.markPrepared();
+                    }
 
-                // must ensure route is prepared, before we can start it
-                if (!routeDefinition.isPrepared()) {
-                    RouteDefinitionHelper.prepareRoute(getCamelContextReference(), routeDefinition);
-                    routeDefinition.markPrepared();
+                    StartupStepRecorder recorder
+                            = getCamelContextReference().adapt(ExtendedCamelContext.class).getStartupStepRecorder();
+                    StartupStep step = recorder.beginStep(Route.class, routeDefinition.getRouteId(), "Create Route");
+                    Route route = model.getModelReifierFactory().createRoute(this, routeDefinition);
+                    recorder.endStep(step);
+
+                    RouteService routeService = new RouteService(route);
+                    startRouteService(routeService, true);
+                } else {
+                    // Add the definition to the list of definitions to remove as the route is excluded
+                    if (routeDefinitionsToRemove == null) {
+                        routeDefinitionsToRemove = new ArrayList<>(routeDefinitions.size());
+                    }
+                    routeDefinitionsToRemove.add(routeDefinition);
                 }
 
-                StartupStepRecorder recorder
-                        = getCamelContextReference().adapt(ExtendedCamelContext.class).getStartupStepRecorder();
-                StartupStep step = recorder.beginStep(Route.class, routeDefinition.getRouteId(), "Create Route");
-                Route route = model.getModelReifierFactory().createRoute(this, routeDefinition);
-                recorder.endStep(step);
-
-                RouteService routeService = new RouteService(route);
-                startRouteService(routeService, true);
-
                 // clear local after the route is created via the reifier
                 pc.setLocalProperties(null);
                 if (localBeans != null) {
                     localBeans.setLocalBeanRepository(null);
                 }
             }
+            if (routeDefinitionsToRemove != null) {
+                // Remove all the excluded routes
+                model.removeRouteDefinitions(routeDefinitionsToRemove);
+            }
         } finally {
             if (!alreadyStartingRoutes) {
                 setStartingRoutes(false);
@@ -975,6 +991,32 @@ public class DefaultCamelContext extends SimpleCamelContext implements ModelCame
         return removed;
     }
 
+    /**
+     * Indicates whether the route should be included according to the precondition.
+     *
+     * @param  definition the definition of the route to check.
+     * @return            {@code true} if the route should be included, {@code false} otherwise.
+     */
+    private boolean includedRoute(RouteDefinition definition) {
+        final String precondition = definition.getPrecondition();
+        if (precondition == null) {
+            LOG.debug("No precondition found, the route is included by default");
+            return true;
+        }
+        final ExpressionDefinition expression = new SimpleExpression(precondition);
+        expression.initPredicate(this);
+
+        Predicate predicate = expression.getPredicate();
+        predicate.initPredicate(this);
+
+        boolean matches = predicate.matches(new DefaultExchange(this));
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("The precondition has been evaluated to {}, thus the route is {}", matches,
+                    matches ? "included" : "excluded");
+        }
+        return matches;
+    }
+
     private static ValueHolder<String> createTransformerKey(TransformerDefinition def) {
         return ObjectHelper.isNotEmpty(def.getScheme())
                 ? new TransformerKey(def.getScheme())
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
index 2444d35..d35ec6b 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteDefinition.java
@@ -90,6 +90,7 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition> implement
     private Map<String, Object> templateParameters;
     private RouteTemplateContext routeTemplateContext;
     private Resource resource;
+    private String precondition;
 
     public RouteDefinition() {
     }
@@ -457,6 +458,18 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition> implement
     }
 
     /**
+     * Sets the expression of the precondition in simple language to evaluate to determine if this route should be
+     * included or not.
+     *
+     * @param  precondition the expression corresponding to the test to evaluate.
+     * @return              the builder
+     */
+    public RouteDefinition precondition(String precondition) {
+        setPrecondition(precondition);
+        return this;
+    }
+
+    /**
      * Configures the startup order for this route
      * <p/>
      * Camel will reorder routes and star them ordered by 0..N where 0 is the lowest number and N the highest number.
@@ -913,6 +926,24 @@ public class RouteDefinition extends OutputDefinition<RouteDefinition> implement
     }
 
     /**
+     * The expression of the precondition in simple language to evaluate to determine if this given route should be
+     * included or not.
+     */
+    public String getPrecondition() {
+        return precondition;
+    }
+
+    /**
+     * The expression of the precondition in simple language to evaluate to determine if this route should be included
+     * or not.
+     */
+    @XmlAttribute
+    @Metadata(label = "advanced")
+    public void setPrecondition(String precondition) {
+        this.precondition = precondition;
+    }
+
+    /**
      * To configure the ordering of the routes being started
      */
     public Integer getStartupOrder() {
diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
index 8e69ebd..f700c69 100644
--- a/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
+++ b/core/camel-core-model/src/main/java/org/apache/camel/model/RouteTemplateDefinition.java
@@ -417,7 +417,7 @@ public class RouteTemplateDefinition extends OptionalIdentifiedDefinition {
         } else {
             copy.setDescription(getDescription());
         }
-
+        copy.setPrecondition(route.getPrecondition());
         return copy;
     }
 
diff --git a/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePreconditionTest.java b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePreconditionTest.java
new file mode 100644
index 0000000..ba35b01
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/builder/RouteTemplatePreconditionTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.builder;
+
+import org.apache.camel.ContextTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The test ensuring that the precondition set on a rule determines if the route is included or not
+ */
+class RouteTemplatePreconditionTest extends ContextTestSupport {
+
+    @Test
+    void testRouteIncluded() throws Exception {
+        TemplatedRouteBuilder.builder(context, "myTemplateWithPrecondition")
+                .parameter("protocol", "json")
+                .routeId("myRoute")
+                .add();
+
+        assertCollectionSize(context.getRouteDefinitions(), 1);
+        assertCollectionSize(context.getRoutes(), 1);
+        assertNotNull(context.getRoute("myRoute"));
+
+        getMockEndpoint("mock:out").expectedMessageCount(1);
+
+        template.sendBody("direct:in", "Hello Included Route");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    void testRouteExcluded() {
+        TemplatedRouteBuilder.builder(context, "myTemplateWithPrecondition")
+                .parameter("protocol", "avro")
+                .routeId("myRoute")
+                .add();
+
+        assertCollectionSize(context.getRouteDefinitions(), 0);
+        assertCollectionSize(context.getRoutes(), 0);
+        assertNull(context.getRoute("myRoute"));
+    }
+
+    @Test
+    void testRouteIncludedByDefault() throws Exception {
+        TemplatedRouteBuilder.builder(context, "myTemplateWithoutPrecondition")
+                .routeId("myRoute")
+                .add();
+
+        assertCollectionSize(context.getRouteDefinitions(), 1);
+        assertCollectionSize(context.getRoutes(), 1);
+        assertNotNull(context.getRoute("myRoute"));
+
+        getMockEndpoint("mock:out").expectedMessageCount(1);
+
+        template.sendBody("direct:in", "Hello Included Route");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() throws Exception {
+        return new RouteBuilder() {
+            @Override
+            public void configure() throws Exception {
+                routeTemplate("myTemplateWithPrecondition")
+                        .templateParameter("protocol")
+                        .from("direct:in").precondition("'{{protocol}}' == 'json'")
+                        .to("mock:out");
+                routeTemplate("myTemplateWithoutPrecondition")
+                        .from("direct:in")
+                        .to("mock:out");
+            }
+        };
+    }
+}
diff --git a/core/camel-core/src/test/java/org/apache/camel/processor/RoutePreconditionTest.java b/core/camel-core/src/test/java/org/apache/camel/processor/RoutePreconditionTest.java
new file mode 100644
index 0000000..508fa3c
--- /dev/null
+++ b/core/camel-core/src/test/java/org/apache/camel/processor/RoutePreconditionTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.processor;
+
+import java.util.Properties;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * The test ensuring that the precondition set on a rule determines if the route is included or not
+ */
+class RoutePreconditionTest extends ContextTestSupport {
+
+    @Override
+    public boolean isUseRouteBuilder() {
+        return false;
+    }
+
+    @Test
+    void testRouteIncluded() throws Exception {
+        Properties init = new Properties();
+        init.setProperty("protocol", "json");
+        context.getPropertiesComponent().setInitialProperties(init);
+
+        context.addRoutes(createRouteBuilder());
+        context.start();
+
+        assertCollectionSize(context.getRouteDefinitions(), 2);
+        assertCollectionSize(context.getRoutes(), 2);
+        assertNotNull(context.getRoute("myRoute"));
+        assertNotNull(context.getRoute("myRouteNP"));
+
+        getMockEndpoint("mock:out").expectedMessageCount(1);
+
+        template.sendBody("direct:in", "Hello Included Route");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Test
+    void testRouteExcluded() throws Exception {
+        Properties init = new Properties();
+        init.setProperty("protocol", "avro");
+        context.getPropertiesComponent().setInitialProperties(init);
+
+        context.addRoutes(createRouteBuilder());
+        context.start();
+
+        assertCollectionSize(context.getRouteDefinitions(), 1);
+        assertCollectionSize(context.getRoutes(), 1);
+        assertNull(context.getRoute("myRoute"));
+        assertNotNull(context.getRoute("myRouteNP"));
+    }
+
+    @Test
+    void testRouteIncludedByDefault() throws Exception {
+        Properties init = new Properties();
+        init.setProperty("protocol", "foo");
+        context.getPropertiesComponent().setInitialProperties(init);
+
+        context.addRoutes(createRouteBuilder());
+        context.start();
+
+        assertCollectionSize(context.getRouteDefinitions(), 1);
+        assertCollectionSize(context.getRoutes(), 1);
+        assertNotNull(context.getRoute("myRouteNP"));
+
+        getMockEndpoint("mock:outNP").expectedMessageCount(1);
+
+        template.sendBody("direct:inNP", "Hello Included Route");
+
+        assertMockEndpointsSatisfied();
+    }
+
+    @Override
+    protected RouteBuilder createRouteBuilder() {
+        return new RouteBuilder() {
+            public void configure() {
+                from("direct:in").routeId("myRoute").precondition("'{{protocol}}' == 'json'")
+                        .to("mock:out");
+                from("direct:inNP").routeId("myRouteNP")
+                        .to("mock:outNP");
+            }
+        };
+    }
+
+}
diff --git a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 5b9047b..b6d537ef 100644
--- a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -1048,6 +1048,7 @@ public class ModelParser extends BaseParser {
                 case "group": def.setGroup(val); break;
                 case "logMask": def.setLogMask(val); break;
                 case "messageHistory": def.setMessageHistory(val); break;
+                case "precondition": def.setPrecondition(val); break;
                 case "routeConfigurationId": def.setRouteConfigurationId(val); break;
                 case "routePolicyRef": def.setRoutePolicyRef(val); break;
                 case "shutdownRoute": def.setShutdownRoute(val); break;
diff --git a/docs/user-manual/modules/ROOT/pages/routes.adoc b/docs/user-manual/modules/ROOT/pages/routes.adoc
index 5aca179..00d2e1f 100644
--- a/docs/user-manual/modules/ROOT/pages/routes.adoc
+++ b/docs/user-manual/modules/ROOT/pages/routes.adoc
@@ -54,6 +54,18 @@ rb -> rb.from("kafka:cheese").to("jms:queue:foo");
 There is a bit more to this as the lambda route must be coded in a Java method that returns an instance of `LambdaRouteBuilder`.
 See more at the xref:lambda-route-builder.adoc[LambdaRouteBuilder] documentation.
 
+== Route Precondition
+
+The routes can be included or not according to the result of a test expressed in simple language that is evaluated only once during the initialization phase.
+
+In the next example, the route is only included if the parameter format has been set to `json`.
+[source,java]
+----
+from("direct:in").precondition("'{{format}}' == 'json'")
+   .unmarshal().json()
+   .to("direct:out");
+----
+
 == More Information
 
 See xref:route-builder.adoc[RouteBuilder] and xref:dsl.adoc[DSL] for a list of supported languages you can use for coding Camel routes.
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
index 18fc980..8e49bb1 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/RouteDefinitionDeserializer.java
@@ -38,6 +38,7 @@ import org.snakeyaml.engine.v2.nodes.NodeTuple;
           properties = {
                   @YamlProperty(name = "id", type = "string"),
                   @YamlProperty(name = "description", type = "string"),
+                  @YamlProperty(name = "precondition", type = "string"),
                   @YamlProperty(name = "group", type = "string"),
                   @YamlProperty(name = "route-configuration-id", type = "string"),
                   @YamlProperty(name = "from", type = "object:org.apache.camel.model.FromDefinition", required = true)
@@ -70,6 +71,9 @@ public class RouteDefinitionDeserializer extends YamlDeserializerBase<RouteDefin
                 case "description":
                     target.setDescription(new DescriptionDefinition(asText(val)));
                     break;
+                case "precondition":
+                    target.setPrecondition(asText(val));
+                    break;
                 case "group":
                     target.setGroup(asText(val));
                     break;
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
index ac4fdc1..9ca79c9 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camel-yaml-dsl.json
@@ -2514,6 +2514,9 @@
           "id" : {
             "type" : "string"
           },
+          "precondition" : {
+            "type" : "string"
+          },
           "route-configuration-id" : {
             "type" : "string"
           }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
index cf4c823..f67dbf4 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/camelYamlDsl.json
@@ -2415,6 +2415,9 @@
           "id" : {
             "type" : "string"
           },
+          "precondition" : {
+            "type" : "string"
+          },
           "routeConfigurationId" : {
             "type" : "string"
           }
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
index c2d7abf..baeb051e 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesTest.groovy
@@ -186,4 +186,31 @@ class RoutesTest extends YamlTestSupport {
             }
         }
     }
+
+    def "load route description with precondition"() {
+        when:
+        loadRoutes '''
+                - route:
+                    id: demo-route
+                    description: something cool
+                    precondition: "{{?red}}"
+                    from:
+                      uri: "direct:info"
+                      steps:
+                        - log: "message"
+            '''
+        then:
+        context.routeDefinitions.size() == 1
+
+        with(context.routeDefinitions[0], RouteDefinition) {
+            routeId == 'demo-route'
+            description.text == 'something cool'
+            input.endpointUri == 'direct:info'
+            precondition == '{{?red}}'
+
+            with (outputs[0], LogDefinition) {
+                message == 'message'
+            }
+        }
+    }
 }