You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by cd...@apache.org on 2023/07/04 11:56:49 UTC

[camel] 02/03: CAMEL-18698 Enhance YAML DSL loader to support input/output data types

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

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

commit 2d8812eefe92451ddb91179e7abe14d283a6acc4
Author: Christoph Deppisch <cd...@redhat.com>
AuthorDate: Wed Jun 28 08:56:04 2023 +0200

    CAMEL-18698 Enhance YAML DSL loader to support input/output data types
    
    - Enable users to specify input/output data types on route and KameletBinding
    - Automatically adds transformation steps and/or input/output data type specifications to the Camel route
---
 .../deserializers/RouteDefinitionDeserializer.java | 12 +++
 .../camel/dsl/yaml/YamlRoutesBuilderLoader.java    | 26 ++++++
 .../camel/dsl/yaml/KameletBindingLoaderTest.groovy | 97 ++++++++++++++++++++++
 .../org/apache/camel/dsl/yaml/RoutesTest.groovy    | 27 ++++++
 .../org/apache/camel/dsl/yaml/TransformTest.groovy | 51 ++++++++++++
 5 files changed, 213 insertions(+)

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 38ba442f082..3369c745d4d 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
@@ -21,6 +21,8 @@ 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.model.FromDefinition;
+import org.apache.camel.model.InputTypeDefinition;
+import org.apache.camel.model.OutputTypeDefinition;
 import org.apache.camel.model.RouteDefinition;
 import org.apache.camel.spi.annotations.YamlIn;
 import org.apache.camel.spi.annotations.YamlProperty;
@@ -48,6 +50,8 @@ import org.snakeyaml.engine.v2.nodes.NodeTuple;
                   @YamlProperty(name = "message-history", type = "boolean"),
                   @YamlProperty(name = "log-mask", type = "boolean"),
                   @YamlProperty(name = "trace", type = "boolean"),
+                  @YamlProperty(name = "input-type", type = "object:org.apache.camel.model.InputTypeDefinition"),
+                  @YamlProperty(name = "output-type", type = "object:org.apache.camel.model.OutputTypeDefinition"),
                   @YamlProperty(name = "from", type = "object:org.apache.camel.model.FromDefinition", required = true)
           })
 public class RouteDefinitionDeserializer extends YamlDeserializerBase<RouteDefinition> {
@@ -119,6 +123,14 @@ public class RouteDefinitionDeserializer extends YamlDeserializerBase<RouteDefin
                 case "trace":
                     target.setTrace(asText(val));
                     break;
+                case "inputType":
+                case "input-type":
+                    target.setInputType(asType(val, InputTypeDefinition.class));
+                    break;
+                case "outputType":
+                case "output-type":
+                    target.setOutputType(asType(val, OutputTypeDefinition.class));
+                    break;
                 case "from":
                     val.setProperty(RouteDefinition.class.getName(), target);
                     target.setInput(asType(val, FromDefinition.class));
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 87eecb3f346..dd6a306aef7 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
@@ -57,6 +57,7 @@ import org.apache.camel.model.rest.RestConfigurationDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.rest.VerbDefinition;
 import org.apache.camel.spi.CamelContextCustomizer;
+import org.apache.camel.spi.DataType;
 import org.apache.camel.spi.DependencyStrategy;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.annotations.RoutesLoader;
@@ -648,6 +649,19 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
                     }
                 }
 
+                MappingNode dataTypes = asMappingNode(nodeAt(source, "/dataTypes"));
+                if (dataTypes != null) {
+                    MappingNode in = asMappingNode(nodeAt(dataTypes, "/in"));
+                    if (in != null) {
+                        route.inputType(extractTupleValue(in.getValue(), "format"));
+                    }
+
+                    MappingNode out = asMappingNode(nodeAt(dataTypes, "/out"));
+                    if (out != null) {
+                        route.transform(new DataType(extractTupleValue(out.getValue(), "format")));
+                    }
+                }
+
                 // steps in the middle (optional)
                 Node steps = nodeAt(root, "/spec/steps");
                 if (steps != null) {
@@ -683,6 +697,18 @@ public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
                 }
 
                 if (sink != null) {
+                    dataTypes = asMappingNode(nodeAt(sink, "/dataTypes"));
+                    if (dataTypes != null) {
+                        MappingNode in = asMappingNode(nodeAt(dataTypes, "/in"));
+                        if (in != null) {
+                            route.transform(new DataType(extractTupleValue(in.getValue(), "format")));
+                        }
+
+                        MappingNode out = asMappingNode(nodeAt(dataTypes, "/out"));
+                        if (out != null) {
+                            route.outputType(extractTupleValue(out.getValue(), "format"));
+                        }
+                    }
 
                     // sink is at the end (mandatory)
                     line = -1;
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletBindingLoaderTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletBindingLoaderTest.groovy
index 2a15541d526..30454f5c188 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletBindingLoaderTest.groovy
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletBindingLoaderTest.groovy
@@ -22,6 +22,7 @@ import org.apache.camel.component.mock.MockEndpoint
 import org.apache.camel.dsl.yaml.support.YamlTestSupport
 import org.apache.camel.model.KameletDefinition
 import org.apache.camel.model.ToDefinition
+import org.apache.camel.model.TransformDefinition
 import org.apache.camel.model.errorhandler.DeadLetterChannelDefinition
 import org.apache.camel.model.errorhandler.DefaultErrorHandlerDefinition
 import org.apache.camel.model.errorhandler.NoErrorHandlerDefinition
@@ -657,4 +658,100 @@ class KameletBindingLoaderTest extends YamlTestSupport {
         }
     }
 
+    def "kamelet binding with input/output data types"() {
+        when:
+        loadBindings('''
+                apiVersion: camel.apache.org/v1alpha1
+                kind: KameletBinding
+                metadata:
+                  name: timer-event-source                  
+                spec:
+                  source:
+                    ref:
+                      kind: Kamelet
+                      apiVersion: camel.apache.org/v1
+                      name: timer-source
+                    dataTypes:
+                      in:
+                        format: plain/text
+                    properties:
+                      message: "Hello world!"
+                  sink:
+                    ref:
+                      kind: Kamelet
+                      apiVersion: camel.apache.org/v1
+                      name: log-sink
+                    dataTypes:
+                      out:
+                        format: application/octet-stream   
+            ''')
+        then:
+        context.routeDefinitions.size() == 3
+
+        with (context.routeDefinitions[0]) {
+            routeId == 'timer-event-source'
+            input.endpointUri == 'kamelet:timer-source?message=Hello+world%21'
+            input.lineNumber == 7
+            inputType.urn == 'plain/text'
+            outputType.urn == 'application/octet-stream'
+            outputs.size() == 1
+            with (outputs[0], ToDefinition) {
+                endpointUri == 'kamelet:log-sink'
+                lineNumber == 17
+            }
+        }
+    }
+
+    def "kamelet binding with data type transformation"() {
+        when:
+        loadBindings('''
+                apiVersion: camel.apache.org/v1alpha1
+                kind: KameletBinding
+                metadata:
+                  name: timer-event-source                  
+                spec:
+                  source:
+                    ref:
+                      kind: Kamelet
+                      apiVersion: camel.apache.org/v1
+                      name: timer-source
+                    dataTypes:
+                      out:
+                        format: application/octet-stream    
+                    properties:
+                      message: "Hello world!"
+                  sink:
+                    ref:
+                      kind: Kamelet
+                      apiVersion: camel.apache.org/v1
+                      name: log-sink
+                    dataTypes:
+                      in:
+                        format: plain/text
+            ''')
+        then:
+        context.routeDefinitions.size() == 3
+
+        with (context.routeDefinitions[0]) {
+            routeId == 'timer-event-source'
+            input.endpointUri == 'kamelet:timer-source?message=Hello+world%21'
+            input.lineNumber == 7
+            outputs.size() == 3
+            with (outputs[0], TransformDefinition) {
+                fromType == 'camel:any'
+                toType == 'application/octet-stream'
+                lineNumber == -1
+            }
+            with (outputs[1], TransformDefinition) {
+                fromType == 'camel:any'
+                toType == 'plain/text'
+                lineNumber == -1
+            }
+            with (outputs[2], ToDefinition) {
+                endpointUri == 'kamelet:log-sink'
+                lineNumber == 17
+            }
+        }
+    }
+
 }
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 983782d5577..3bfe4494be4 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
@@ -164,6 +164,33 @@ class RoutesTest extends YamlTestSupport {
             }
     }
 
+    def "load route with input/output types"() {
+        when:
+        loadRoutes '''
+                - route:
+                    inputType: 
+                      urn: "plain/text"
+                    outputType: 
+                      urn: "application/octet-stream"
+                    from: 
+                      uri: "direct:info"
+                      steps:
+                        - log: "message"
+            '''
+        then:
+        context.routeDefinitions.size() == 1
+
+        with(context.routeDefinitions[0], RouteDefinition) {
+            inputType.urn == 'plain/text'
+            outputType.urn == 'application/octet-stream'
+
+            input.endpointUri == 'direct:info'
+            with (outputs[0], LogDefinition) {
+                message == 'message'
+            }
+        }
+    }
+
     def "load route inlined"() {
         when:
         loadRoutes '''
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/TransformTest.groovy b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/TransformTest.groovy
new file mode 100644
index 00000000000..32fd939de05
--- /dev/null
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/TransformTest.groovy
@@ -0,0 +1,51 @@
+/*
+ * 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.StepDefinition
+import org.apache.camel.model.ToDefinition
+import org.apache.camel.model.TransformDefinition
+
+class TransformTest extends YamlTestSupport {
+
+    def "transform with data types"() {
+        when:
+            loadRoutes '''
+                - from:
+                    uri: "direct:start"
+                    steps:    
+                      - step:
+                          steps:
+                            - transform:
+                                fromType: "plain-text"
+                                toType: "application-octet-stream"
+                      - to: "mock:result"    
+                             
+            '''
+        then:
+            with(context.routeDefinitions[0].outputs[0], StepDefinition) {
+                with(outputs[0], TransformDefinition) {
+                    fromType == 'plain-text'
+                    toType == 'application-octet-stream'
+                }
+            }
+            with(context.routeDefinitions[0].outputs[1], ToDefinition) {
+                endpointUri == 'mock:result'
+            }
+    }
+}