This is an automated email from the ASF dual-hosted git repository. lburgazzoli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/camel.git
commit 536d50806833e6f2c22f9b3dc18173a34a4cfc91 Author: Luca Burgazzoli <[email protected]> AuthorDate: Wed Apr 14 20:04:26 2021 +0200 [CAMEL-16510] YAML Dsl : support for Kamelet EIP --- .../org/apache/camel/model/KameletDefinition.java | 4 + .../camel-yaml-dsl-deserializers/pom.xml | 1 + .../dsl/yaml/deserializers/ModelDeserializers.java | 47 ----- .../deserializers/ModelDeserializersResolver.java | 2 - .../dsl/yaml/deserializers/CustomResolver.java | 5 +- .../yaml/deserializers/KameletDeserializer.java | 102 ++++++++++ dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml | 5 + .../src/generated/resources/camel-yaml-dsl.json | 33 ++-- .../org/apache/camel/dsl/yaml/KameletTest.groovy | 215 +++++++++++++++++++++ .../camel/dsl/yaml/support/YamlTestSupport.groovy | 40 +++- 10 files changed, 386 insertions(+), 68 deletions(-) diff --git a/core/camel-core-model/src/main/java/org/apache/camel/model/KameletDefinition.java b/core/camel-core-model/src/main/java/org/apache/camel/model/KameletDefinition.java index f4c1625..1f1364a 100644 --- a/core/camel-core-model/src/main/java/org/apache/camel/model/KameletDefinition.java +++ b/core/camel-core-model/src/main/java/org/apache/camel/model/KameletDefinition.java @@ -37,6 +37,10 @@ public class KameletDefinition extends OutputDefinition<KameletDefinition> { public KameletDefinition() { } + public KameletDefinition(String name) { + this.name = name; + } + @Override public String toString() { return "Kamelet[" + getOutputs() + "]"; diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/pom.xml b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/pom.xml index a3b85bf..8a7dd45 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/pom.xml +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/pom.xml @@ -128,6 +128,7 @@ <bannedDefinition>org.apache.camel.model.language.ExpressionDefinition</bannedDefinition> <bannedDefinition>org.apache.camel.model.ExpressionSubElementDefinition</bannedDefinition> <bannedDefinition>org.apache.camel.model.PropertyDefinitions</bannedDefinition> + <bannedDefinition>org.apache.camel.model.KameletDefinition</bannedDefinition> </bannedDefinitions> <additionalDefinitions> <additionalDefinition>org.apache.camel.model.SagaOptionDefinition</additionalDefinition> diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java index f12f10d..df21838 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java @@ -35,7 +35,6 @@ import org.apache.camel.model.InputTypeDefinition; import org.apache.camel.model.InterceptDefinition; import org.apache.camel.model.InterceptFromDefinition; import org.apache.camel.model.InterceptSendToEndpointDefinition; -import org.apache.camel.model.KameletDefinition; import org.apache.camel.model.LoadBalanceDefinition; import org.apache.camel.model.LoadBalancerDefinition; import org.apache.camel.model.LogDefinition; @@ -6830,52 +6829,6 @@ public final class ModelDeserializers extends YamlDeserializerSupport { } @YamlType( - types = org.apache.camel.model.KameletDefinition.class, - order = org.apache.camel.dsl.yaml.common.YamlDeserializerResolver.ORDER_LOWEST - 1, - nodes = "kamelet", - properties = { - @YamlProperty(name = "inherit-error-handler", type = "boolean"), - @YamlProperty(name = "name", type = "string", required = true), - @YamlProperty(name = "steps", type = "array:org.apache.camel.model.ProcessorDefinition") - } - ) - public static class KameletDefinitionDeserializer extends YamlDeserializerBase<KameletDefinition> { - public KameletDefinitionDeserializer() { - super(KameletDefinition.class); - } - - @Override - protected KameletDefinition newInstance() { - return new KameletDefinition(); - } - - @Override - protected boolean setProperty(KameletDefinition target, String propertyKey, - String propertyName, Node node) { - switch(propertyKey) { - case "inherit-error-handler": { - String val = asText(node); - target.setInheritErrorHandler(java.lang.Boolean.valueOf(val)); - break; - } - case "name": { - String val = asText(node); - target.setName(val); - break; - } - case "steps": { - setSteps(target, node);; - break; - } - default: { - return false; - } - } - return true; - } - } - - @YamlType( types = org.apache.camel.model.cloud.KubernetesServiceCallServiceDiscoveryConfiguration.class, order = org.apache.camel.dsl.yaml.common.YamlDeserializerResolver.ORDER_LOWEST - 1, nodes = "kubernetes-service-discovery", diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java index 513a1a9..0a1741b 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializersResolver.java @@ -167,8 +167,6 @@ public final class ModelDeserializersResolver implements YamlDeserializerResolve case "org.apache.camel.model.dataformat.JsonDataFormat": return new ModelDeserializers.JsonDataFormatDeserializer(); case "jsonpath": return new ModelDeserializers.JsonPathExpressionDeserializer(); case "org.apache.camel.model.language.JsonPathExpression": return new ModelDeserializers.JsonPathExpressionDeserializer(); - case "kamelet": return new ModelDeserializers.KameletDefinitionDeserializer(); - case "org.apache.camel.model.KameletDefinition": return new ModelDeserializers.KameletDefinitionDeserializer(); case "kubernetes-service-discovery": return new ModelDeserializers.KubernetesServiceCallServiceDiscoveryConfigurationDeserializer(); case "org.apache.camel.model.cloud.KubernetesServiceCallServiceDiscoveryConfiguration": return new ModelDeserializers.KubernetesServiceCallServiceDiscoveryConfigurationDeserializer(); case "lzf": return new ModelDeserializers.LZFDataFormatDeserializer(); diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/CustomResolver.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/CustomResolver.java index 16b29bb..448cf74 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/CustomResolver.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/CustomResolver.java @@ -54,10 +54,11 @@ public class CustomResolver implements YamlDeserializerResolver { return new BeansDeserializer(); case "error-handler": return new ErrorHandlerBuilderDeserializer(); - //case "do-try": - // return new TryDefinitionDeserializer(); case "org.apache.camel.model.ProcessorDefinition": return new ProcessorDefinitionDeserializer(); + case "kamelet": + case "org.apache.camel.model.KameletDefinition": + return new KameletDeserializer(); default: return null; } diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/KameletDeserializer.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/KameletDeserializer.java new file mode 100644 index 0000000..6f1c1dd --- /dev/null +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/KameletDeserializer.java @@ -0,0 +1,102 @@ +/* + * 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.deserializers; + +import java.net.URISyntaxException; +import java.util.Map; + +import org.apache.camel.dsl.yaml.common.YamlDeserializationContext; +import org.apache.camel.dsl.yaml.common.YamlDeserializerBase; +import org.apache.camel.dsl.yaml.common.YamlDeserializerResolver; +import org.apache.camel.dsl.yaml.common.exception.UnsupportedFieldException; +import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException; +import org.apache.camel.model.KameletDefinition; +import org.apache.camel.spi.annotations.YamlProperty; +import org.apache.camel.spi.annotations.YamlType; +import org.apache.camel.util.URISupport; +import org.snakeyaml.engine.v2.nodes.MappingNode; +import org.snakeyaml.engine.v2.nodes.Node; +import org.snakeyaml.engine.v2.nodes.NodeTuple; + +@YamlType( + inline = true, + types = org.apache.camel.model.KameletDefinition.class, + order = YamlDeserializerResolver.ORDER_DEFAULT, + nodes = "kamelet", + properties = { + @YamlProperty(name = "inherit-error-handler", type = "boolean"), + @YamlProperty(name = "name", type = "string", required = true), + @YamlProperty(name = "parameters", type = "object"), + @YamlProperty(name = "steps", type = "array:org.apache.camel.model.ProcessorDefinition") + }) +public class KameletDeserializer extends YamlDeserializerBase<KameletDefinition> { + public KameletDeserializer() { + super(KameletDefinition.class); + } + + @Override + protected KameletDefinition newInstance() { + return new KameletDefinition(); + } + + @Override + protected KameletDefinition newInstance(String value) { + return new KameletDefinition(value); + } + + @Override + protected void setProperties(KameletDefinition target, MappingNode node) { + final YamlDeserializationContext dc = getDeserializationContext(node); + + String name = null; + Map<String, Object> parameters = null; + + for (NodeTuple tuple : node.getValue()) { + final String key = asText(tuple.getKeyNode()); + final Node val = tuple.getValueNode(); + + setDeserializationContext(val, dc); + + switch (key) { + case "steps": + setSteps(target, val); + break; + case "id": + target.setId(asText(val)); + break; + case "name": + name = asText(val); + break; + case "parameters": + parameters = asScalarMap(tuple.getValueNode()); + break; + default: + throw new UnsupportedFieldException(node, key); + } + } + + if (parameters != null) { + try { + name += "?" + URISupport.createQueryString(parameters, false); + } catch (URISyntaxException e) { + throw new YamlDeserializationException(e); + } + } + + target.setName(name); + } +} diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml b/dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml index 54169ce..9d850e9 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/pom.xml @@ -129,6 +129,11 @@ <artifactId>camel-seda</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-kamelet</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> 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 5aa0842..1d2caf5 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 @@ -1036,21 +1036,28 @@ "required" : [ "uri" ] }, "org.apache.camel.model.KameletDefinition" : { - "type" : "object", - "properties" : { - "inherit-error-handler" : { - "type" : "boolean" - }, - "name" : { - "type" : "string" - }, - "steps" : { - "type" : "array", - "items" : { - "$ref" : "#/items/definitions/org.apache.camel.model.ProcessorDefinition" + "oneOf" : [ { + "type" : "string" + }, { + "type" : "object", + "properties" : { + "inherit-error-handler" : { + "type" : "boolean" + }, + "name" : { + "type" : "string" + }, + "parameters" : { + "type" : "object" + }, + "steps" : { + "type" : "array", + "items" : { + "$ref" : "#/items/definitions/org.apache.camel.model.ProcessorDefinition" + } } } - }, + } ], "required" : [ "name" ] }, "org.apache.camel.model.LoadBalanceDefinition" : { diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletTest.groovy new file mode 100644 index 0000000..b1ff173 --- /dev/null +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletTest.groovy @@ -0,0 +1,215 @@ +/* + * 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.component.mock.MockEndpoint +import org.apache.camel.dsl.yaml.common.YamlDeserializationMode +import org.apache.camel.dsl.yaml.support.YamlTestSupport +import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy +import org.apache.camel.spi.Resource + +class KameletTest extends YamlTestSupport { + @Override + def doSetup() { + context.start() + } + + def "kamelet (#resource)"(Tuple2<YamlDeserializationMode, Resource> resource) { + setup: + addTemplate('setPayload') { + from('kamelet:source') + .setBody().simple('${body}: {{payload}}') + } + + setFlowMode(resource[0] as YamlDeserializationMode) + loadRoutes(resource[1] as Resource) + + withMock('mock:kamelet') { + expectedMessageCount 1 + expectedBodiesReceived 'a: 1' + } + when: + withTemplate { + to('direct:start').withBody('a').send() + } + + then: + MockEndpoint.assertIsSatisfied(context) + + where: + resource << [ + new Tuple2<YamlDeserializationMode, Resource>( + YamlDeserializationMode.CLASSIC, + asResource('inline', ''' + - from: + uri: "direct:start" + steps: + - kamelet: "setPayload?payload=1" + - to: "mock:kamelet" + ''')), + new Tuple2<YamlDeserializationMode, Resource>( + YamlDeserializationMode.CLASSIC, + asResource('name', ''' + - from: + uri: "direct:start" + steps: + - kamelet: + name: "setPayload?payload=1" + - to: "mock:kamelet" + ''')), + new Tuple2<YamlDeserializationMode, Resource>( + YamlDeserializationMode.CLASSIC, + asResource('name_with_steps', ''' + - from: + uri: "direct:start" + steps: + - kamelet: + name: "setPayload?payload=1" + steps: + - to: "mock:kamelet" + ''')), + new Tuple2<YamlDeserializationMode, Resource>( + YamlDeserializationMode.CLASSIC, + asResource('properties', ''' + - from: + uri: "direct:start" + steps: + - kamelet: + name: "setPayload" + parameters: + payload: 1 + - to: "mock:kamelet" + ''')), + new Tuple2<YamlDeserializationMode, Resource>( + YamlDeserializationMode.CLASSIC, + asResource('properties_with_steps', ''' + - from: + uri: "direct:start" + steps: + - kamelet: + name: "setPayload" + parameters: + payload: 1 + steps: + - to: "mock:kamelet" + ''')), + new Tuple2<YamlDeserializationMode, Resource>( + YamlDeserializationMode.FLOW, + asResource('inline', ''' + - from: + uri: "direct:start" + steps: + - kamelet: "setPayload?payload=1" + - to: "mock:kamelet" + ''')), + new Tuple2<YamlDeserializationMode, Resource>( + YamlDeserializationMode.FLOW, + asResource('name', ''' + - from: + uri: "direct:start" + steps: + - kamelet: + name: "setPayload?payload=1" + - to: "mock:kamelet" + ''')), + new Tuple2<YamlDeserializationMode, Resource>( + YamlDeserializationMode.FLOW, + asResource('properties', ''' + - from: + uri: "direct:start" + steps: + - kamelet: + name: "setPayload" + parameters: + payload: 1 + - to: "mock:kamelet" + ''')) + ] + } + + def "kamelet (aggregation)"() { + setup: + addTemplate('aggregate') { + from('kamelet:source') + .aggregate() + .simple('${header.StockSymbol}') + .aggregationStrategy(new UseLatestAggregationStrategy()) + .completionSize("{{size}}") + .to("kamelet:sink") + } + + loadRoutes ''' + - from: + uri: "direct:route" + steps: + - kamelet: + name: aggregate?size=2 + steps: + - to: "mock:result" + ''' + + withMock('mock:result') { + expectedBodiesReceived '2', '4' + } + + when: + withTemplate { + to('direct:route').withBody('1').withHeader('StockSymbol', 1).send() + to('direct:route').withBody('2').withHeader('StockSymbol', 1).send() + to('direct:route').withBody('3').withHeader('StockSymbol', 2).send() + to('direct:route').withBody('4').withHeader('StockSymbol', 2).send() + } + then: + MockEndpoint.assertIsSatisfied(context) + } + + def "kamelet (aggregation with flow)"() { + setup: + setFlowMode(YamlDeserializationMode.FLOW) + + addTemplate('aggregate') { + from('kamelet:source') + .aggregate() + .simple('${header.StockSymbol}') + .aggregationStrategy(new UseLatestAggregationStrategy()) + .completionSize("{{size}}") + .to("kamelet:sink") + } + + loadRoutes ''' + - from: + uri: "direct:route" + steps: + - kamelet: aggregate?size=2 + - to: "mock:result" + ''' + + withMock('mock:result') { + expectedBodiesReceived '2', '4' + } + + when: + withTemplate { + to('direct:route').withBody('1').withHeader('StockSymbol', 1).send() + to('direct:route').withBody('2').withHeader('StockSymbol', 1).send() + to('direct:route').withBody('3').withHeader('StockSymbol', 2).send() + to('direct:route').withBody('4').withHeader('StockSymbol', 2).send() + } + then: + MockEndpoint.assertIsSatisfied(context) + } +} 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 1aedc0d..bbffe8d 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 @@ -23,16 +23,20 @@ import com.github.fge.jsonschema.main.JsonSchemaFactory import groovy.util.logging.Slf4j import org.apache.camel.CamelContext import org.apache.camel.FluentProducerTemplate +import org.apache.camel.builder.RouteBuilder import org.apache.camel.component.mock.MockEndpoint import org.apache.camel.dsl.yaml.YamlRoutesBuilderLoader import org.apache.camel.dsl.yaml.common.YamlDeserializationMode import org.apache.camel.impl.DefaultCamelContext +import org.apache.camel.model.RouteTemplateDefinition import org.apache.camel.spi.HasCamelContext import org.apache.camel.spi.Resource import org.apache.camel.support.ResourceHelper import spock.lang.AutoCleanup import spock.lang.Specification +import java.nio.charset.StandardCharsets + @Slf4j class YamlTestSupport extends Specification implements HasCamelContext { static def MAPPER = new ObjectMapper(new YAMLFactory()) @@ -56,6 +60,17 @@ class YamlTestSupport extends Specification implements HasCamelContext { context.routesLoader.loadRoutes(resources) } + def addTemplate(String name, @DelegatesTo(RouteTemplateDefinition) Closure<?> closure) { + context.addRoutes(new RouteBuilder() { + @Override + void configure() throws Exception { + closure.resolveStrategy = Closure.DELEGATE_FIRST + closure.delegate = routeTemplate(name) + closure.call() + } + }); + } + def loadRoutes(Resource... resources) { loadRoutes(resources.toList()) } @@ -92,10 +107,27 @@ class YamlTestSupport extends Specification implements HasCamelContext { } static Resource asResource(String location, String content) { - return ResourceHelper.fromString( - location.endsWith('.yaml') ? location : location + '.yaml', - content.stripIndent() - ) + return new Resource() { + @Override + String getLocation() { + return location.endsWith('.yaml') ? location : location + '.yaml' + } + + @Override + boolean exists() { + return false + } + + @Override + InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(content.stripIndent().getBytes(StandardCharsets.UTF_8)) + } + + @Override + String toString() { + return location + } + } } def setFlowMode(YamlDeserializationMode mode) {
