This is an automated email from the ASF dual-hosted git repository.
lburgazzoli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 9701170 CAMEL-16570: make it possible to discover kamelets at runtime
9701170 is described below
commit 97011703af164659ca0fcd19e3e0c9b2717eabf6
Author: Luca Burgazzoli <[email protected]>
AuthorDate: Fri May 7 18:15:13 2021 +0200
CAMEL-16570: make it possible to discover kamelets at runtime
---
.../camel/catalog/docs/kamelet-component.adoc | 3 +-
.../kamelet/KameletComponentConfigurer.java | 3 +
.../apache/camel/component/kamelet/kamelet.json | 1 +
.../src/main/docs/kamelet-component.adoc | 9 +-
.../apache/camel/component/kamelet/Kamelet.java | 1 +
.../camel/component/kamelet/KameletComponent.java | 45 ++++-
.../component/kamelet/KameletDiscoveryTest.java | 100 ++++++++++
.../camel/impl/engine/DefaultRoutesLoader.java | 2 +-
.../dsl/KameletComponentBuilderFactory.java | 16 ++
.../modules/ROOT/pages/kamelet-component.adoc | 3 +-
.../yaml/common/YamlDeserializationContext.java | 17 ++
.../dsl/yaml/common/YamlDeserializerSupport.java | 54 ++++++
.../org/apache/camel/routes-loader/kamelet.yaml | 2 +
.../camel/dsl/yaml/KameletRoutesBuilderLoader.java | 73 ++++++++
.../camel/dsl/yaml/YamlRoutesBuilderLoader.java | 137 ++++----------
.../dsl/yaml/YamlRoutesBuilderLoaderSupport.java | 126 +++++++++++++
.../apache/camel/dsl/yaml/KameletLoaderTest.groovy | 208 +++++++++++++++++++++
.../camel/dsl/yaml/support/YamlTestSupport.groovy | 11 +-
.../test/resources/kamelets/mySetBody.kamelet.yaml | 32 ++++
19 files changed, 730 insertions(+), 113 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-component.adoc
index 40ad18a..e9168ca 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-component.adoc
@@ -27,13 +27,14 @@ kamelet:templateId/routeId[?options]
// component options: START
-The Kamelet component supports 7 options, which are listed below.
+The Kamelet component supports 8 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
| Name | Description | Default | Type
+| *location* (common) | The location of the Kamelets on the file system. |
classpath:/kamelets | String
| *routeProperties* (common) | Set route local parameters. | | Map
| *templateProperties* (common) | Set template local parameters. | | Map
| *bridgeErrorHandler* (consumer) | Allows for bridging the consumer to the
Camel routing Error Handler, which mean any exceptions occurred while the
consumer is trying to pickup incoming messages, or the likes, will now be
processed as a message and handled by the routing Error Handler. By default the
consumer will use the org.apache.camel.spi.ExceptionHandler to deal with
exceptions, that will be logged at WARN or ERROR level and ignored. | false |
boolean
diff --git
a/components/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletComponentConfigurer.java
b/components/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletComponentConfigurer.java
index 238976e..1cc8ff6 100644
---
a/components/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletComponentConfigurer.java
+++
b/components/camel-kamelet/src/generated/java/org/apache/camel/component/kamelet/KameletComponentConfigurer.java
@@ -28,6 +28,7 @@ public class KameletComponentConfigurer extends
PropertyConfigurerSupport implem
case "bridgeErrorHandler":
target.setBridgeErrorHandler(property(camelContext, boolean.class, value));
return true;
case "lazystartproducer":
case "lazyStartProducer":
target.setLazyStartProducer(property(camelContext, boolean.class, value));
return true;
+ case "location": target.setLocation(property(camelContext,
java.lang.String.class, value)); return true;
case "routeproperties":
case "routeProperties":
target.setRouteProperties(property(camelContext, java.util.Map.class, value));
return true;
case "templateproperties":
@@ -47,6 +48,7 @@ public class KameletComponentConfigurer extends
PropertyConfigurerSupport implem
case "bridgeErrorHandler": return boolean.class;
case "lazystartproducer":
case "lazyStartProducer": return boolean.class;
+ case "location": return java.lang.String.class;
case "routeproperties":
case "routeProperties": return java.util.Map.class;
case "templateproperties":
@@ -67,6 +69,7 @@ public class KameletComponentConfigurer extends
PropertyConfigurerSupport implem
case "bridgeErrorHandler": return target.isBridgeErrorHandler();
case "lazystartproducer":
case "lazyStartProducer": return target.isLazyStartProducer();
+ case "location": return target.getLocation();
case "routeproperties":
case "routeProperties": return target.getRouteProperties();
case "templateproperties":
diff --git
a/components/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json
b/components/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json
index 36dd720..819ae57 100644
---
a/components/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json
+++
b/components/camel-kamelet/src/generated/resources/org/apache/camel/component/kamelet/kamelet.json
@@ -22,6 +22,7 @@
"lenientProperties": true
},
"componentProperties": {
+ "location": { "kind": "property", "displayName": "Location", "group":
"common", "label": "", "required": false, "type": "string", "javaType":
"java.lang.String", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "classpath:\/kamelets", "description": "The location of the
Kamelets on the file system." },
"routeProperties": { "kind": "property", "displayName": "Route
Properties", "group": "common", "label": "", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.util.Properties>",
"deprecated": false, "autowired": false, "secret": false, "description": "Set
route local parameters." },
"templateProperties": { "kind": "property", "displayName": "Template
Properties", "group": "common", "label": "", "required": false, "type":
"object", "javaType": "java.util.Map<java.lang.String, java.util.Properties>",
"deprecated": false, "autowired": false, "secret": false, "description": "Set
template local parameters." },
"bridgeErrorHandler": { "kind": "property", "displayName": "Bridge Error
Handler", "group": "consumer", "label": "consumer", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": false, "description": "Allows for bridging the
consumer to the Camel routing Error Handler, which mean any exceptions occurred
while the consumer is trying to pickup incoming messages, or the likes, will
now be processed as a me [...]
diff --git a/components/camel-kamelet/src/main/docs/kamelet-component.adoc
b/components/camel-kamelet/src/main/docs/kamelet-component.adoc
index 40ad18a..be9dd23 100644
--- a/components/camel-kamelet/src/main/docs/kamelet-component.adoc
+++ b/components/camel-kamelet/src/main/docs/kamelet-component.adoc
@@ -27,13 +27,14 @@ kamelet:templateId/routeId[?options]
// component options: START
-The Kamelet component supports 7 options, which are listed below.
+The Kamelet component supports 8 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
| Name | Description | Default | Type
+| *location* (common) | The location of the Kamelets on the file system. |
classpath:/kamelets | String
| *routeProperties* (common) | Set route local parameters. | | Map
| *templateProperties* (common) | Set template local parameters. | | Map
| *bridgeErrorHandler* (consumer) | Allows for bridging the consumer to the
Camel routing Error Handler, which mean any exceptions occurred while the
consumer is trying to pickup incoming messages, or the likes, will now be
processed as a message and handled by the routing Error Handler. By default the
consumer will use the org.apache.camel.spi.ExceptionHandler to deal with
exceptions, that will be logged at WARN or ERROR level and ignored. | false |
boolean
@@ -85,9 +86,13 @@ with the following path and query parameters:
[NOTE]
====
-The *kamelet* endpoint is *lenient*, which means that the endpoint accepts
additional parameters that are passed to the ROute Templkate engine and
consumed upon route materialization.
+The *kamelet* endpoint is *lenient*, which means that the endpoint accepts
additional parameters that are passed to the
xref:manual::route-template.adoc[Route Template] engine and consumed upon route
materialization.
====
+== Discovery
+
+If a xref:manual::route-template.adoc[Route Template] is not found, the
*kamelet* endpoint tries to load the related *kamelet* definition from the file
system (by default `classpath:/kamelets`). The default resolution mechanism
expect kamelet files to have the extension `.kamelet.yaml`.
+
== Samples
diff --git
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
index 98626ae..18aa658 100644
---
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
+++
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java
@@ -42,6 +42,7 @@ public final class Kamelet {
public static final String SINK_ID = "sink";
public static final String PARAM_ROUTE_ID = "routeId";
public static final String PARAM_TEMPLATE_ID = "templateId";
+ public static final String DEFAULT_LOCATION = "classpath:/kamelets";
// use a running counter as uuid
private static final UuidGenerator UUID = new SimpleUuidGenerator();
diff --git
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
index 6053bed..d7b17c3 100644
---
a/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
+++
b/components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/KameletComponent.java
@@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.camel.CamelContext;
import org.apache.camel.Endpoint;
+import org.apache.camel.ExtendedCamelContext;
import org.apache.camel.Processor;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.VetoCamelContextStartException;
@@ -73,6 +74,8 @@ public class KameletComponent extends DefaultComponent {
private Map<String, Properties> templateProperties;
@Metadata
private Map<String, Properties> routeProperties;
+ @Metadata(defaultValue = Kamelet.DEFAULT_LOCATION)
+ private String location = Kamelet.DEFAULT_LOCATION;
public KameletComponent() {
}
@@ -263,6 +266,17 @@ public class KameletComponent extends DefaultComponent {
this.routeProperties = routeProperties;
}
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * The location of the Kamelets on the file system.
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
int getStateCounter() {
return stateCounter;
}
@@ -339,7 +353,7 @@ public class KameletComponent extends DefaultComponent {
* Once the camel context is initialized all the endpoint tracked by this
LifecycleHandler will
* be used to create routes from templates.
*/
- private static class LifecycleHandler extends LifecycleStrategySupport {
+ private class LifecycleHandler extends LifecycleStrategySupport {
private final List<KameletEndpoint> endpoints;
private final AtomicBoolean initialized;
@@ -348,19 +362,36 @@ public class KameletComponent extends DefaultComponent {
this.initialized = new AtomicBoolean();
}
- public static void createRouteForEndpoint(KameletEndpoint endpoint)
throws Exception {
- LOGGER.debug("Creating route from template={} and id={}",
endpoint.getTemplateId(), endpoint.getRouteId());
+ public void createRouteForEndpoint(KameletEndpoint endpoint) throws
Exception {
+ final ExtendedCamelContext ecc =
getCamelContext().adapt(ExtendedCamelContext.class);
+ final ModelCamelContext context =
getCamelContext().adapt(ModelCamelContext.class);
+ final String templateId = endpoint.getTemplateId();
+ final String routeId = endpoint.getRouteId();
+
+ if (context.getRouteTemplateDefinition(templateId) == null) {
+ LOGGER.debug("Loading route template={} from {}", templateId,
getLocation());
+
+ String path = getLocation();
+ if (path != null) {
+ if (!path.endsWith("/")) {
+ path += "/";
+ }
+
+ ecc.getRoutesLoader().loadRoutes(
+ ecc.getResourceLoader().resolveResource(path +
templateId + ".kamelet.yaml"));
+ }
+ }
+
+ LOGGER.debug("Creating route from template={} and id={}",
templateId, routeId);
- final ModelCamelContext context =
endpoint.getCamelContext().adapt(ModelCamelContext.class);
- final String id =
context.addRouteFromTemplate(endpoint.getRouteId(), endpoint.getTemplateId(),
- endpoint.getKameletProperties());
+ final String id = context.addRouteFromTemplate(routeId,
templateId, endpoint.getKameletProperties());
final RouteDefinition def = context.getRouteDefinition(id);
if (!def.isPrepared()) {
context.startRouteDefinitions(Collections.singletonList(def));
}
- LOGGER.debug("Route with id={} created from template={}", id,
endpoint.getTemplateId());
+ LOGGER.debug("Route with id={} created from template={}", id,
templateId);
}
@Override
diff --git
a/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletDiscoveryTest.java
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletDiscoveryTest.java
new file mode 100644
index 0000000..aee9463
--- /dev/null
+++
b/components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletDiscoveryTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.component.kamelet;
+
+import org.apache.camel.FailedToCreateRouteException;
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.impl.engine.DefaultRoutesLoader;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.support.RoutesBuilderLoaderSupport;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class KameletDiscoveryTest extends CamelTestSupport {
+
+ @Test
+ public void kameletCanBeDiscovered() throws Exception {
+ context.getRegistry().bind(
+ DefaultRoutesLoader.ROUTES_LOADER_KEY_PREFIX + "kamelet.yaml",
+ new RoutesBuilderLoaderSupport() {
+ @Override
+ public String getSupportedExtension() {
+ return "kamelet.yaml";
+ }
+
+ @Override
+ public RoutesBuilder loadRoutesBuilder(Resource resource)
throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ routeTemplate("mySetBody")
+ .from("kamelet:source")
+ .setBody().constant("discovered");
+
+ }
+ };
+ }
+ });
+
+ context.addRoutes(new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:discovery")
+ .toF("kamelet:mySetBody");
+ }
+ });
+
+
assertThat(fluentTemplate.to("direct:discovery").request(String.class)).isEqualTo("discovered");
+ }
+
+ @Test
+ public void kameletNotFound() throws Exception {
+ context.getRegistry().bind(
+ DefaultRoutesLoader.ROUTES_LOADER_KEY_PREFIX + "kamelet.yaml",
+ new RoutesBuilderLoaderSupport() {
+ @Override
+ public String getSupportedExtension() {
+ return "kamelet.yaml";
+ }
+
+ @Override
+ public RoutesBuilder loadRoutesBuilder(Resource resource)
throws Exception {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ }
+ };
+ }
+ });
+
+ RouteBuilder builder = new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:discovery")
+ .toF("kamelet:mySetBody");
+ }
+ };
+
+ assertThatThrownBy(() -> context.addRoutes(builder))
+ .isInstanceOf(FailedToCreateRouteException.class)
+ .hasRootCauseMessage("Cannot find RouteTemplate with id
mySetBody");
+ }
+}
diff --git
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java
index 86c959a..e060d8f 100644
---
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java
+++
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultRoutesLoader.java
@@ -84,7 +84,7 @@ public class DefaultRoutesLoader extends ServiceSupport
implements RoutesLoader
for (Resource resource : resources) {
// the loader to use is derived from the file extension
- final String extension = FileUtil.onlyExt(resource.getLocation(),
true);
+ final String extension = FileUtil.onlyExt(resource.getLocation(),
false);
if (ObjectHelper.isEmpty(extension)) {
throw new IllegalArgumentException(
diff --git
a/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KameletComponentBuilderFactory.java
b/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KameletComponentBuilderFactory.java
index d62f9bb..7c5a4b1 100644
---
a/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KameletComponentBuilderFactory.java
+++
b/core/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KameletComponentBuilderFactory.java
@@ -51,6 +51,21 @@ public interface KameletComponentBuilderFactory {
extends
ComponentBuilder<KameletComponent> {
/**
+ * The location of the Kamelets on the file system.
+ *
+ * The option is a: <code>java.lang.String</code> type.
+ *
+ * Default: classpath:/kamelets
+ * Group: common
+ *
+ * @param location the value to set
+ * @return the dsl builder
+ */
+ default KameletComponentBuilder location(java.lang.String location) {
+ doSetProperty("location", location);
+ return this;
+ }
+ /**
* Set route local parameters.
*
* The option is a: <code>java.util.Map&lt;java.lang.String,
@@ -198,6 +213,7 @@ public interface KameletComponentBuilderFactory {
String name,
Object value) {
switch (name) {
+ case "location": ((KameletComponent)
component).setLocation((java.lang.String) value); return true;
case "routeProperties": ((KameletComponent)
component).setRouteProperties((java.util.Map) value); return true;
case "templateProperties": ((KameletComponent)
component).setTemplateProperties((java.util.Map) value); return true;
case "bridgeErrorHandler": ((KameletComponent)
component).setBridgeErrorHandler((boolean) value); return true;
diff --git a/docs/components/modules/ROOT/pages/kamelet-component.adoc
b/docs/components/modules/ROOT/pages/kamelet-component.adoc
index 26a21ff..4357148 100644
--- a/docs/components/modules/ROOT/pages/kamelet-component.adoc
+++ b/docs/components/modules/ROOT/pages/kamelet-component.adoc
@@ -29,13 +29,14 @@ kamelet:templateId/routeId[?options]
// component options: START
-The Kamelet component supports 7 options, which are listed below.
+The Kamelet component supports 8 options, which are listed below.
[width="100%",cols="2,5,^1,2",options="header"]
|===
| Name | Description | Default | Type
+| *location* (common) | The location of the Kamelets on the file system. |
classpath:/kamelets | String
| *routeProperties* (common) | Set route local parameters. | | Map
| *templateProperties* (common) | Set template local parameters. | | Map
| *bridgeErrorHandler* (consumer) | Allows for bridging the consumer to the
Camel routing Error Handler, which mean any exceptions occurred while the
consumer is trying to pickup incoming messages, or the likes, will now be
processed as a message and handled by the routing Error Handler. By default the
consumer will use the org.apache.camel.spi.ExceptionHandler to deal with
exceptions, that will be logged at WARN or ERROR level and ignored. | false |
boolean
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializationContext.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializationContext.java
index cb20da3..f0a42dc 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializationContext.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializationContext.java
@@ -87,6 +87,10 @@ public class YamlDeserializationContext extends
StandardConstructor implements C
this.camelContext = camelContext.adapt(ExtendedCamelContext.class);
}
+ public Object constructDocument(Node node) {
+ return super.construct(node);
+ }
+
@Override
protected Optional<ConstructNode> findConstructorFor(Node node) {
ConstructNode ctor = resolve(node);
@@ -141,6 +145,19 @@ public class YamlDeserializationContext extends
StandardConstructor implements C
return type.cast(result);
}
+ public <T> T construct(Node node, Class<T> type) {
+ ConstructNode constructor = resolve(type);
+ if (constructor == null) {
+ throw new YamlDeserializationException("Unable to find constructor
for node: " + node);
+ }
+ Object result = constructor.construct(node);
+ if (result == null) {
+ return null;
+ }
+
+ return type.cast(result);
+ }
+
// *********************************
//
// Resolve
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerSupport.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerSupport.java
index 48cade8..8ddd141 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerSupport.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerSupport.java
@@ -78,14 +78,26 @@ public class YamlDeserializerSupport {
}
public static byte[] asByteArray(Node node) {
+ if (node == null) {
+ return null;
+ }
+
return asByteArray(asText(node));
}
public static Class<?> asClass(Node node) {
+ if (node == null) {
+ return null;
+ }
+
return asClass(asText(node));
}
public static List<String> asStringList(Node node) {
+ if (node == null) {
+ return null;
+ }
+
List<String> answer;
if (node.getNodeType() == NodeType.SCALAR) {
@@ -103,14 +115,25 @@ public class YamlDeserializerSupport {
}
public static Set<String> asStringSet(Node node) {
+ if (node == null) {
+ return null;
+ }
+
return asStringSet(asText(node));
}
public static Class<?>[] asClassArray(Node node) throws
YamlDeserializationException {
+ if (node == null) {
+ return null;
+ }
+
return asClassArray(asText(node));
}
public static String asText(Node node) throws YamlDeserializationException
{
+ if (node == null) {
+ return null;
+ }
if (node.getNodeType() != NodeType.SCALAR) {
throw new IllegalArgumentException("Node is not SCALAR");
}
@@ -119,6 +142,10 @@ public class YamlDeserializerSupport {
}
public static Map<String, Object> asMap(Node node) {
+ if (node == null) {
+ return null;
+ }
+
final MappingNode mn = asMappingNode(node);
final Map<String, Object> answer = new HashMap<>();
@@ -142,6 +169,10 @@ public class YamlDeserializerSupport {
}
public static Map<String, Object> asScalarMap(Node node) {
+ if (node == null) {
+ return null;
+ }
+
final MappingNode mn = asMappingNode(node);
final Map<String, Object> answer = new HashMap<>();
@@ -352,4 +383,27 @@ public class YamlDeserializerSupport {
}
}
}
+
+ public static Node nodeAt(Node root, String pointer) {
+ if (ObjectHelper.isEmpty(pointer)) {
+ return root;
+ }
+
+ MappingNode mn = asMappingNode(root);
+ for (String path : pointer.split("/")) {
+ for (NodeTuple child : mn.getValue()) {
+ if (child.getKeyNode() instanceof ScalarNode) {
+ ScalarNode scalar = (ScalarNode) child.getKeyNode();
+ if (scalar.getValue().equals(path)) {
+ String next = pointer.substring(path.length() + 1);
+ return ObjectHelper.isEmpty(next)
+ ? child.getValueNode()
+ : nodeAt(child.getValueNode(), next);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
}
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/kamelet.yaml
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/kamelet.yaml
new file mode 100644
index 0000000..867db7d
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/META-INF/services/org/apache/camel/routes-loader/kamelet.yaml
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.dsl.yaml.KameletRoutesBuilderLoader
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/KameletRoutesBuilderLoader.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/KameletRoutesBuilderLoader.java
new file mode 100644
index 0000000..9502a8b
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/KameletRoutesBuilderLoader.java
@@ -0,0 +1,73 @@
+/*
+ * 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.api.management.ManagedResource;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
+import org.apache.camel.model.RouteTemplateDefinition;
+import org.apache.camel.spi.annotations.RoutesLoader;
+import org.snakeyaml.engine.v2.nodes.Node;
+import org.snakeyaml.engine.v2.nodes.NodeTuple;
+
+import static
org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asMappingNode;
+import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asText;
+import static org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.nodeAt;
+
+@ManagedResource(description = "Managed Kamelet RoutesBuilderLoader")
+@RoutesLoader(KameletRoutesBuilderLoader.EXTENSION)
+public class KameletRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport
{
+ public static final String EXTENSION = "kamelet.yaml";
+
+ public KameletRoutesBuilderLoader() {
+ super(EXTENSION);
+ }
+
+ @Override
+ protected RouteBuilder builder(Node node) {
+ Node template = nodeAt(node, "/spec/template");
+ if (template == null) {
+ // fallback till flow get removed
+ template = nodeAt(node, "/spec/flow");
+ }
+ if (template == null) {
+ throw new IllegalArgumentException("No template defined");
+ }
+
+ final YamlDeserializationContext context =
this.getDeserializationContext();
+ final RouteTemplateDefinition rtd = context.construct(template,
RouteTemplateDefinition.class);
+
+ rtd.id(asText(nodeAt(node, "/metadata/name")));
+
+ Node properties = nodeAt(node, "/spec/definition/properties");
+ if (properties != null) {
+ for (NodeTuple p : asMappingNode(properties).getValue()) {
+ final String key = asText(p.getKeyNode());
+ final Node def = nodeAt(p.getValueNode(), "/default");
+
+ rtd.templateParameter(key, asText(def));
+ }
+ }
+
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ getRouteTemplateCollection().routeTemplate(rtd);
+ }
+ };
+ }
+}
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 f58e24f..2d19c50 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
@@ -16,20 +16,9 @@
*/
package org.apache.camel.dsl.yaml;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
import org.apache.camel.api.management.ManagedResource;
import org.apache.camel.builder.ErrorHandlerBuilder;
import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.dsl.support.RouteBuilderLoaderSupport;
-import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
-import org.apache.camel.dsl.yaml.common.YamlDeserializationMode;
-import org.apache.camel.dsl.yaml.deserializers.CustomResolver;
-import
org.apache.camel.dsl.yaml.deserializers.EndpointProducerDeserializersResolver;
-import org.apache.camel.dsl.yaml.deserializers.ModelDeserializersResolver;
import org.apache.camel.dsl.yaml.deserializers.OutputAwareFromDefinition;
import org.apache.camel.model.OnExceptionDefinition;
import org.apache.camel.model.RouteDefinition;
@@ -37,109 +26,57 @@ import org.apache.camel.model.RouteTemplateDefinition;
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.Resource;
import org.apache.camel.spi.annotations.RoutesLoader;
-import org.apache.camel.support.service.ServiceHelper;
-import org.apache.camel.util.ObjectHelper;
-import org.snakeyaml.engine.v2.api.Load;
-import org.snakeyaml.engine.v2.api.LoadSettings;
+import org.snakeyaml.engine.v2.nodes.Node;
+
+import static
org.apache.camel.dsl.yaml.common.YamlDeserializerSupport.asSequenceNode;
@ManagedResource(description = "Managed YAML RoutesBuilderLoader")
@RoutesLoader(YamlRoutesBuilderLoader.EXTENSION)
-public class YamlRoutesBuilderLoader extends RouteBuilderLoaderSupport {
- public static final String DESERIALIZATION_MODE =
"CamelYamlDslDeserializationMode";
+public class YamlRoutesBuilderLoader extends YamlRoutesBuilderLoaderSupport {
public static final String EXTENSION = "yaml";
- private LoadSettings settings;
- private YamlDeserializationContext deserializationContext;
-
public YamlRoutesBuilderLoader() {
super(EXTENSION);
}
- @Override
- protected void doBuild() throws Exception {
- super.doBuild();
-
- this.settings = LoadSettings.builder().build();
- this.deserializationContext = new YamlDeserializationContext(settings);
- this.deserializationContext.setCamelContext(getCamelContext());
- this.deserializationContext.addResolvers(new CustomResolver());
- this.deserializationContext.addResolvers(new
ModelDeserializersResolver());
- this.deserializationContext.addResolvers(new
EndpointProducerDeserializersResolver());
- }
-
- @Override
- protected void doStart() throws Exception {
- super.doStart();
-
- final Map<String, String> options =
getCamelContext().getGlobalOptions();
- final String mode = options.getOrDefault(DESERIALIZATION_MODE,
YamlDeserializationMode.CLASSIC.name());
- if (mode != null) {
- this.deserializationContext.setDeserializationMode(
-
YamlDeserializationMode.valueOf(mode.toUpperCase(Locale.US)));
- }
-
- ServiceHelper.startService(this.deserializationContext);
- }
-
- @Override
- protected void doStop() throws Exception {
- super.doStop();
-
- ServiceHelper.stopService(this.deserializationContext);
-
- this.deserializationContext = null;
- this.settings = null;
- }
-
- @Override
- public RouteBuilder doLoadRouteBuilder(Resource resource) throws Exception
{
- ObjectHelper.notNull(deserializationContext, "constructor");
- ObjectHelper.notNull(settings, "settings");
-
+ protected RouteBuilder builder(Node root) {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
- final Load load = new Load(settings, deserializationContext);
-
- try (InputStream is = resource.getInputStream()) {
- for (Object item : (List<?>) load.loadFromInputStream(is))
{
-
- configure(item);
- }
- }
- }
-
- private void configure(Object item) {
- if (item instanceof OutputAwareFromDefinition) {
- RouteDefinition route = new RouteDefinition();
- route.setInput(((OutputAwareFromDefinition)
item).getDelegate());
- route.setOutputs(((OutputAwareFromDefinition)
item).getOutputs());
- getRouteCollection().route(route);
- } else if (item instanceof RouteDefinition) {
- getRouteCollection().route((RouteDefinition) item);
- } else if (item instanceof RestDefinition) {
- RestDefinition definition = (RestDefinition) item;
- for (VerbDefinition verb : definition.getVerbs()) {
- verb.setRest(definition);
- }
- getRestCollection().rest(definition);
- } else if (item instanceof CamelContextCustomizer) {
- ((CamelContextCustomizer)
item).configure(getCamelContext());
- } else if (item instanceof OnExceptionDefinition) {
- if (!getRouteCollection().getRoutes().isEmpty()) {
- throw new IllegalArgumentException("onException must
be defined before any routes in the RouteBuilder");
- }
-
getRouteCollection().getOnExceptions().add((OnExceptionDefinition) item);
- } else if (item instanceof ErrorHandlerBuilder) {
- if (!getRouteCollection().getRoutes().isEmpty()) {
- throw new IllegalArgumentException(
- "errorHandler must be defined before any
routes in the RouteBuilder");
+ for (Node node : asSequenceNode(root).getValue()) {
+ Object item =
getDeserializationContext().mandatoryResolve(node).construct(node);
+
+ if (item instanceof OutputAwareFromDefinition) {
+ RouteDefinition route = new RouteDefinition();
+ route.setInput(((OutputAwareFromDefinition)
item).getDelegate());
+ route.setOutputs(((OutputAwareFromDefinition)
item).getOutputs());
+ getRouteCollection().route(route);
+ } else if (item instanceof RouteDefinition) {
+ getRouteCollection().route((RouteDefinition) item);
+ } else if (item instanceof RestDefinition) {
+ RestDefinition definition = (RestDefinition) item;
+ for (VerbDefinition verb : definition.getVerbs()) {
+ verb.setRest(definition);
+ }
+ getRestCollection().rest(definition);
+ } else if (item instanceof CamelContextCustomizer) {
+ ((CamelContextCustomizer)
item).configure(getCamelContext());
+ } else if (item instanceof OnExceptionDefinition) {
+ if (!getRouteCollection().getRoutes().isEmpty()) {
+ throw new IllegalArgumentException(
+ "onException must be defined before any
routes in the RouteBuilder");
+ }
+
getRouteCollection().getOnExceptions().add((OnExceptionDefinition) item);
+ } else if (item instanceof ErrorHandlerBuilder) {
+ if (!getRouteCollection().getRoutes().isEmpty()) {
+ throw new IllegalArgumentException(
+ "errorHandler must be defined before any
routes in the RouteBuilder");
+ }
+ errorHandler((ErrorHandlerBuilder) item);
+ } else if (item instanceof RouteTemplateDefinition) {
+
getRouteTemplateCollection().routeTemplate((RouteTemplateDefinition) item);
}
- errorHandler((ErrorHandlerBuilder) item);
- } else if (item instanceof RouteTemplateDefinition) {
-
getRouteTemplateCollection().routeTemplate((RouteTemplateDefinition) item);
}
}
};
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
new file mode 100644
index 0000000..9f8078c
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/java/org/apache/camel/dsl/yaml/YamlRoutesBuilderLoaderSupport.java
@@ -0,0 +1,126 @@
+/*
+ * 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 java.io.InputStream;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.dsl.support.RouteBuilderLoaderSupport;
+import org.apache.camel.dsl.yaml.common.YamlDeserializationContext;
+import org.apache.camel.dsl.yaml.common.YamlDeserializationMode;
+import org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException;
+import org.apache.camel.dsl.yaml.deserializers.CustomResolver;
+import
org.apache.camel.dsl.yaml.deserializers.EndpointProducerDeserializersResolver;
+import org.apache.camel.dsl.yaml.deserializers.ModelDeserializersResolver;
+import org.apache.camel.spi.Resource;
+import org.apache.camel.support.service.ServiceHelper;
+import org.apache.camel.util.ObjectHelper;
+import org.snakeyaml.engine.v2.api.LoadSettings;
+import org.snakeyaml.engine.v2.api.YamlUnicodeReader;
+import org.snakeyaml.engine.v2.composer.Composer;
+import org.snakeyaml.engine.v2.nodes.Node;
+import org.snakeyaml.engine.v2.parser.Parser;
+import org.snakeyaml.engine.v2.parser.ParserImpl;
+import org.snakeyaml.engine.v2.scanner.StreamReader;
+
+abstract class YamlRoutesBuilderLoaderSupport extends
RouteBuilderLoaderSupport {
+ public static final String DESERIALIZATION_MODE =
"CamelYamlDslDeserializationMode";
+
+ private LoadSettings settings;
+ private YamlDeserializationContext deserializationContext;
+ private YamlDeserializationMode deserializationMode;
+
+ public YamlRoutesBuilderLoaderSupport(String extension) {
+ super(extension);
+ }
+
+ public YamlDeserializationMode getDeserializationMode() {
+ return deserializationMode;
+ }
+
+ public void setDeserializationMode(YamlDeserializationMode
deserializationMode) {
+ this.deserializationMode = deserializationMode;
+ }
+
+ @Override
+ protected void doBuild() throws Exception {
+ super.doBuild();
+
+ this.settings = LoadSettings.builder().build();
+ this.deserializationContext = new YamlDeserializationContext(settings);
+ this.deserializationContext.setCamelContext(getCamelContext());
+ this.deserializationContext.addResolvers(new CustomResolver());
+ this.deserializationContext.addResolvers(new
ModelDeserializersResolver());
+ this.deserializationContext.addResolvers(new
EndpointProducerDeserializersResolver());
+ }
+
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+
+ if (this.deserializationMode == null) {
+ final Map<String, String> options =
getCamelContext().getGlobalOptions();
+ final String mode = options.getOrDefault(DESERIALIZATION_MODE,
YamlDeserializationMode.CLASSIC.name());
+ if (mode != null) {
+ this.deserializationContext.setDeserializationMode(
+
YamlDeserializationMode.valueOf(mode.toUpperCase(Locale.US)));
+ }
+ } else {
+
this.deserializationContext.setDeserializationMode(deserializationMode);
+ }
+
+ ServiceHelper.startService(this.deserializationContext);
+ }
+
+ @Override
+ protected void doStop() throws Exception {
+ super.doStop();
+
+ ServiceHelper.stopService(this.deserializationContext);
+
+ this.deserializationContext = null;
+ this.settings = null;
+ }
+
+ @Override
+ public RouteBuilder doLoadRouteBuilder(Resource resource) throws Exception
{
+ ObjectHelper.notNull(deserializationContext, "constructor");
+ ObjectHelper.notNull(settings, "settings");
+
+ try (InputStream is = resource.getInputStream()) {
+ final StreamReader reader = new StreamReader(new
YamlUnicodeReader(is), settings);
+ final Parser parser = new ParserImpl(reader, settings);
+ final Composer composer = new Composer(parser, settings);
+
+ return composer.getSingleNode()
+ .map(this::builder)
+ .orElseThrow(() -> new
YamlDeserializationException("Unable to deserialize resource"));
+ }
+ }
+
+ protected LoadSettings getSettings() {
+ return this.settings;
+ }
+
+ protected YamlDeserializationContext getDeserializationContext() {
+ return this.deserializationContext;
+ }
+
+ protected abstract RouteBuilder builder(Node node);
+}
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletLoaderTest.groovy
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletLoaderTest.groovy
new file mode 100644
index 0000000..d3ae630
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/KameletLoaderTest.groovy
@@ -0,0 +1,208 @@
+/*
+ * 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.support.YamlTestSupport
+import org.apache.camel.model.ToDefinition
+
+class KameletLoaderTest extends YamlTestSupport {
+ @Override
+ def doSetup() {
+ context.start()
+ }
+
+ def "kamelet with flow"() {
+ when:
+ loadKamelets('''
+ apiVersion: camel.apache.org/v1alpha1
+ kind: Kamelet
+ metadata:
+ name: aws-s3-sink
+ spec:
+ definition:
+ title: "AWS S3 Sink"
+ description: "AWS S3 Sink"
+ required:
+ - bucketNameOrArn
+ - accessKey
+ - secretKey
+ - region
+ type: object
+ properties:
+ bucketNameOrArn:
+ title: Bucket Name
+ description: The S3 Bucket name or ARN.
+ type: string
+ accessKey:
+ title: Access Key
+ description: The access key obtained from AWS.
+ type: string
+ format: password
+ x-descriptors:
+ - urn:alm:descriptor:com.tectonic.ui:password
+ overrideEndpoint:
+ title: Override Endpoint
+ type: boolean
+ default: false
+ x-descriptors:
+ - 'urn:alm:descriptor:com.tectonic.ui:checkbox'
+ flow:
+ from:
+ uri: "kamelet:source"
+ steps:
+ - to:
+ uri: "aws2-s3:{{bucketNameOrArn}}"
+ parameters:
+ secretKey: "{{secretKey}}"
+ accessKey: "{{accessKey}}"
+ region: "{{region}}"
+ uriEndpointOverride: "{{uriEndpointOverride}}"
+ overrideEndpoint: "{{overrideEndpoint}}"
+ autoCreateBucket: "{{autoCreateBucket}}"
+ ''')
+ then:
+ context.routeTemplateDefinitions.size() == 1
+
+ with (context.routeTemplateDefinitions[0]) {
+ id == 'aws-s3-sink'
+
+ templateParameters.size() == 3
+
+ templateParameters.any {
+ it.name == 'bucketNameOrArn' && it.defaultValue == null
+ }
+ templateParameters.any {
+ it.name == 'overrideEndpoint' && it.defaultValue == 'false'
+ }
+
+ with(route) {
+ input.endpointUri == 'kamelet:source'
+ outputs.size() == 1
+ with (outputs[0], ToDefinition) {
+ endpointUri ==~ /aws2-s3:.*/
+ }
+ }
+ }
+ }
+
+ def "kamelet with template"() {
+ when:
+ loadKamelets('''
+ apiVersion: camel.apache.org/v1alpha1
+ kind: Kamelet
+ metadata:
+ name: aws-s3-sink
+ spec:
+ definition:
+ title: "AWS S3 Sink"
+ description: "AWS S3 Sink"
+ required:
+ - bucketNameOrArn
+ - accessKey
+ - secretKey
+ - region
+ type: object
+ properties:
+ bucketNameOrArn:
+ title: Bucket Name
+ description: The S3 Bucket name or ARN.
+ type: string
+ accessKey:
+ title: Access Key
+ description: The access key obtained from AWS.
+ type: string
+ format: password
+ x-descriptors:
+ - urn:alm:descriptor:com.tectonic.ui:password
+ overrideEndpoint:
+ title: Override Endpoint
+ type: boolean
+ default: false
+ x-descriptors:
+ - 'urn:alm:descriptor:com.tectonic.ui:checkbox'
+ template:
+ from:
+ uri: "kamelet:source"
+ steps:
+ - to:
+ uri: "aws2-s3:{{bucketNameOrArn}}"
+ parameters:
+ secretKey: "{{secretKey}}"
+ accessKey: "{{accessKey}}"
+ region: "{{region}}"
+ uriEndpointOverride: "{{uriEndpointOverride}}"
+ overrideEndpoint: "{{overrideEndpoint}}"
+ autoCreateBucket: "{{autoCreateBucket}}"
+ ''')
+ then:
+ context.routeTemplateDefinitions.size() == 1
+
+ with (context.routeTemplateDefinitions[0]) {
+ id == 'aws-s3-sink'
+
+ templateParameters.size() == 3
+
+ templateParameters.any {
+ it.name == 'bucketNameOrArn' && it.defaultValue == null
+ }
+ templateParameters.any {
+ it.name == 'overrideEndpoint' && it.defaultValue == 'false'
+ }
+
+ with(route) {
+ input.endpointUri == 'kamelet:source'
+ outputs.size() == 1
+ with (outputs[0], ToDefinition) {
+ endpointUri ==~ /aws2-s3:.*/
+ }
+ }
+ }
+ }
+
+ def "kamelet discovery"() {
+ setup:
+ def payload = UUID.randomUUID().toString()
+
+ loadRoutes """
+ - from:
+ uri: "direct:start"
+ steps:
+ - to: "kamelet:mySetBody?payload=${payload}"
+ - to: "mock:result"
+ """
+
+ withMock('mock:result') {
+ expectedMessageCount 1
+ expectedBodiesReceived payload
+ }
+ when:
+ context.start()
+
+ withTemplate {
+ to('direct:start').withBody(payload).send()
+ }
+ then:
+ context.routeTemplateDefinitions.size() == 1
+
+ with (context.routeTemplateDefinitions[0]) {
+ id == 'mySetBody'
+ }
+
+ 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 bbffe8d..a9749f8 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
@@ -52,7 +52,6 @@ class YamlTestSupport extends Specification implements
HasCamelContext {
def report = SCHEMA.validate(target)
if (!report.isSuccess()) {
-
throw new IllegalArgumentException("${report}")
}
}
@@ -85,6 +84,16 @@ class YamlTestSupport extends Specification implements
HasCamelContext {
)
}
+ def loadKamelets(String... resources) {
+ int index = 0
+
+ context.routesLoader.loadRoutes(
+ resources.collect {
+ it ->
ResourceHelper.fromString("route-${index++}.kamelet.yaml", it.stripIndent())
+ }
+ )
+ }
+
def withMock(
String uri,
@DelegatesTo(MockEndpoint) Closure<?> closure) {
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/resources/kamelets/mySetBody.kamelet.yaml
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/resources/kamelets/mySetBody.kamelet.yaml
new file mode 100644
index 0000000..fa5f49d
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/resources/kamelets/mySetBody.kamelet.yaml
@@ -0,0 +1,32 @@
+#
+# 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.
+#
+apiVersion: camel.apache.org/v1alpha1
+kind: Kamelet
+metadata:
+ name: mySetBody
+spec:
+ definition:
+ properties:
+ payload:
+ title: The Payload
+ type: string
+ template:
+ from:
+ uri: "kamelet:source"
+ steps:
+ - set-body:
+ constant: "{{payload}}"
\ No newline at end of file