davsclaus commented on code in PR #27646:
URL: https://github.com/apache/camel/pull/27646#discussion_r4236604730


##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java:
##########
@@ -123,6 +123,7 @@ public void execute(String... args) {
                         .addSubcommand("route-dump", new CommandLine(new 
CamelRouteDumpAction(this)))
                         .addSubcommand("route-structure", new CommandLine(new 
CamelRouteStructureAction(this)))
                         .addSubcommand("route-topology", new CommandLine(new 
CamelRouteTopologyAction(this)))
+                        .addSubcommand("semantic-evaluate", new 
CommandLine(new SemanticEvaluate(this)))

Review Comment:
   Naming question: `camel eval expression` already evaluates against a running 
integration with `--body`, `--header` and `--variable`, which are the same 
sample-exchange options used here. Would `camel eval semantic` fit better than 
`camel cmd semantic-evaluate`? We would get `camel eval expression` / `camel 
eval semantic` side by side, and the command name would not repeat the verb. 
I'm not strongly against `cmd semantic-evaluate`, but the JIRA left the names 
open and they are hard to change after a release, so let's decide before merge.



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSemantic.java:
##########
@@ -0,0 +1,105 @@
+/*
+ * 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.action;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import picocli.CommandLine;
+
[email protected](name = "semantic", description = "List semantic 
definitions and expert contracts",
+                     sortOptions = false, showDefaultValues = true)
+public class CamelSemantic extends SemanticActionCommand {
+
+    @CommandLine.Option(names = "--expert", description = "Show the operations 
and parameter contract of this expert")
+    String expert;
+
+    public CamelSemantic(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    protected JsonObject request() {
+        if (expert != null && expert.isBlank()) {
+            throw new IllegalArgumentException("--expert must not be blank");
+        }
+        JsonObject request = new JsonObject();
+        request.put("action", "semantic-metadata");
+        if (expert == null) {
+            request.put("overview", true);
+        } else {
+            request.put("expert", expert);
+        }
+        return request;
+    }
+
+    @Override
+    protected int printResponse(JsonObject response) {
+        if (expert != null) {
+            JsonArray operations = response.getCollection("operations");
+            if (operations == null || operations.isEmpty()) {
+                return error(3, "No operations available for semantic expert: 
" + expert, null);
+            }
+        }
+        return super.printResponse(response);
+    }
+
+    @Override
+    protected void render(JsonObject response) {
+        if (expert != null) {
+            printer().println("Expert: " + expert);
+            renderOperations(response.getCollection("operations"));
+            return;
+        }
+        printer().println("Definitions:");
+        printer().println("NAME\tEXPERT\tOPERATION\tSTATE\tRESULT 
TYPE\tERROR");

Review Comment:
   The other `camel get` commands render tables with 
`AsciiTable.getTable(AsciiTable.NO_BORDERS, ...)`. That includes `get 
route-controller`, which also targets a single integration. Tab-separated 
output looks out of place next to them and does not line up in a terminal. 
Could this use the same `AsciiTable` and `Column` style for the definitions and 
experts lists? Scripts already have `--json` as the machine-readable format, so 
the text output can be laid out for people.



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/exceptionhandler/MissingPluginParameterExceptionHandler.java:
##########
@@ -33,6 +34,9 @@ public class MissingPluginParameterExceptionHandler 
implements IParameterExcepti
     public int handleParseException(ParameterException ex, String[] args) 
throws Exception {
         CommandLine cmd = ex.getCommandLine();
         PrintWriter err = cmd.getErr();
+        if (cmd.getCommand() instanceof SemanticActionCommand semantic) {

Review Comment:
   This is the generic parse-error handler for every command, and now it 
depends on one concrete command type. A small interface, for example 
`UsageErrorHandler { int usageError(String message, String[] args); }`, 
implemented by `SemanticActionCommand`, would keep the handler generic. The 
upcoming CAMEL-25389 work (central error handling and the exit-code contract) 
could then adopt it for other commands without adding more `instanceof` checks 
here.



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticEvaluate.java:
##########
@@ -0,0 +1,168 @@
+/*
+ * 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.action;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import picocli.CommandLine;
+
[email protected](name = "semantic-evaluate", description = "Evaluate a 
semantic definition or expert operation",
+                     sortOptions = false, showDefaultValues = true)
+public class SemanticEvaluate extends SemanticActionCommand {
+
+    private static final ObjectReader JSON_VALUE = new 
ObjectMapper().readerFor(Object.class)
+            .with(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
+
+    @CommandLine.Option(names = "--evaluation", description = "Named semantic 
definition to evaluate")
+    String evaluation;
+
+    @CommandLine.Option(names = "--body", description = "Sample message body 
(text, file:path or json:value)")
+    String body;
+
+    @CommandLine.Option(names = "--header", description = "Sample header 
(key=value; use json: for a typed value)")
+    List<String> headers;
+
+    @CommandLine.Option(names = "--variable",
+                        description = "Sample exchange-local variable 
(key=value; use json: for a typed value)")
+    List<String> variables;
+
+    @CommandLine.Option(names = "--expert", description = "Expert to call 
directly")
+    String expert;
+
+    @CommandLine.Option(names = "--operation", description = "Expert operation 
to call directly")
+    String operation;
+
+    @CommandLine.Option(names = "--input", description = "Direct expert input 
(text, file:path or json:value)")
+    String input;
+
+    @CommandLine.Option(names = "--parameter", description = "Expert parameter 
(key=value; use json: for a typed value)")
+    List<String> parameters;
+
+    @CommandLine.Option(names = "--evaluation-timeout", defaultValue = "50000",
+                        description = "Provider evaluation timeout in 
milliseconds (1-50000)")
+    long evaluationTimeout = 50000;
+
+    public SemanticEvaluate(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    protected JsonObject request() {
+        if (evaluationTimeout < 1 || evaluationTimeout > 50000) {
+            throw new IllegalArgumentException("--evaluation-timeout must be 
between 1 and 50000 milliseconds");
+        }
+        JsonObject request = new JsonObject();
+        request.put("action", "semantic-evaluate");
+        request.put("timeout", evaluationTimeout);
+        if (evaluation != null) {
+            requireText(evaluation, "--evaluation");
+            if (expert != null || operation != null || input != null || 
parameters != null) {
+                throw new IllegalArgumentException("Choose --evaluation or 
--expert with --operation and --input");
+            }
+            request.put("evaluation", evaluation);
+            if (body != null) {
+                request.put("body", value(body, true));
+            }
+            if (headers != null) {
+                request.put("headers", values(headers, "--header"));
+            }
+            if (variables != null) {
+                JsonObject sampleVariables = values(variables, "--variable");
+                if (sampleVariables.keySet().stream().anyMatch(key -> 
key.contains(":"))) {
+                    throw new IllegalArgumentException(
+                            "Sample variables must have exchange-local names 
without a repository prefix");
+                }
+                request.put("variables", sampleVariables);
+            }
+        } else {
+            requireText(expert, "--expert");

Review Comment:
   With neither `--evaluation` nor `--expert` given (for example `camel cmd 
semantic-evaluate my-app`), the error is `--expert is required and must not be 
blank`. That steers users toward the direct-call mode, although a named 
evaluation is the more common case. Suggestion: when all of `evaluation`, 
`expert`, `operation` and `input` are null, report `Choose --evaluation=<name>, 
or --expert with --operation and --input` (still exit 2).



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticActionCommand.java:
##########
@@ -0,0 +1,151 @@
+/*
+ * 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.action;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import org.apache.camel.util.json.JsonObject;
+import picocli.CommandLine;
+
+public abstract class SemanticActionCommand extends ActionBaseCommand {
+
+    @CommandLine.Parameters(description = "Name or pid of a running Camel 
integration", arity = "0..1")
+    String name = "*";
+
+    @CommandLine.Option(names = "--json", description = "Output a single JSON 
document")
+    boolean json;
+
+    @CommandLine.Option(names = "--timeout", defaultValue = "60000",
+                        description = "Timeout in milliseconds waiting for the 
integration")
+    long timeout = 60000;
+
+    @CommandLine.Spec
+    CommandLine.Model.CommandSpec commandSpec;
+
+    protected SemanticActionCommand(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doCall() {
+        try {
+            if (timeout <= 0) {
+                return error(2, "--timeout must be greater than zero", null);
+            }
+            JsonObject request;
+            try {
+                request = request();
+            } catch (IllegalArgumentException e) {
+                return error(2, e.getMessage(), null);
+            }
+            List<Long> pids = 
findPids(name).stream().filter(this::isRunningIntegration).toList();
+            if (pids.isEmpty()) {
+                return error(3, "No running Camel integration matches: " + 
name, null);
+            }
+            if (pids.size() != 1) {
+                return error(3, "Multiple Camel integrations match " + name + 
": " + pids + ". Specify a pid.", null);
+            }
+            JsonObject response = execute(pids.get(0), request);
+            if (response == null) {

Review Comment:
   If the selected application runs an older Camel version, its connector only 
reads the legacy `{pid}-action.json`. It never picks up 
`{pid}-action-{id}.json`, so the CLI waits the full `--timeout` (60 s by 
default) and exits 4 with "No reply". The exit-code table says exit 3 for 
"unavailable semantic tooling". This is likely to happen with `camel run 
--camel-version=...` or an older Spring Boot or Quarkus app.
   
   The status file already carries `context.version` (see 
`CamelContextStatus`), so `isRunningIntegration` could also return exit 3 
straight away when the version is older than the first release with per-request 
action files and the semantic consoles (4.23). That gives a clear diagnostic 
instead of a one-minute hang.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to