This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-24054 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 5d7562bd8b350e90acd82bbe661264be6f8d6958 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Jul 13 19:43:49 2026 +0200 CAMEL-24054: Add POST support to dev consoles and variables to eval-language Co-Authored-By: Claude Opus 4.6 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../platform/http/main/ManagementHttpServer.java | 21 +++- .../apache/camel/dev-console/eval-language.json | 14 +++ .../camel/impl/console/EvalLanguageDevConsole.java | 11 +++ .../impl/console/EvalLanguageDevConsoleTest.java | 106 +++++++++++++++++++++ .../camel-jbang-eval-expression.adoc | 1 + .../camel/cli/connector/LocalCliConnector.java | 22 ++++- .../META-INF/camel-jbang-commands-metadata.json | 2 +- .../commands/action/EvalExpressionCommand.java | 18 ++++ 8 files changed, 186 insertions(+), 9 deletions(-) diff --git a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java index df77e4904c15..7fd07e9a0acf 100644 --- a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java +++ b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/ManagementHttpServer.java @@ -995,13 +995,15 @@ public class ManagementHttpServer extends ServiceSupport implements CamelContext protected void setupDevConsole() { Route dev = router.route("/q/dev"); - dev.method(HttpMethod.GET); + dev.method(HttpMethod.GET).method(HttpMethod.POST); dev.produces("text/plain"); dev.produces("application/json"); + dev.handler(BodyHandler.create(false)); Route devSub = router.route("/q/dev/*"); - devSub.method(HttpMethod.GET); + devSub.method(HttpMethod.GET).method(HttpMethod.POST); devSub.produces("text/plain"); devSub.produces("application/json"); + devSub.handler(BodyHandler.create(false)); Handler<RoutingContext> handler = new Handler<RoutingContext>() { @Override @@ -1083,6 +1085,19 @@ public class ManagementHttpServer extends ServiceSupport implements CamelContext } else { Map<String, Object> params = new HashMap<>(); ctx.queryParams().forEach(params::put); + if (ctx.request().method() == HttpMethod.POST) { + String jsonBody = ctx.body() != null ? ctx.body().asString() : null; + if (jsonBody != null && !jsonBody.isBlank()) { + try { + Object parsed = Jsoner.deserialize(jsonBody); + if (parsed instanceof JsonObject jo) { + jo.forEach((k, v) -> params.put(k, v)); + } + } catch (Exception e) { + // ignore invalid JSON + } + } + } params.put(Exchange.HTTP_PATH, path); StringBuilder sb = new StringBuilder(); JsonObject root = new JsonObject(); @@ -1127,7 +1142,7 @@ public class ManagementHttpServer extends ServiceSupport implements CamelContext dev.blockingHandler(handler); devSub.blockingHandler(handler); - platformHttpComponent.addHttpManagementEndpoint("/q/dev", "GET", null, + platformHttpComponent.addHttpManagementEndpoint("/q/dev", "GET,POST", null, "text/plain,application/json", null); } diff --git a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/eval-language.json b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/eval-language.json index 35c058585559..efcadb3915cf 100644 --- a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/eval-language.json +++ b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/eval-language.json @@ -83,6 +83,20 @@ "autowired": false, "secret": false, "description": "Template to use for executing simple language function" + }, + "variables": { + "index": 5, + "kind": "option", + "displayName": "Variables", + "group": "query", + "label": "query", + "required": false, + "type": "string", + "javaType": "java.lang.String", + "deprecated": false, + "autowired": false, + "secret": false, + "description": "Optional exchange variables" } } } diff --git a/core/camel-console/src/main/java/org/apache/camel/impl/console/EvalLanguageDevConsole.java b/core/camel-console/src/main/java/org/apache/camel/impl/console/EvalLanguageDevConsole.java index f14d13bca504..2eeea51acd74 100644 --- a/core/camel-console/src/main/java/org/apache/camel/impl/console/EvalLanguageDevConsole.java +++ b/core/camel-console/src/main/java/org/apache/camel/impl/console/EvalLanguageDevConsole.java @@ -48,6 +48,9 @@ public class EvalLanguageDevConsole extends AbstractDevConsole { @Metadata(label = "query", description = "Optional message headers", javaType = "java.lang.String") public static final String HEADERS = "headers"; + @Metadata(label = "query", description = "Optional exchange variables", javaType = "java.lang.String") + public static final String VARIABLES = "variables"; + public EvalLanguageDevConsole() { super("camel", "eval-language", "Evaluate Language", "Evaluate Language and display result"); } @@ -68,6 +71,10 @@ public class EvalLanguageDevConsole extends AbstractDevConsole { if (headers instanceof Map map) { dummy.getMessage().setHeaders(map); } + var variables = options.get(VARIABLES); + if (variables instanceof Map map2) { + map2.forEach((k, v) -> dummy.setVariable(k.toString(), v)); + } String out; boolean predicate = optionBoolean(options, PREDICATE, false); @@ -101,6 +108,10 @@ public class EvalLanguageDevConsole extends AbstractDevConsole { if (headers instanceof Map map) { dummy.getMessage().setHeaders(map); } + var variables = options.get(VARIABLES); + if (variables instanceof Map map2) { + map2.forEach((k, v) -> dummy.setVariable(k.toString(), v)); + } Exception cause = null; String out = null; diff --git a/core/camel-console/src/test/java/org/apache/camel/impl/console/EvalLanguageDevConsoleTest.java b/core/camel-console/src/test/java/org/apache/camel/impl/console/EvalLanguageDevConsoleTest.java new file mode 100644 index 000000000000..59e16c302000 --- /dev/null +++ b/core/camel-console/src/test/java/org/apache/camel/impl/console/EvalLanguageDevConsoleTest.java @@ -0,0 +1,106 @@ +/* + * 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.impl.console; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.camel.console.DevConsole; +import org.apache.camel.util.json.JsonObject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class EvalLanguageDevConsoleTest extends AbstractDevConsoleTest { + + @Test + public void testEvalWithBody() { + DevConsole con = assertConsoleExists("eval-language", "camel"); + + JsonObject out = callJson(con, Map.of( + "language", "simple", + "template", "${body}", + "body", "Hello World")); + + Assertions.assertEquals("success", out.getString("status")); + Assertions.assertEquals("Hello World", out.getString("result")); + } + + @Test + public void testEvalWithHeaders() { + DevConsole con = assertConsoleExists("eval-language", "camel"); + + Map<String, String> headers = new LinkedHashMap<>(); + headers.put("myKey", "myValue"); + + JsonObject out = callJson(con, Map.of( + "language", "simple", + "template", "${header.myKey}", + "body", "", + "headers", headers)); + + Assertions.assertEquals("success", out.getString("status")); + Assertions.assertEquals("myValue", out.getString("result")); + } + + @Test + public void testEvalWithVariables() { + DevConsole con = assertConsoleExists("eval-language", "camel"); + + Map<String, String> variables = new LinkedHashMap<>(); + variables.put("foo", "bar"); + + JsonObject out = callJson(con, Map.of( + "language", "simple", + "template", "${variable.foo}", + "body", "", + "variables", variables)); + + Assertions.assertEquals("success", out.getString("status")); + Assertions.assertEquals("bar", out.getString("result")); + } + + @Test + public void testEvalPredicate() { + DevConsole con = assertConsoleExists("eval-language", "camel"); + + JsonObject out = callJson(con, Map.of( + "language", "simple", + "template", "${body} == 'Hello'", + "body", "Hello", + "predicate", true)); + + Assertions.assertEquals("success", out.getString("status")); + Assertions.assertEquals("true", out.getString("result")); + } + + @Test + public void testEvalWithBodyAndVariables() { + DevConsole con = assertConsoleExists("eval-language", "camel"); + + Map<String, String> variables = new LinkedHashMap<>(); + variables.put("greeting", "Hi"); + + JsonObject out = callJson(con, Map.of( + "language", "simple", + "template", "${variable.greeting} ${body}", + "body", "World", + "variables", variables)); + + Assertions.assertEquals("success", out.getString("status")); + Assertions.assertEquals("Hi World", out.getString("result")); + } +} diff --git a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-eval-expression.adoc b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-eval-expression.adoc index fad65d52ced2..258ebdad7dc5 100644 --- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-eval-expression.adoc +++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-eval-expression.adoc @@ -30,6 +30,7 @@ camel eval expression [options] | `--repo,--repos` | Additional maven repositories for download on-demand (Use commas to separate multiple repositories) | | String | `--template` | The template to use for evaluating (prefix with file: to refer to loading template from file) | | String | `--timeout` | Timeout in millis waiting for evaluation to be done | 10000 | long +| `--variable` | Exchange variable (key=value) | | List | `--watch` | Execute periodically and showing output fullscreen | | boolean | `-h,--help` | Display the help and sub-commands | | boolean |=== 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 eee6b7a0c13d..3e6985b8c941 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 @@ -1058,16 +1058,28 @@ public class LocalCliConnector extends ServiceSupport implements CliConnector, C boolean predicate = root.getBooleanOrDefault("predicate", false); String template = root.getStringOrDefault("template", ""); String body = root.getStringOrDefault("body", ""); - Map<String, String> map = new LinkedHashMap<>(); + Map<String, String> headerMap = new LinkedHashMap<>(); Collection<JsonObject> headers = root.getCollection("headers"); if (headers != null) { - map = new LinkedHashMap<>(); for (JsonObject jo : headers) { - map.put(jo.getString("key"), jo.getString("value")); + headerMap.put(jo.getString("key"), jo.getString("value")); } } - JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON, - Map.of("language", lan, "predicate", predicate, "template", template, "body", body, "headers", map)); + Map<String, String> variableMap = new LinkedHashMap<>(); + Collection<JsonObject> variables = root.getCollection("variables"); + if (variables != null) { + for (JsonObject jo : variables) { + variableMap.put(jo.getString("key"), jo.getString("value")); + } + } + Map<String, Object> params = new HashMap<>(); + params.put("language", lan); + params.put("predicate", predicate); + params.put("template", template); + params.put("body", body); + params.put("headers", headerMap); + params.put("variables", variableMap); + JsonObject json = (JsonObject) dc.call(DevConsole.MediaType.JSON, params); LOG.trace("Updating output file: {}", outputFile); IOHelper.writeText(json.toJson(), outputFile); } else { diff --git a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json index 092515439667..c3463703d766 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json +++ b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json @@ -12,7 +12,7 @@ { "name": "dirty", "fullName": "dirty", "description": "Check if there are dirty files from previous Camel runs that did not terminate gracefully", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.Dirty", "options": [ { "names": "--clean", "description": "Clean dirty files which are no longer in use", "defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", " [...] { "name": "doc", "fullName": "doc", "description": "Shows documentation for kamelet, component, and other Camel resources", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.catalog.CatalogDoc", "options": [ { "names": "--camel-version", "description": "To use a different Camel version than the default version", "javaType": "java.lang.String", "type": "string" }, { "names": "--download", "description": "Whether to allow automatic downloading JAR dependencies (over the internet [...] { "name": "doctor", "fullName": "doctor", "description": "Checks the environment and reports potential issues", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Doctor", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ] }, - { "name": "eval", "fullName": "eval", "description": "Evaluate Camel expressions and scripts", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.EvalCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "expression", "fullName": "eval expression", "description": "Evaluates Camel expression", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.EvalEx [...] + { "name": "eval", "fullName": "eval", "description": "Evaluate Camel expressions and scripts", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.EvalCommand", "options": [ { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "expression", "fullName": "eval expression", "description": "Evaluates Camel expression", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.EvalEx [...] { "name": "explain", "fullName": "explain", "description": "Explain what a Camel route does using AI\/LLM", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Explain", "options": [ { "names": "--api-key", "description": "API key for authentication. Also reads ANTHROPIC_API_KEY, OPENAI_API_KEY, or LLM_API_KEY env vars", "javaType": "java.lang.String", "type": "string" }, { "names": "--api-type", "description": "API type: 'ollama', 'openai' (OpenAI-compatible), or 'anthropic' (A [...] { "name": "export", "fullName": "export", "description": "Export to other runtimes (Camel Main, Spring Boot, or Quarkus)", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Export", "options": [ { "names": "--build-property", "description": "Maven build properties, ex. --build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, { "names": "--camel-spring-boot-version", "description": "Camel version to use with Spring Boot", "javaType": "java.lang.String", "ty [...] { "name": "get", "fullName": "get", "description": "Get status of Camel integrations", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.process.CamelStatus", "options": [ { "names": "--watch", "description": "Execute periodically and showing output fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "activity", "fullName": " [...] diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/EvalExpressionCommand.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/EvalExpressionCommand.java index dbbef4f163de..1501c278f966 100644 --- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/EvalExpressionCommand.java +++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/EvalExpressionCommand.java @@ -76,6 +76,10 @@ public class EvalExpressionCommand extends ActionWatchCommand { description = "Message header (key=value)") List<String> headers; + @CommandLine.Option(names = { "--variable" }, + description = "Exchange variable (key=value)") + List<String> variables; + @CommandLine.Option(names = { "--timeout" }, defaultValue = "10000", description = "Timeout in millis waiting for evaluation to be done") long timeout = 10000; @@ -165,6 +169,20 @@ public class EvalExpressionCommand extends ActionWatchCommand { } root.put("headers", arr); } + if (variables != null) { + JsonArray arr = new JsonArray(); + for (String v : variables) { + JsonObject jo = new JsonObject(); + if (!v.contains("=")) { + printer().printErr("Variable must be in key=value format, was: " + v); + return 1; + } + jo.put("key", StringHelper.before(v, "=")); + jo.put("value", StringHelper.after(v, "=")); + arr.add(jo); + } + root.put("variables", arr); + } Path outputFile = getOutputFile(Long.toString(pid)); PathUtils.deleteFile(outputFile);
