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 d0f88b631651 CAMEL-25195: camel-console - send dev console supports 
base64 encoded bodies (#27141)
d0f88b631651 is described below

commit d0f88b631651f360f0639fa4c58e970678215e17
Author: Federico Mariani <[email protected]>
AuthorDate: Wed Sep 30 20:58:16 2026 +0200

    CAMEL-25195: camel-console - send dev console supports base64 encoded 
bodies (#27141)
    
    The send dev console (and the send action of camel-cli-connector) only 
accepted the body as text, so tools could not send binary payloads. A new 
bodyEncoding option (base64) decodes the body and sends it as byte[]; the file: 
prefix is not interpreted for encoded bodies, and unsupported values are 
reported as an error. LocalCliConnector forwards bodyEncoding from the send 
action.
    
    Co-Authored-By: Claude <[email protected]>
---
 .../apache/camel/catalog/dev-consoles-openapi.json |  7 +++
 .../apache/camel/catalog/dev-consoles/send.json    | 27 +++++++--
 .../org/apache/camel/dev-console/send.json         | 27 +++++++--
 .../apache/camel/impl/console/SendDevConsole.java  | 19 ++++++-
 .../camel/impl/console/SendDevConsoleTest.java     | 65 ++++++++++++++++++++++
 .../camel/cli/connector/LocalCliConnector.java     |  4 ++
 6 files changed, 136 insertions(+), 13 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
index 2146b5bcc7fa..297bd579d51e 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles-openapi.json
@@ -7049,6 +7049,13 @@
                                                                                
"type": "string",
                                                                                
"description": "The message body to send. Can refer to files using file: prefix"
                                                                        },
+                                                                       
"bodyEncoding": {
+                                                                               
"type": "string",
+                                                                               
"enum": [
+                                                                               
        "base64"
+                                                                               
],
+                                                                               
"description": "Encoding of the body option. Use base64 to send binary content, 
which is decoded and sent as byte[]."
+                                                                       },
                                                                        
"bodyMaxChars": {
                                                                                
"type": "integer",
                                                                                
"default": 32768,
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/send.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/send.json
index 5813744eba48..83206b04163d 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/send.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/send.json
@@ -27,9 +27,26 @@
       "secret": false,
       "description": "The message body to send. Can refer to files using file: 
prefix"
     },
-    "bodyMaxChars": {
+    "bodyEncoding": {
       "index": 1,
       "kind": "option",
+      "displayName": "Body Encoding",
+      "group": "query",
+      "label": "query",
+      "required": false,
+      "type": "string",
+      "javaType": "java.lang.String",
+      "enum": [
+        "base64"
+      ],
+      "deprecated": false,
+      "autowired": false,
+      "secret": false,
+      "description": "Encoding of the body option. Use base64 to send binary 
content, which is decoded and sent as byte[]."
+    },
+    "bodyMaxChars": {
+      "index": 2,
+      "kind": "option",
       "displayName": "Body Max Chars",
       "group": "query",
       "label": "query",
@@ -43,7 +60,7 @@
       "description": "Maximum size of the message body to include in the dump"
     },
     "endpoint": {
-      "index": 2,
+      "index": 3,
       "kind": "option",
       "displayName": "Endpoint",
       "group": "query",
@@ -57,7 +74,7 @@
       "description": "Endpoint for where to send messages (can also refer to a 
route id, endpoint pattern)"
     },
     "exchangePattern": {
-      "index": 3,
+      "index": 4,
       "kind": "option",
       "displayName": "Exchange Pattern",
       "group": "query",
@@ -75,7 +92,7 @@
       "description": "Exchange pattern when sending"
     },
     "poll": {
-      "index": 4,
+      "index": 5,
       "kind": "option",
       "displayName": "Poll",
       "group": "query",
@@ -89,7 +106,7 @@
       "description": "Whether to poll message from the endpoint instead of 
sending"
     },
     "pollTimeout": {
-      "index": 5,
+      "index": 6,
       "kind": "option",
       "displayName": "Poll Timeout",
       "group": "query",
diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/send.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/send.json
index 5813744eba48..83206b04163d 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/send.json
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/send.json
@@ -27,9 +27,26 @@
       "secret": false,
       "description": "The message body to send. Can refer to files using file: 
prefix"
     },
-    "bodyMaxChars": {
+    "bodyEncoding": {
       "index": 1,
       "kind": "option",
+      "displayName": "Body Encoding",
+      "group": "query",
+      "label": "query",
+      "required": false,
+      "type": "string",
+      "javaType": "java.lang.String",
+      "enum": [
+        "base64"
+      ],
+      "deprecated": false,
+      "autowired": false,
+      "secret": false,
+      "description": "Encoding of the body option. Use base64 to send binary 
content, which is decoded and sent as byte[]."
+    },
+    "bodyMaxChars": {
+      "index": 2,
+      "kind": "option",
       "displayName": "Body Max Chars",
       "group": "query",
       "label": "query",
@@ -43,7 +60,7 @@
       "description": "Maximum size of the message body to include in the dump"
     },
     "endpoint": {
-      "index": 2,
+      "index": 3,
       "kind": "option",
       "displayName": "Endpoint",
       "group": "query",
@@ -57,7 +74,7 @@
       "description": "Endpoint for where to send messages (can also refer to a 
route id, endpoint pattern)"
     },
     "exchangePattern": {
-      "index": 3,
+      "index": 4,
       "kind": "option",
       "displayName": "Exchange Pattern",
       "group": "query",
@@ -75,7 +92,7 @@
       "description": "Exchange pattern when sending"
     },
     "poll": {
-      "index": 4,
+      "index": 5,
       "kind": "option",
       "displayName": "Poll",
       "group": "query",
@@ -89,7 +106,7 @@
       "description": "Whether to poll message from the endpoint instead of 
sending"
     },
     "pollTimeout": {
-      "index": 5,
+      "index": 6,
       "kind": "option",
       "displayName": "Poll Timeout",
       "group": "query",
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/SendDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/SendDevConsole.java
index 9735436acf41..b015c30b303f 100644
--- 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/SendDevConsole.java
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/SendDevConsole.java
@@ -19,6 +19,7 @@ package org.apache.camel.impl.console;
 import java.io.Closeable;
 import java.io.File;
 import java.io.FileInputStream;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -76,6 +77,11 @@ public class SendDevConsole extends AbstractDevConsole {
               javaType = "java.lang.String")
     public static final String BODY = "body";
 
+    @Metadata(label = "query",
+              description = "Encoding of the body option. Use base64 to send 
binary content, which is decoded and sent as byte[].",
+              javaType = "java.lang.String", enums = "base64")
+    public static final String BODY_ENCODING = "bodyEncoding";
+
     @Metadata(label = "query", description = "Whether to poll message from the 
endpoint instead of sending",
               javaType = "java.lang.Boolean")
     public static final String POLL = "poll";
@@ -261,7 +267,7 @@ public class SendDevConsole extends AbstractDevConsole {
             throws Exception {
         Exchange out = null;
         if (target != null) {
-            final Object inputBody = prepareBody(body);
+            final Object inputBody = prepareBody(body, optionString(options, 
BODY_ENCODING));
             final Map<String, Object> inputHeaders = prepareHeaders(options);
             if (poll) {
                 out = consumer.receive(target, timeout);
@@ -324,7 +330,13 @@ public class SendDevConsole extends AbstractDevConsole {
         return target;
     }
 
-    private Object prepareBody(String body) throws Exception {
+    private Object prepareBody(String body, String bodyEncoding) throws 
Exception {
+        if (bodyEncoding != null) {
+            if (!"base64".equalsIgnoreCase(bodyEncoding)) {
+                throw new IllegalArgumentException("Unsupported bodyEncoding: 
" + bodyEncoding + " (supported: base64)");
+            }
+            return Base64.getDecoder().decode(body);
+        }
         Object b = body;
         if (body.startsWith("file:")) {
             File file = new File(body.substring(5));
@@ -344,7 +356,8 @@ public class SendDevConsole extends AbstractDevConsole {
     }
 
     private static boolean isCustomHeader(String key) {
-        return !BODY.equals(key) && !BODY_MAX_CHARS.equals(key) && 
!POLL.equals(key) && !POLL_TIMEOUT.equals(key)
+        return !BODY.equals(key) && !BODY_ENCODING.equals(key) && 
!BODY_MAX_CHARS.equals(key) && !POLL.equals(key)
+                && !POLL_TIMEOUT.equals(key)
                 && !EXCHANGE_PATTERN.equals(key) && !ENDPOINT.equals(key)
                 && !"CamelHttpPath".equals(key); // do not include ourself 
/q/dev/send
     }
diff --git 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/SendDevConsoleTest.java
 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/SendDevConsoleTest.java
index e0c988c0c5b8..acbc85970291 100644
--- 
a/core/camel-console/src/test/java/org/apache/camel/impl/console/SendDevConsoleTest.java
+++ 
b/core/camel-console/src/test/java/org/apache/camel/impl/console/SendDevConsoleTest.java
@@ -16,15 +16,23 @@
  */
 package org.apache.camel.impl.console;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.Message;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.console.DevConsole;
+import org.apache.camel.support.service.ServiceHelper;
 import org.apache.camel.util.json.JsonObject;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -147,6 +155,63 @@ public class SendDevConsoleTest extends 
AbstractDevConsoleTest {
         assertNotNull(jsonOut.getString("status"));
     }
 
+    @Test
+    public void testSendConsoleBase64Body() throws Exception {
+        DevConsole console = startedSendConsole();
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedMessageCount(1);
+
+        byte[] data = new byte[] { 0, (byte) 0xC3, (byte) 0xA8, (byte) 0xFF };
+        Map<String, Object> options = createOptions("direct:start", 
Base64.getEncoder().encodeToString(data));
+        options.put(SendDevConsole.BODY_ENCODING, "base64");
+
+        JsonObject out = callJson(console, options);
+        assertEquals("success", out.getString("status"), out.toJson());
+        mock.assertIsSatisfied();
+        Message msg = mock.getReceivedExchanges().get(0).getMessage();
+        assertArrayEquals(data, msg.getBody(byte[].class));
+        
assertFalse(msg.getHeaders().containsKey(SendDevConsole.BODY_ENCODING));
+    }
+
+    @Test
+    public void testSendConsoleBase64BodyIsNotAFileReference() throws 
Exception {
+        DevConsole console = startedSendConsole();
+        MockEndpoint mock = getMockEndpoint("mock:result");
+        mock.expectedBodiesReceived("file:pom.xml");
+
+        Map<String, Object> options = createOptions("direct:start",
+                
Base64.getEncoder().encodeToString("file:pom.xml".getBytes(StandardCharsets.UTF_8)));
+        options.put(SendDevConsole.BODY_ENCODING, "base64");
+
+        callJson(console, options);
+        mock.assertIsSatisfied();
+    }
+
+    @Test
+    public void testSendConsoleInvalidBodyEncoding() {
+        DevConsole console = startedSendConsole();
+
+        Map<String, Object> options = createOptions("direct:start", "Hello");
+        options.put(SendDevConsole.BODY_ENCODING, "hex");
+        JsonObject out = callJson(console, options);
+        assertEquals("error", out.getString("status"));
+
+        options = createOptions("direct:start", "not base64!");
+        options.put(SendDevConsole.BODY_ENCODING, "base64");
+        out = callJson(console, options);
+        assertEquals("error", out.getString("status"));
+    }
+
+    /**
+     * The resolved console is not started, so it has no producer to send with.
+     */
+    private DevConsole startedSendConsole() {
+        DevConsole console = assertConsoleExists("send");
+        CamelContextAware.trySetCamelContext(console, context);
+        ServiceHelper.startService(console);
+        return console;
+    }
+
     private Map<String, Object> createOptions(String endpoint, String body) {
         Map<String, Object> options = new HashMap<>();
         options.put(SendDevConsole.ENDPOINT, endpoint);
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index d059599f1b6b..51730c34a4e3 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -682,6 +682,10 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
             if (body != null) {
                 args.put("body", body);
             }
+            String bodyEncoding = root.getString("bodyEncoding");
+            if (bodyEncoding != null) {
+                args.put("bodyEncoding", bodyEncoding);
+            }
             if (exchangePattern != null) {
                 args.put("exchangePattern", exchangePattern);
             }

Reply via email to