You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2024/03/22 17:28:50 UTC

(camel) branch main updated: CAMEL-20601: Support error handler on Camel JBang bind command (#13594)

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

davsclaus 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 5a10130c215 CAMEL-20601: Support error handler on Camel JBang bind command (#13594)
5a10130c215 is described below

commit 5a10130c215ff4cdd75153a162f068838b49514c
Author: Christoph Deppisch <cd...@redhat.com>
AuthorDate: Fri Mar 22 18:28:44 2024 +0100

    CAMEL-20601: Support error handler on Camel JBang bind command (#13594)
    
    - Allow to add error handler to the Pipe resource
    - Support different error handler types (sink, uri, log, none)
    - Support user properties on the error handlers
---
 .../apache/camel/dsl/jbang/core/commands/Bind.java | 108 ++++-
 .../templates/error-handler-log.yaml.tmpl          |   2 +
 .../templates/error-handler-sink-kamelet.yaml.tmpl |   8 +
 .../templates/error-handler-sink-uri.yaml.tmpl     |   5 +
 .../templates/pipe-kamelet-kamelet.yaml.tmpl       |   1 +
 .../resources/templates/pipe-kamelet-uri.yaml.tmpl |   1 +
 .../resources/templates/pipe-uri-kamelet.yaml.tmpl |   1 +
 .../resources/templates/pipe-uri-uri.yaml.tmpl     |   1 +
 .../camel/dsl/jbang/core/commands/BindTest.java    | 472 +++++++++++++++++++++
 9 files changed, 595 insertions(+), 4 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 27b16288bc6..0c6d19e41cc 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
@@ -70,6 +70,10 @@ public class Bind extends CamelCommand {
                         required = true)
     String sink;
 
+    @CommandLine.Option(names = { "--error-handler" },
+            description = "Add error handler (none|log|sink:<endpoint>). Sink endpoints are expected in the format \"[[apigroup/]version:]kind:[namespace/]name\", plain Camel URIs or Kamelet name.")
+    String errorHandler;
+
     @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")
@@ -157,11 +161,78 @@ public class Bind extends CamelCommand {
             stepsContext = sb.toString();
         }
 
+        String errorHandlerContext = "";
+        if (errorHandler != null) {
+            StringBuilder sb = new StringBuilder("\n  errorHandler:\n");
+
+            Map<String, Object> errorHandlerParameters = getProperties("error-handler");
+
+            String[] errorHandlerTokens = errorHandler.split(":", 2);
+            String errorHandlerType = errorHandlerTokens[0];
+
+            String errorHandlerSpec;
+            switch (errorHandlerType) {
+                case "sink":
+                    if (errorHandlerTokens.length != 2) {
+                        printer().println(
+                                "Invalid error handler syntax. Type 'sink' needs an endpoint configuration (ie sink:endpointUri)");
+                        return -1;
+                    }
+                    String endpoint = errorHandlerTokens[1];
+
+                    String sinkType;
+                    Map<String, Object> errorHandlerSinkProperties = getProperties("error-handler.sink");
+
+                    // remove sink properties from error handler parameters
+                    errorHandlerSinkProperties.keySet().stream()
+                            .map(key -> "sink." + key)
+                            .filter(errorHandlerParameters::containsKey)
+                            .forEach(errorHandlerParameters::remove);
+
+                    if (endpoint.contains(":")) {
+                        sinkType = "uri";
+                        if (endpoint.contains("?")) {
+                            String query = StringHelper.after(endpoint, "?");
+                            endpoint = StringHelper.before(endpoint, "?");
+                            if (query != null) {
+                                errorHandlerSinkProperties.putAll(URISupport.parseQuery(query, true));
+                            }
+                        }
+                    } else {
+                        sinkType = "kamelet";
+                        errorHandlerSinkProperties = kameletProperties(endpoint, errorHandlerSinkProperties);
+                    }
+
+                    is = Bind.class.getClassLoader()
+                            .getResourceAsStream("templates/error-handler-sink-%s.yaml.tmpl".formatted(sinkType));
+                    errorHandlerSpec = IOHelper.loadText(is);
+                    IOHelper.close(is);
+                    errorHandlerSpec = errorHandlerSpec.replaceFirst("\\{\\{ \\.Name }}", endpoint);
+                    errorHandlerSpec = errorHandlerSpec.replaceFirst("\\{\\{ \\.ErrorHandlerProperties }}",
+                            asEndpointProperties(errorHandlerSinkProperties, 4));
+                    errorHandlerSpec = errorHandlerSpec.replaceFirst("\\{\\{ \\.ErrorHandlerParameter }}",
+                            asErrorHandlerParameters(errorHandlerParameters));
+                    break;
+                case "log":
+                    is = Bind.class.getClassLoader().getResourceAsStream("templates/error-handler-log.yaml.tmpl");
+                    errorHandlerSpec = IOHelper.loadText(is);
+                    IOHelper.close(is);
+                    errorHandlerSpec = errorHandlerSpec.replaceFirst("\\{\\{ \\.ErrorHandlerParameter }}",
+                            asErrorHandlerParameters(errorHandlerParameters));
+                    break;
+                default:
+                    errorHandlerSpec = "    none: {}";
+            }
+            sb.append(errorHandlerSpec);
+            errorHandlerContext = sb.toString();
+        }
+
         String name = FileUtil.onlyName(file, false);
         context = context.replaceFirst("\\{\\{ \\.Name }}", name);
         context = context.replaceFirst("\\{\\{ \\.Source }}", sourceEndpoint);
         context = context.replaceFirst("\\{\\{ \\.Sink }}", sinkEndpoint);
         context = context.replaceFirst("\\{\\{ \\.Steps }}", stepsContext);
+        context = context.replaceFirst("\\{\\{ \\.ErrorHandler }}", errorHandlerContext);
 
         Map<String, Object> sourceProperties = getProperties("source");
         if ("kamelet".equals(in)) {
@@ -204,6 +275,24 @@ public class Bind extends CamelCommand {
         return 0;
     }
 
+    /**
+     * Creates YAML snippet representing the error handler parameters section.
+     *
+     * @param props the properties to set as error handler parameters.
+     */
+    private String asErrorHandlerParameters(Map<String, Object> props) {
+        if (props.isEmpty()) {
+            return "parameters: {}";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("parameters:\n");
+        for (Map.Entry<String, Object> propertyEntry : props.entrySet()) {
+            sb.append("        ").append(propertyEntry.getKey()).append(": ").append(propertyEntry.getValue()).append("\n");
+        }
+        return sb.toString().trim();
+    }
+
     /**
      * Creates YAML snippet representing the endpoint properties section.
      *
@@ -211,15 +300,26 @@ public class Bind extends CamelCommand {
      * @return
      */
     private String asEndpointProperties(Map<String, Object> props) {
+        return asEndpointProperties(props, 0);
+    }
+
+    /**
+     * Creates YAML snippet representing the endpoint properties section.
+     *
+     * @param  props            the properties to set as endpoint properties.
+     * @param  additionalIndent optional number of additional spaces used as indentation.
+     * @return
+     */
+    private String asEndpointProperties(Map<String, Object> props, int additionalIndent) {
         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();
+            return sb.append("#properties:\n      ").append(" ".repeat(additionalIndent)).append("#key: \"value\"").toString();
         }
 
         sb.append("properties:\n");
         for (Map.Entry<String, Object> propertyEntry : props.entrySet()) {
-            sb.append("      ").append(propertyEntry.getKey()).append(": ")
+            sb.append("      ").append(" ".repeat(additionalIndent)).append(propertyEntry.getKey()).append(": ")
                     .append(propertyEntry.getValue()).append("\n");
         }
         return sb.toString().trim();
@@ -227,7 +327,7 @@ public class Bind extends CamelCommand {
 
     /**
      * 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.
+     * pipe (source, sink, errorHandler, step[1-n]) can have its individual properties.
      *
      * @param  keyPrefix
      * @return
@@ -240,7 +340,7 @@ public class Bind extends CamelCommand {
                     String[] keyValue = propertyExpression.split("=", 2);
                     if (keyValue.length != 2) {
                         printer().printf(
-                                "property '%s' does not follow format [source|sink|step-<n>].<key>=<value>%n",
+                                "property '%s' does not follow format [source|sink|error-handler|step-<n>].<key>=<value>%n",
                                 propertyExpression);
                         continue;
                     }
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-log.yaml.tmpl b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-log.yaml.tmpl
new file mode 100644
index 00000000000..3261b156fb0
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-log.yaml.tmpl
@@ -0,0 +1,2 @@
+    log:
+      {{ .ErrorHandlerParameter }}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-sink-kamelet.yaml.tmpl b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-sink-kamelet.yaml.tmpl
new file mode 100644
index 00000000000..2b418ebb2f2
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-sink-kamelet.yaml.tmpl
@@ -0,0 +1,8 @@
+    sink:
+      endpoint:
+        ref:
+          kind: Kamelet
+          apiVersion: camel.apache.org/v1
+          name: {{ .Name }}
+        {{ .ErrorHandlerProperties }}
+      {{ .ErrorHandlerParameter }}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-sink-uri.yaml.tmpl b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-sink-uri.yaml.tmpl
new file mode 100644
index 00000000000..ff67827b25a
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/error-handler-sink-uri.yaml.tmpl
@@ -0,0 +1,5 @@
+    sink:
+      endpoint:
+        uri: {{ .Name }}
+        {{ .ErrorHandlerProperties }}
+      {{ .ErrorHandlerParameter }}
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-kamelet-kamelet.yaml.tmpl b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-kamelet-kamelet.yaml.tmpl
index 1583542184a..b2536e72670 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-kamelet-kamelet.yaml.tmpl
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/pipe-kamelet-kamelet.yaml.tmpl
@@ -16,3 +16,4 @@ spec:
       apiVersion: camel.apache.org/v1
       name: {{ .Sink }}
     {{ .SinkProperties }}
+{{ .ErrorHandler }}
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 07de5540d3f..560840b1838 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
@@ -13,3 +13,4 @@ spec:
   sink:
     uri: {{ .Sink }}
     {{ .SinkProperties }}
+{{ .ErrorHandler }}
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 9c7b90820a7..44b3bfc296c 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
@@ -13,3 +13,4 @@ spec:
       apiVersion: camel.apache.org/v1
       name: {{ .Sink }}
     {{ .SinkProperties }}
+{{ .ErrorHandler }}
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 b71a4e1f96a..2e0050262fa 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
@@ -10,3 +10,4 @@ spec:
   sink:
     uri: {{ .Sink }}
     {{ .SinkProperties }}
+{{ .ErrorHandler }}
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
index edf952212f9..02cdc70e6e6 100644
--- 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
@@ -567,6 +567,478 @@ class BindTest extends CamelCommandBaseTest {
                 """.trim(), output);
     }
 
+    @Test
+    public void shouldBindKameletSinkErrorHandler() 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.errorHandler = "sink:log-sink";
+
+        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"
+                  errorHandler:
+                    sink:
+                      endpoint:
+                        ref:
+                          kind: Kamelet
+                          apiVersion: camel.apache.org/v1
+                          name: log-sink
+                        #properties:
+                          #key: "value"
+                      parameters: {}
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindKameletSinkErrorHandlerWithParameters() 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.errorHandler = "sink:log-sink";
+
+        command.properties = new String[] {
+                "error-handler.maximumRedeliveries=3",
+                "error-handler.redeliveryDelay=2000"
+        };
+
+        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"
+                  errorHandler:
+                    sink:
+                      endpoint:
+                        ref:
+                          kind: Kamelet
+                          apiVersion: camel.apache.org/v1
+                          name: log-sink
+                        #properties:
+                          #key: "value"
+                      parameters:
+                        redeliveryDelay: 2000
+                        maximumRedeliveries: 3
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindKameletSinkErrorHandlerAndSinkProperties() 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.errorHandler = "sink:log-sink";
+
+        command.properties = new String[] {
+                "error-handler.sink.showHeaders=true",
+                "error-handler.maximumRedeliveries=3",
+                "error-handler.redeliveryDelay=2000"
+        };
+
+        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"
+                  errorHandler:
+                    sink:
+                      endpoint:
+                        ref:
+                          kind: Kamelet
+                          apiVersion: camel.apache.org/v1
+                          name: log-sink
+                        properties:
+                          showHeaders: true
+                      parameters:
+                        redeliveryDelay: 2000
+                        maximumRedeliveries: 3
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindEndpointUriSinkErrorHandler() 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.errorHandler = "sink:log:error";
+
+        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"
+                  errorHandler:
+                    sink:
+                      endpoint:
+                        uri: log:error
+                        #properties:
+                          #key: "value"
+                      parameters: {}
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindEndpointUriSinkErrorHandlerWithParameters() 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.errorHandler = "sink:log:error";
+
+        command.properties = new String[] {
+                "error-handler.maximumRedeliveries=3",
+                "error-handler.redeliveryDelay=2000"
+        };
+
+        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"
+                  errorHandler:
+                    sink:
+                      endpoint:
+                        uri: log:error
+                        #properties:
+                          #key: "value"
+                      parameters:
+                        redeliveryDelay: 2000
+                        maximumRedeliveries: 3
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindEndpointUriSinkErrorHandlerAndSinkProperties() 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.errorHandler = "sink:log:error";
+
+        command.properties = new String[] {
+                "error-handler.sink.showHeaders=true",
+                "error-handler.maximumRedeliveries=3",
+                "error-handler.redeliveryDelay=2000"
+        };
+
+        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"
+                  errorHandler:
+                    sink:
+                      endpoint:
+                        uri: log:error
+                        properties:
+                          showHeaders: true
+                      parameters:
+                        redeliveryDelay: 2000
+                        maximumRedeliveries: 3
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindEndpointUriSinkErrorHandlerAndUriProperties() 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.errorHandler = "sink:log:error?showStreams=false";
+
+        command.properties = new String[] {
+                "error-handler.sink.showHeaders=true",
+                "error-handler.maximumRedeliveries=3",
+                "error-handler.redeliveryDelay=2000"
+        };
+
+        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"
+                  errorHandler:
+                    sink:
+                      endpoint:
+                        uri: log:error
+                        properties:
+                          showStreams: false
+                          showHeaders: true
+                      parameters:
+                        redeliveryDelay: 2000
+                        maximumRedeliveries: 3
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindWithLogErrorHandler() 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.errorHandler = "log";
+
+        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"
+                  errorHandler:
+                    log:
+                      parameters: {}
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindWithLogErrorHandlerWithParameters() 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.errorHandler = "log";
+
+        command.properties = new String[] {
+                "error-handler.maximumRedeliveries=3",
+                "error-handler.redeliveryDelay=2000"
+        };
+
+        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"
+                  errorHandler:
+                    log:
+                      parameters:
+                        redeliveryDelay: 2000
+                        maximumRedeliveries: 3
+                """.trim(), output);
+    }
+
+    @Test
+    public void shouldBindWithNoErrorHandler() 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.errorHandler = "none";
+
+        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"
+                  errorHandler:
+                    none: {}
+                """.trim(), output);
+    }
+
     @Test
     public void shouldSupportJsonOutput() throws Exception {
         Bind command = new Bind(new CamelJBangMain().withPrinter(printer));