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

davsclaus pushed a commit to branch back
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 8a8dd70271f518bf1dacb1ac24a05186e3a56fe0
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Fri May 13 10:45:10 2022 +0200

    CAMEL-18109: camel-yaml-dsl - Backwards compatible with route and steps
---
 .../camel/dsl/yaml/YamlRoutesBuilderLoader.java    |  49 ++++-
 .../camel/dsl/yaml/IntegrationLoaderTest.groovy    | 104 ++++++++++
 .../dsl/yaml/RoutesBackwardsCompatibleTest.groovy  | 220 +++++++++++++++++++++
 .../camel/dsl/yaml/support/YamlTestSupport.groovy  |  26 ++-
 4 files changed, 391 insertions(+), 8 deletions(-)

diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
index 6e965242975..f63b5eb4d71 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoader.java
@@ -67,12 +67,15 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.snakeyaml.engine.v2.api.LoadSettings;
 import org.snakeyaml.engine.v2.api.YamlUnicodeReader;
+import org.snakeyaml.engine.v2.common.ScalarStyle;
 import org.snakeyaml.engine.v2.composer.Composer;
 import org.snakeyaml.engine.v2.nodes.MappingNode;
 import org.snakeyaml.engine.v2.nodes.Node;
 import org.snakeyaml.engine.v2.nodes.NodeTuple;
 import org.snakeyaml.engine.v2.nodes.NodeType;
+import org.snakeyaml.engine.v2.nodes.ScalarNode;
 import org.snakeyaml.engine.v2.nodes.SequenceNode;
+import org.snakeyaml.engine.v2.nodes.Tag;
 import org.snakeyaml.engine.v2.parser.Parser;
 import org.snakeyaml.engine.v2.parser.ParserImpl;
 import org.snakeyaml.engine.v2.scanner.StreamReader;
@@ -248,7 +251,8 @@ public class YamlRoutesBuilderLoader extends 
YamlRoutesBuilderLoaderSupport {
     }
 
     private Object preConfigureNode(Node root, YamlDeserializationContext ctx, 
boolean preParse) {
-        Object target = root;
+        // backwards compatible fixes
+        Object target = routeBackwardsCompatible(root, ctx, preParse);
 
         // check if the yaml is a camel-k yaml with embedded binding/routes 
(called flow(s))
         if (Objects.equals(root.getNodeType(), NodeType.MAPPING)) {
@@ -270,6 +274,48 @@ public class YamlRoutesBuilderLoader extends 
YamlRoutesBuilderLoaderSupport {
         return target;
     }
 
+    private Object routeBackwardsCompatible(Node root, 
YamlDeserializationContext ctx, boolean preParse) {
+        Object target = root;
+
+        // look for every route (the root must be a sequence)
+        if (!isSequenceNode(root)) {
+            return target;
+        }
+
+        SequenceNode seq = asSequenceNode(root);
+        for (Node node : seq.getValue()) {
+            Node route = nodeAt(node, "/route");
+            if (route != null) {
+                // backwards compatible steps are under route, but should be 
under from
+                Node steps = nodeAt(route, "/steps");
+                if (steps != null) {
+                    int line = -1;
+                    if (steps.getStartMark().isPresent()) {
+                        line = steps.getStartMark().get().getLine();
+                    }
+                    String file = ctx.getResource().getLocation();
+                    LOG.warn("Deprecated route/steps detected in {}:{}", file, 
line
+                                                                               
+ ". To migrate move route/steps to route/from/steps");
+                    // move steps from route to from
+                    Node from = nodeAt(route, "/from");
+                    MappingNode mn = asMappingNode(from);
+                    if (mn != null && mn.getValue() != null) {
+                        ScalarNode sn = new ScalarNode(Tag.STR, "steps", 
ScalarStyle.PLAIN);
+                        NodeTuple nt = new NodeTuple(sn, steps);
+                        mn.getValue().add(nt);
+                        mn = asMappingNode(route);
+                        mn.getValue().removeIf(t -> {
+                            String k = asText(t.getKeyNode());
+                            return "steps".equals(k);
+                        });
+                    }
+                }
+            }
+        }
+
+        return target;
+    }
+
     /**
      * Camel K Integration file
      */
@@ -321,6 +367,7 @@ public class YamlRoutesBuilderLoader extends 
YamlRoutesBuilderLoaderSupport {
                 if (routes.getNodeType() != NodeType.SEQUENCE) {
                     throw new InvalidNodeTypeException(routes, 
NodeType.SEQUENCE);
                 }
+                routes = (Node) routeBackwardsCompatible(routes, ctx, 
preParse);
                 answer.add(routes);
             }
         }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/IntegrationLoaderTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/IntegrationLoaderTest.groovy
index f1b61f8bf00..f714804f2ee 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/IntegrationLoaderTest.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/IntegrationLoaderTest.groovy
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.yaml
 
 import org.apache.camel.dsl.yaml.support.YamlTestSupport
 import org.apache.camel.model.LogDefinition
+import org.apache.camel.model.ToDefinition
 import org.apache.camel.spi.PropertiesComponent
 
 class IntegrationLoaderTest extends YamlTestSupport {
@@ -128,4 +129,107 @@ class IntegrationLoaderTest extends YamlTestSupport {
         pc.resolveProperty("TEST_MESSAGE").get() == "Hello World"
     }
 
+    def "integration"() {
+        when:
+        loadIntegrations('''
+                apiVersion: camel.apache.org/v1
+                kind: Integration
+                metadata:
+                  name: foobar3
+                spec:
+                  flows:
+                    - from:
+                        uri: "seda:foo"
+                        steps:    
+                          - to: "mock:result"   
+                          ''')
+        then:
+        context.routeDefinitions.size() == 1
+
+        with(context.routeDefinitions[0].outputs[0], ToDefinition) {
+            uri == "mock:result"
+        }
+    }
+
+    def "integrationTwo"() {
+        when:
+        loadIntegrations('''
+                apiVersion: camel.apache.org/v1
+                kind: Integration
+                metadata:
+                  name: foobar3
+                spec:
+                  flows:
+                    - from:
+                        uri: "seda:foo"
+                        steps:    
+                          - to: "mock:result"   
+                    - from:
+                        uri: "seda:foo2"
+                        steps:    
+                          - to: "mock:result2"   
+                          ''')
+        then:
+        context.routeDefinitions.size() == 2
+
+        with(context.routeDefinitions[0].outputs[0], ToDefinition) {
+            uri == "mock:result"
+        }
+        with(context.routeDefinitions[1].outputs[0], ToDefinition) {
+            uri == "mock:result2"
+        }
+    }
+
+    def "integration with route"() {
+        when:
+        loadIntegrations('''
+                apiVersion: camel.apache.org/v1
+                kind: Integration
+                metadata:
+                  name: foobar3
+                spec:
+                  flows:
+                    - route:
+                        id: myRoute
+                        from:
+                          uri: "seda:foo"
+                          steps:    
+                            - to: "mock:result"   
+                          ''')
+        then:
+        context.routeDefinitions.size() == 1
+
+        context.routeDefinitions[0].id == "myRoute"
+
+        with(context.routeDefinitions[0].outputs[0], ToDefinition) {
+            uri == "mock:result"
+        }
+    }
+
+    def "integration with route backward"() {
+        when:
+        loadIntegrations('''
+                apiVersion: camel.apache.org/v1
+                kind: Integration
+                metadata:
+                  name: foobar3
+                spec:
+                  flows:
+                    - route:
+                        id: myRoute2
+                        from:
+                          uri: "seda:foo"
+                        steps:    
+                          - to: "mock:result"   
+                          ''')
+        then:
+        context.routeDefinitions.size() == 1
+
+        context.routeDefinitions[0].id == "myRoute2"
+
+        with(context.routeDefinitions[0].outputs[0], ToDefinition) {
+            uri == "mock:result"
+        }
+    }
+
 }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesBackwardsCompatibleTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesBackwardsCompatibleTest.groovy
new file mode 100644
index 00000000000..f7d7789c7c8
--- /dev/null
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/RoutesBackwardsCompatibleTest.groovy
@@ -0,0 +1,220 @@
+/*
+ * 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.dsl.yaml
+
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+import org.apache.camel.model.LogDefinition
+import org.apache.camel.model.RouteDefinition
+
+class RoutesBackwardsCompatibleTest extends YamlTestSupport {
+
+    def "load from"() {
+        when:
+            loadRoutes '''
+                - from:
+                    uri: "direct:info"
+                    steps:
+                    - log: "message"
+            '''
+        then:
+            context.routeDefinitions.size() == 1
+
+            with(context.routeDefinitions[0], RouteDefinition) {
+                input.endpointUri == 'direct:info'
+
+                with (outputs[0], LogDefinition) {
+                    message == 'message'
+                }
+            }
+    }
+
+    def "load from with parameters"() {
+        when:
+            loadRoutes '''
+                - from:
+                    uri: "direct:info"
+                    parameters:
+                      timeout: 1234    
+                    steps:
+                    - log: "message"
+            '''
+        then:
+            context.routeDefinitions.size() == 1
+
+            with(context.routeDefinitions[0], RouteDefinition) {
+                input.endpointUri == 'direct:info?timeout=1234'
+
+                with (outputs[0], LogDefinition) {
+                    message == 'message'
+                }
+            }
+    }
+
+    def "load multi from "() {
+        when:
+            loadRoutes '''
+                - from:
+                    uri: "direct:1"
+                    steps:
+                    - log: "1"
+                - from:
+                    uri: "direct:2"
+                    steps:
+                    - log: "2"
+            '''
+        then:
+            context.routeDefinitions.size() == 2
+
+            with(context.routeDefinitions[0], RouteDefinition) {
+                input.endpointUri == 'direct:1'
+
+                with (outputs[0], LogDefinition) {
+                    message == '1'
+                }
+            }
+            with(context.routeDefinitions[1], RouteDefinition) {
+                input.endpointUri == 'direct:2'
+
+                with (outputs[0], LogDefinition) {
+                    message == '2'
+                }
+            }
+    }
+
+    def "load route"() {
+        when:
+            loadRoutesNoValidate '''
+                - route:
+                    from:
+                      uri: "direct:info"
+                    steps:
+                      - log: "message"
+            '''
+        then:
+            context.routeDefinitions.size() == 1
+
+            with(context.routeDefinitions[0], RouteDefinition) {
+                input.endpointUri == 'direct:info'
+
+                with (outputs[0], LogDefinition) {
+                    message == 'message'
+                }
+            }
+    }
+
+    def "load route with parameters"() {
+        when:
+            loadRoutesNoValidate '''
+                - route:
+                    from:
+                      uri: "direct:info"
+                      parameters:
+                        timeout: 1234
+                    steps:
+                      - log: "message"
+            '''
+        then:
+            context.routeDefinitions.size() == 1
+
+            with(context.routeDefinitions[0], RouteDefinition) {
+                input.endpointUri == 'direct:info?timeout=1234'
+
+                with (outputs[0], LogDefinition) {
+                    message == 'message'
+                }
+            }
+    }
+
+    def "load route inlined"() {
+        when:
+            loadRoutesNoValidate '''
+                - route:
+                    id: demo-route
+                    stream-caching: true
+                    auto-startup: false
+                    from:
+                      uri: "direct:info"
+                    steps:
+                      - log: "message"
+            '''
+        then:
+        context.routeDefinitions.size() == 1
+
+        with(context.routeDefinitions[0], RouteDefinition) {
+            routeId == 'demo-route'
+            streamCache == 'true'
+            autoStartup == 'false'
+            input.endpointUri == 'direct:info'
+
+            with (outputs[0], LogDefinition) {
+                message == 'message'
+            }
+        }
+    }
+
+    def "load route description"() {
+        when:
+            loadRoutesNoValidate '''
+                - route:
+                    id: demo-route
+                    description: something cool
+                    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'
+
+            with (outputs[0], LogDefinition) {
+                message == 'message'
+            }
+        }
+    }
+
+    def "load route description with precondition"() {
+        when:
+            loadRoutesNoValidate '''
+                - 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'
+            }
+        }
+    }
+}
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/YamlTestSupport.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/YamlTestSupport.groovy
index bfd3b77aefc..176165e89f7 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/YamlTestSupport.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/YamlTestSupport.groovy
@@ -47,13 +47,15 @@ class YamlTestSupport extends Specification implements 
HasCamelContext {
     @AutoCleanup
     def context = new DefaultCamelContext()
 
-    def loadRoutes(Collection<Resource> resources) {
-        for (def resource: resources) {
-            def target = MAPPER.readTree(resource.inputStream)
-            def report = SCHEMA.validate(target)
-
-            if (!report.isSuccess()) {
-                throw new IllegalArgumentException("${report}")
+    def loadRoutes(Collection<Resource> resources, boolean validate = true) {
+        if (validate) {
+            for (def resource : resources) {
+                def target = MAPPER.readTree(resource.inputStream)
+                def report = SCHEMA.validate(target)
+
+                if (!report.isSuccess()) {
+                    throw new IllegalArgumentException("${report}")
+                }
             }
         }
 
@@ -85,6 +87,16 @@ class YamlTestSupport extends Specification implements 
HasCamelContext {
         )
     }
 
+    def loadRoutesNoValidate(String... resources) {
+        int index = 0
+
+        loadRoutes(
+            resources.collect {
+                it -> ResourceHelper.fromString("route-${index++}.yaml", 
it.stripIndent())
+            }, false
+        )
+    }
+
     def loadKamelets(Resource... resources) {
         loadKamelets(resources.toList())
     }

Reply via email to