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
The following commit(s) were added to refs/heads/main by this push:
new 17175a0dc3e CAMEL-20602: Support user properties on Camel JBang bind
command
17175a0dc3e is described below
commit 17175a0dc3e46901b136e37dbab2115272789067
Author: Christoph Deppisch <[email protected]>
AuthorDate: Fri Mar 22 16:37:38 2024 +0100
CAMEL-20602: Support user properties on Camel JBang bind command
- Allow to specify properties on source, sink, step in a Pipe
- User properties can be set as additional command options targeting either
the source or the sink or a specific step
---
.../apache/camel/dsl/jbang/core/commands/Bind.java | 220 ++++++--
.../camel/dsl/jbang/core/common/YamlHelper.java | 59 ++
.../resources/templates/pipe-kamelet-uri.yaml.tmpl | 3 +-
.../resources/templates/pipe-uri-kamelet.yaml.tmpl | 3 +-
.../resources/templates/pipe-uri-uri.yaml.tmpl | 6 +-
.../main/resources/templates/step-uri.yaml.tmpl | 3 +-
.../camel/dsl/jbang/core/commands/BindTest.java | 623 +++++++++++++++++++++
.../jbang/core/commands/k/KubernetesHelper.java | 24 +-
8 files changed, 863 insertions(+), 78 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
index 3fd5d4f09f7..27b16288bc6 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Bind.java
@@ -20,16 +20,22 @@ import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Path;
-import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Set;
import java.util.Stack;
+import org.apache.camel.dsl.jbang.core.common.JSonHelper;
+import org.apache.camel.dsl.jbang.core.common.YamlHelper;
import org.apache.camel.github.GitHubResourceResolver;
import org.apache.camel.impl.engine.DefaultResourceResolvers;
import org.apache.camel.spi.Resource;
import org.apache.camel.spi.ResourceResolver;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.URISupport;
+import org.apache.camel.util.json.Jsoner;
import org.snakeyaml.engine.v2.api.LoadSettings;
import org.snakeyaml.engine.v2.api.YamlUnicodeReader;
import org.snakeyaml.engine.v2.composer.Composer;
@@ -64,21 +70,49 @@ public class Bind extends CamelCommand {
required = true)
String sink;
+ @CommandLine.Option(names = { "--property" },
+ description = "Adds a pipe property in the form of
[source|sink|step-<n>].<key>=<value> where <n> is the step number starting from
1",
+ arity = "0")
+ String[] properties;
+
+ @CommandLine.Option(names = { "--output" },
+ defaultValue = "file",
+ description = "Output format generated by this command
(supports: file, yaml or json).")
+ String output;
+
public Bind(CamelJBangMain main) {
super(main);
}
@Override
public Integer doCall() throws Exception {
-
// the pipe source and sink can either be a kamelet or an uri
String in = "kamelet";
String out = "kamelet";
+
+ String sourceEndpoint = source;
+ String sinkEndpoint = sink;
+ Map<String, Object> sourceUriProperties = new HashMap<>();
+ Map<String, Object> sinkUriProperties = new HashMap<>();
if (source.contains(":")) {
in = "uri";
+ if (source.contains("?")) {
+ sourceEndpoint = StringHelper.before(source, "?");
+ String query = StringHelper.after(source, "?");
+ if (query != null) {
+ sourceUriProperties = URISupport.parseQuery(query, true);
+ }
+ }
}
if (sink.contains(":")) {
out = "uri";
+ if (sink.contains("?")) {
+ sinkEndpoint = StringHelper.before(sink, "?");
+ String query = StringHelper.after(sink, "?");
+ if (query != null) {
+ sinkUriProperties = URISupport.parseQuery(query, true);
+ }
+ }
}
InputStream is =
Bind.class.getClassLoader().getResourceAsStream("templates/pipe-" + in + "-" +
out + ".yaml.tmpl");
@@ -88,21 +122,35 @@ public class Bind extends CamelCommand {
String stepsContext = "";
if (steps != null) {
StringBuilder sb = new StringBuilder("\n steps:\n");
- for (String step : steps) {
+ for (int i = 0; i < steps.length; i++) {
+ String step = steps[i];
boolean uri = step.contains(":");
String text;
+ String stepType;
+ Map<String, Object> stepProperties =
getProperties("step-%d".formatted(i + 1));
if (uri) {
- is =
Bind.class.getClassLoader().getResourceAsStream("templates/step-uri.yaml.tmpl");
- text = IOHelper.loadText(is);
- IOHelper.close(is);
- text = text.replaceFirst("\\{\\{ \\.Name }}", step);
+ stepType = "uri";
+ if (step.contains("?")) {
+ String query = StringHelper.after(step, "?");
+ step = StringHelper.before(step, "?");
+ if (query != null) {
+ stepProperties.putAll(URISupport.parseQuery(query,
true));
+ }
+ }
} else {
- is =
Bind.class.getClassLoader().getResourceAsStream("templates/step-kamelet.yaml.tmpl");
- text = IOHelper.loadText(is);
- IOHelper.close(is);
- text = text.replaceFirst("\\{\\{ \\.Name }}", step);
- String props = kameletProperties(step);
- text = text.replaceFirst("\\{\\{ \\.StepProperties }}",
props);
+ stepType = "kamelet";
+ stepProperties = kameletProperties(step, stepProperties);
+ }
+
+ is =
Bind.class.getClassLoader().getResourceAsStream("templates/step-%s.yaml.tmpl".formatted(stepType));
+ text = IOHelper.loadText(is);
+ IOHelper.close(is);
+ text = text.replaceFirst("\\{\\{ \\.Name }}", step);
+
+ if (i == steps.length - 1) {
+ text = text.replaceFirst("\\{\\{ \\.StepProperties }}\n",
asEndpointProperties(stepProperties));
+ } else {
+ text = text.replaceFirst("\\{\\{ \\.StepProperties }}",
asEndpointProperties(stepProperties));
}
sb.append(text);
}
@@ -111,31 +159,117 @@ public class Bind extends CamelCommand {
String name = FileUtil.onlyName(file, false);
context = context.replaceFirst("\\{\\{ \\.Name }}", name);
- context = context.replaceFirst("\\{\\{ \\.Source }}", source);
- context = context.replaceFirst("\\{\\{ \\.Sink }}", sink);
+ context = context.replaceFirst("\\{\\{ \\.Source }}", sourceEndpoint);
+ context = context.replaceFirst("\\{\\{ \\.Sink }}", sinkEndpoint);
context = context.replaceFirst("\\{\\{ \\.Steps }}", stepsContext);
+ Map<String, Object> sourceProperties = getProperties("source");
if ("kamelet".equals(in)) {
- String props = kameletProperties(source);
- context = context.replaceFirst("\\{\\{ \\.SourceProperties }}",
props);
+ sourceProperties = kameletProperties(sourceEndpoint,
sourceProperties);
+ } else {
+ sourceProperties.putAll(sourceUriProperties);
}
+ context = context.replaceFirst("\\{\\{ \\.SourceProperties }}\n",
asEndpointProperties(sourceProperties));
+
+ Map<String, Object> sinkProperties = getProperties("sink");
if ("kamelet".equals(out)) {
- String props = kameletProperties(sink);
- context = context.replaceFirst("\\{\\{ \\.SinkProperties }}",
props);
+ sinkProperties = kameletProperties(sinkEndpoint, sinkProperties);
+ } else {
+ sinkProperties.putAll(sinkUriProperties);
}
+ context = context.replaceFirst("\\{\\{ \\.SinkProperties }}\n",
asEndpointProperties(sinkProperties));
- IOHelper.writeText(context, new FileOutputStream(file, false));
+ switch (output) {
+ case "file":
+ if (file.endsWith(".yaml")) {
+ IOHelper.writeText(context, new FileOutputStream(file,
false));
+ } else if (file.endsWith(".json")) {
+
IOHelper.writeText(Jsoner.serialize(YamlHelper.yaml().loadAs(context,
Map.class)),
+ new FileOutputStream(file, false));
+ } else {
+ IOHelper.writeText(context, new FileOutputStream(file +
".yaml", false));
+ }
+ break;
+ case "yaml":
+ printer().println(context);
+ break;
+ case "json":
+
printer().println(JSonHelper.prettyPrint(Jsoner.serialize(YamlHelper.yaml().loadAs(context,
Map.class)), 2)
+ .replaceAll("\\\\/", "/"));
+ break;
+ default:
+ printer().printf("Unsupported output format '%s' (supported:
file, yaml, json)%n", output);
+ return -1;
+ }
return 0;
}
- protected String kameletProperties(String kamelet) throws Exception {
+ /**
+ * Creates YAML snippet representing the endpoint properties section.
+ *
+ * @param props the properties to set as endpoint properties.
+ * @return
+ */
+ private String asEndpointProperties(Map<String, Object> props) {
StringBuilder sb = new StringBuilder();
+ if (props.isEmpty()) {
+ // create a dummy placeholder, so it is easier to add new
properties manually
+ return sb.append("#properties:\n ").append("#key:
\"value\"").toString();
+ }
+
+ sb.append("properties:\n");
+ for (Map.Entry<String, Object> propertyEntry : props.entrySet()) {
+ sb.append(" ").append(propertyEntry.getKey()).append(": ")
+ .append(propertyEntry.getValue()).append("\n");
+ }
+ return sb.toString().trim();
+ }
+
+ /**
+ * Extracts properties from given property arguments. Filter properties by
given prefix. This way each component in
+ * pipe (source, sink, step[1-n]) can have its individual properties.
+ *
+ * @param keyPrefix
+ * @return
+ */
+ private Map<String, Object> getProperties(String keyPrefix) {
+ Map<String, Object> props = new HashMap<>();
+ if (properties != null) {
+ for (String propertyExpression : properties) {
+ if (propertyExpression.startsWith(keyPrefix + ".")) {
+ String[] keyValue = propertyExpression.split("=", 2);
+ if (keyValue.length != 2) {
+ printer().printf(
+ "property '%s' does not follow format
[source|sink|step-<n>].<key>=<value>%n",
+ propertyExpression);
+ continue;
+ }
+ props.put(keyValue[0].substring(keyPrefix.length() + 1),
keyValue[1]);
+ }
+ }
+ }
+
+ return props;
+ }
+
+ /**
+ * Get required properties from Kamelet specification and add those to the
given user properties if not already set.
+ * In case a required property is not present in the provided user
properties the value is either set to the example
+ * coming from the Kamelet specification or to a placeholder value for
users to fill in manually. Property values do
+ * already have quotes when the type is String.
+ *
+ * @param kamelet
+ * @return
+ * @throws Exception
+ */
+ protected Map<String, Object> kameletProperties(String kamelet,
Map<String, Object> userProperties) throws Exception {
+ Map<String, Object> endpointProperties = new HashMap<>();
InputStream is;
String loc;
Resource res;
- // try local disk first before github
+ // try local disk first before GitHub
ResourceResolver resolver = new
DefaultResourceResolvers.FileResolver();
try {
res = resolver.resolve("file:" + kamelet + ".kamelet.yaml");
@@ -167,26 +301,23 @@ public class Bind extends CamelCommand {
if (root != null) {
Set<String> required = asStringSet(nodeAt(root,
"/spec/definition/required"));
if (required != null && !required.isEmpty()) {
- sb.append("properties:\n");
- Iterator<String> it = required.iterator();
- while (it.hasNext()) {
- String req = it.next();
- String type = asText(nodeAt(root,
"/spec/definition/properties/" + req + "/type"));
- String example = asText(nodeAt(root,
"/spec/definition/properties/" + req + "/example"));
- sb.append(" ").append(req).append(": ");
- if (example != null) {
- if ("string".equals(type)) {
- sb.append("\"");
- }
- sb.append(example);
- if ("string".equals(type)) {
- sb.append("\"");
+ for (String req : required) {
+ if (!userProperties.containsKey(req)) {
+ String type = asText(nodeAt(root,
"/spec/definition/properties/" + req + "/type"));
+ String example = asText(nodeAt(root,
"/spec/definition/properties/" + req + "/example"));
+ StringBuilder vb = new StringBuilder();
+ if (example != null) {
+ if ("string".equals(type)) {
+ vb.append("\"");
+ }
+ vb.append(example);
+ if ("string".equals(type)) {
+ vb.append("\"");
+ }
+ } else {
+ vb.append("\"value\"");
}
- } else {
- sb.append("\"value\"");
- }
- if (it.hasNext()) {
- sb.append("\n");
+ endpointProperties.put(req, vb.toString());
}
}
}
@@ -199,12 +330,9 @@ public class Bind extends CamelCommand {
System.err.println("Kamelet not found on github: " + kamelet);
}
- // create a dummy placeholder, so it is easier to add new properties
manually
- if (sb.isEmpty()) {
- sb.append("#properties:\n #key: \"value\"");
- }
+ endpointProperties.putAll(userProperties);
- return sb.toString();
+ return endpointProperties;
}
static class FileConsumer extends ParameterConsumer<Bind> {
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/YamlHelper.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/YamlHelper.java
new file mode 100644
index 00000000000..db408c40a13
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/YamlHelper.java
@@ -0,0 +1,59 @@
+/*
+ * 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.jbang.core.common;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.introspector.Property;
+import org.yaml.snakeyaml.nodes.NodeTuple;
+import org.yaml.snakeyaml.nodes.Tag;
+import org.yaml.snakeyaml.representer.Representer;
+
+public final class YamlHelper {
+
+ private YamlHelper() {
+ }
+
+ /**
+ * Creates new Yaml instance. The implementation provided by Snakeyaml is
not thread-safe. It is better to create a
+ * fresh instance for every YAML stream.
+ *
+ * @return
+ */
+ public static Yaml yaml() {
+ Representer representer = new Representer(new DumperOptions()) {
+ @Override
+ protected NodeTuple representJavaBeanProperty(
+ Object javaBean, Property property, Object propertyValue,
Tag customTag) {
+ // if value of property is null, ignore it.
+ if (propertyValue == null || (propertyValue instanceof
Collection && ((Collection<?>) propertyValue).isEmpty())
+ ||
+ (propertyValue instanceof Map && ((Map<?, ?>)
propertyValue).isEmpty())) {
+ return null;
+ } else {
+ return super.representJavaBeanProperty(javaBean, property,
propertyValue, customTag);
+ }
+ }
+ };
+ representer.getPropertyUtils().setSkipMissingProperties(true);
+ return new Yaml(representer);
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-kamelet-uri.yaml.tmpl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-kamelet-uri.yaml.tmpl
index 7e6b69967c4..07de5540d3f 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-kamelet-uri.yaml.tmpl
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-kamelet-uri.yaml.tmpl
@@ -12,5 +12,4 @@ spec:
{{ .Steps }}
sink:
uri: {{ .Sink }}
- #properties:
- #key: "value"
+ {{ .SinkProperties }}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-uri-kamelet.yaml.tmpl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-uri-kamelet.yaml.tmpl
index 06dd6b0ee67..9c7b90820a7 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-uri-kamelet.yaml.tmpl
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-uri-kamelet.yaml.tmpl
@@ -5,8 +5,7 @@ metadata:
spec:
source:
uri: {{ .Source }}
- #properties:
- #key: "value"
+ {{ .SourceProperties }}
{{ .Steps }}
sink:
ref:
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-uri-uri.yaml.tmpl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-uri-uri.yaml.tmpl
index c88aade2e25..b71a4e1f96a 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-uri-uri.yaml.tmpl
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-uri-uri.yaml.tmpl
@@ -5,10 +5,8 @@ metadata:
spec:
source:
uri: {{ .Source }}
- #properties:
- #key: "value"
+ {{ .SourceProperties }}
{{ .Steps }}
sink:
uri: {{ .Sink }}
- #properties:
- #key: "value"
+ {{ .SinkProperties }}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/step-uri.yaml.tmpl
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/step-uri.yaml.tmpl
index ad279dac276..dc1c371c06a 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/step-uri.yaml.tmpl
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/step-uri.yaml.tmpl
@@ -1,3 +1,2 @@
- uri: {{ .Name }}
- #properties:
- #key: "value"
\ No newline at end of file
+ {{ .StepProperties }}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/BindTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/BindTest.java
new file mode 100644
index 00000000000..edf952212f9
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/BindTest.java
@@ -0,0 +1,623 @@
+/*
+ * 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.jbang.core.commands;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class BindTest extends CamelCommandBaseTest {
+
+ @Test
+ public void shouldBindKameletSourceToKameletSink() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer-source";
+ command.sink = "log-sink";
+ command.output = "yaml";
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-log
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: "hello world"
+ sink:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: log-sink
+ #properties:
+ #key: "value"
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindKameletSourceToKameletSinkWithProperties() throws
Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer-source";
+ command.sink = "log-sink";
+ command.output = "yaml";
+
+ command.properties = new String[] {
+ "source.message=Hello",
+ "source.period=5000",
+ "sink.showHeaders=true",
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-log
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: Hello
+ period: 5000
+ sink:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: log-sink
+ properties:
+ showHeaders: true
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindWithSteps() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-http";
+ command.source = "timer-source";
+ command.sink = "http-sink";
+ command.output = "yaml";
+
+ command.steps = new String[] {
+ "set-body-action",
+ "log-action"
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-http
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: "hello world"
+ steps:
+ - ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: set-body-action
+ properties:
+ value: "value"
+ - ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: log-action
+ #properties:
+ #key: "value"
+ sink:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: http-sink
+ properties:
+ url: "https://my-service/path"
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindWithUriSteps() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-http";
+ command.source = "timer-source";
+ command.sink = "http-sink";
+ command.output = "yaml";
+
+ command.steps = new String[] {
+ "set-body-action",
+ "log:info"
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-http
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: "hello world"
+ steps:
+ - ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: set-body-action
+ properties:
+ value: "value"
+ - uri: log:info
+ #properties:
+ #key: "value"
+ sink:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: http-sink
+ properties:
+ url: "https://my-service/path"
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindWithStepsAndProperties() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-http";
+ command.source = "timer-source";
+ command.sink = "http-sink";
+ command.output = "yaml";
+
+ command.steps = new String[] {
+ "set-body-action",
+ "log-action"
+ };
+
+ command.properties = new String[] {
+ "step-1.value=\"Camel rocks!\"",
+ "step-2.showHeaders=true",
+ "step-2.showExchangePattern=false"
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-http
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: "hello world"
+ steps:
+ - ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: set-body-action
+ properties:
+ value: "Camel rocks!"
+ - ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: log-action
+ properties:
+ showHeaders: true
+ showExchangePattern: false
+ sink:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: http-sink
+ properties:
+ url: "https://my-service/path"
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindWithUriStepsAndProperties() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-http";
+ command.source = "timer-source";
+ command.sink = "http-sink";
+ command.output = "yaml";
+
+ command.steps = new String[] {
+ "set-body-action",
+ "log:info"
+ };
+
+ command.properties = new String[] {
+ "step-1.value=\"Camel rocks!\"",
+ "step-2.showHeaders=true"
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-http
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: "hello world"
+ steps:
+ - ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: set-body-action
+ properties:
+ value: "Camel rocks!"
+ - uri: log:info
+ properties:
+ showHeaders: true
+ sink:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: http-sink
+ properties:
+ url: "https://my-service/path"
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindWithUriStepsAndUriProperties() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-http";
+ command.source = "timer-source";
+ command.sink = "http-sink";
+ command.output = "yaml";
+
+ command.steps = new String[] {
+ "set-body-action",
+ "log:info?showExchangePattern=false&showStreams=true"
+ };
+
+ command.properties = new String[] {
+ "step-1.value=\"Camel rocks!\"",
+ "step-2.showHeaders=true"
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-http
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: "hello world"
+ steps:
+ - ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: set-body-action
+ properties:
+ value: "Camel rocks!"
+ - uri: log:info
+ properties:
+ showStreams: true
+ showHeaders: true
+ showExchangePattern: false
+ sink:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: http-sink
+ properties:
+ url: "https://my-service/path"
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindKameletSourceToUri() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer-source";
+ command.sink = "log:info";
+ command.output = "yaml";
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-log
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: "hello world"
+ sink:
+ uri: log:info
+ #properties:
+ #key: "value"
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindKameletSourceToUriWithProperties() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer-source";
+ command.sink = "log:info";
+ command.output = "yaml";
+
+ command.properties = new String[] {
+ "source.message=Hello",
+ "sink.showHeaders=true",
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-log
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: Hello
+ sink:
+ uri: log:info
+ properties:
+ showHeaders: true
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindKameletSourceToUriWithUriProperties() throws
Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer-source";
+ command.sink = "log:info?showStreams=false";
+ command.output = "yaml";
+
+ command.properties = new String[] {
+ "source.message=Hello",
+ "sink.showHeaders=true",
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-log
+ spec:
+ source:
+ ref:
+ kind: Kamelet
+ apiVersion: camel.apache.org/v1
+ name: timer-source
+ properties:
+ message: Hello
+ sink:
+ uri: log:info
+ properties:
+ showStreams: false
+ showHeaders: true
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindUriToUri() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer:tick";
+ command.sink = "log:info";
+ command.output = "yaml";
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-log
+ spec:
+ source:
+ uri: timer:tick
+ #properties:
+ #key: "value"
+ sink:
+ uri: log:info
+ #properties:
+ #key: "value"
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindUriToUriWithProperties() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer:tick";
+ command.sink = "log:info";
+ command.output = "yaml";
+
+ command.properties = new String[] {
+ "source.message=Hello",
+ "sink.showHeaders=true",
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-log
+ spec:
+ source:
+ uri: timer:tick
+ properties:
+ message: Hello
+ sink:
+ uri: log:info
+ properties:
+ showHeaders: true
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldBindUriToUriWithUriProperties() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer:tick?period=10000";
+ command.sink = "log:info?showStreams=false";
+ command.output = "yaml";
+
+ command.properties = new String[] {
+ "source.message=Hello",
+ "sink.showHeaders=true",
+ };
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ apiVersion: camel.apache.org/v1
+ kind: Pipe
+ metadata:
+ name: timer-to-log
+ spec:
+ source:
+ uri: timer:tick
+ properties:
+ period: 10000
+ message: Hello
+ sink:
+ uri: log:info
+ properties:
+ showStreams: false
+ showHeaders: true
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldSupportJsonOutput() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer-source";
+ command.sink = "log-sink";
+ command.output = "json";
+
+ command.doCall();
+
+ String output = printer.getOutput();
+ Assertions.assertEquals("""
+ {
+ "apiVersion": "camel.apache.org/v1",
+ "kind": "Pipe",
+ "metadata": {
+ "name": "timer-to-log"
+ },
+ "spec": {
+ "source": {
+ "ref": {
+ "kind": "Kamelet",
+ "apiVersion": "camel.apache.org/v1",
+ "name": "timer-source"
+ },
+ "properties": {
+ "message": "hello world"
+ }
+ },
+ "sink": {
+ "ref": {
+ "kind": "Kamelet",
+ "apiVersion": "camel.apache.org/v1",
+ "name": "log-sink"
+ }
+ }
+ }
+ }
+ """.trim(), output);
+ }
+
+ @Test
+ public void shouldHandleUnsupportedOutputFormat() throws Exception {
+ Bind command = new Bind(new CamelJBangMain().withPrinter(printer));
+ command.file = "timer-to-log";
+ command.source = "timer-source";
+ command.sink = "log-sink";
+ command.output = "wrong";
+
+ Assertions.assertEquals(-1, command.doCall());
+
+ Assertions.assertEquals("Unsupported output format 'wrong' (supported:
file, yaml, json)", printer.getOutput());
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/KubernetesHelper.java
b/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/KubernetesHelper.java
index 61944dc85b7..20b44d7a5fa 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/KubernetesHelper.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-k/src/main/java/org/apache/camel/dsl/jbang/core/commands/k/KubernetesHelper.java
@@ -17,7 +17,6 @@
package org.apache.camel.dsl.jbang.core.commands.k;
-import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@@ -31,14 +30,10 @@ import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientBuilder;
+import org.apache.camel.dsl.jbang.core.common.YamlHelper;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.StringHelper;
-import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
-import org.yaml.snakeyaml.introspector.Property;
-import org.yaml.snakeyaml.nodes.NodeTuple;
-import org.yaml.snakeyaml.nodes.Tag;
-import org.yaml.snakeyaml.representer.Representer;
/**
* Helper class provides access to cached Kubernetes client. Also provides
access to generic Json and Yaml mappers.
@@ -103,22 +98,7 @@ public final class KubernetesHelper {
* @return
*/
public static Yaml yaml() {
- Representer representer = new Representer(new DumperOptions()) {
- @Override
- protected NodeTuple representJavaBeanProperty(
- Object javaBean, Property property, Object propertyValue,
Tag customTag) {
- // if value of property is null, ignore it.
- if (propertyValue == null || (propertyValue instanceof
Collection && ((Collection<?>) propertyValue).isEmpty())
- ||
- (propertyValue instanceof Map && ((Map<?, ?>)
propertyValue).isEmpty())) {
- return null;
- } else {
- return super.representJavaBeanProperty(javaBean, property,
propertyValue, customTag);
- }
- }
- };
- representer.getPropertyUtils().setSkipMissingProperties(true);
- return new Yaml(representer);
+ return YamlHelper.yaml();
}
public static ObjectMapper json() {