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()) }