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

gnodet 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 dc73cb46757c CAMEL-23236: Add doc --example flag and camel-kit shell 
banner hint (#23347)
dc73cb46757c is described below

commit dc73cb46757cee7a5e0da2357e78332d9484520d
Author: Guillaume Nodet <[email protected]>
AuthorDate: Wed May 20 08:17:19 2026 +0200

    CAMEL-23236: Add doc --example flag and camel-kit shell banner hint (#23347)
    
    - Add --example flag to camel doc that generates a minimal working
      YAML route snippet for any component (consumer or producer)
    - Add camel-kit discoverability hint in shell banner when no routes
      are found in the current directory
    - Add unit tests for the new --example flag
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../ROOT/pages/jbang-commands/camel-jbang-doc.adoc |  1 +
 .../META-INF/camel-jbang-commands-metadata.json    |  2 +-
 .../camel/dsl/jbang/core/commands/Shell.java       |  1 +
 .../jbang/core/commands/catalog/CatalogDoc.java    | 87 +++++++++++++++++++++-
 .../core/commands/catalog/CatalogDocTest.java      | 82 ++++++++++++++++++++
 5 files changed, 170 insertions(+), 3 deletions(-)

diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-doc.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-doc.adoc
index d2049ffdf83d..9171fdbb2c4e 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-doc.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-doc.adoc
@@ -21,6 +21,7 @@ camel doc [options]
 | Option | Description | Default | Type
 | `--camel-version` | To use a different Camel version than the default 
version |  | String
 | `--download` | Whether to allow automatic downloading JAR dependencies (over 
the internet) | true | boolean
+| `--example` | Prints a minimal working YAML route snippet for the component 
| false | boolean
 | `--filter` | Filter option listed in tables by name, description, or group | 
 | String
 | `--header` | Whether to display component message headers | false | boolean
 | `--kamelets-version` | Apache Camel Kamelets version | 
RuntimeType.KAMELETS_VERSION | String
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 24545f91b2f4..055c7288eedb 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
@@ -8,7 +8,7 @@
     { "name": "debug", "fullName": "debug", "description": "Debug local Camel 
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug", 
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd 
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background", "description": "Run in the background", "defaultValue": 
"false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To  [...]
     { "name": "dependency", "fullName": "dependency", "description": "Displays 
all Camel dependencies required to run", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.DependencyCommand", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"copy", "fullName": "dependency copy", "description": "Copies all Camel 
dependencies required to run to a specific directory", "sourc [...]
     { "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": "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": "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 
OPENAI_API_KEY or LLM_API_KEY env vars", "javaType": "java.lang.String", 
"type": "string" }, { "names": "--api-type", "description": "API type: 'ollama' 
or 'openai' (OpenAI-compatible)", "defaultValue": "ollama", "javaTyp [...]
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java
index 01e919e74a98..da0427070077 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java
@@ -159,6 +159,7 @@ public class Shell extends CamelCommand {
             writer.println("  Quick start:  init MyRoute.yaml && run *");
             writer.println("  Templates:    init --list");
             writer.println("  Docs:         doc <component>");
+            writer.println("  AI scaffold:  plugin add kit");
             writer.println("  Need help?    help");
         } else {
             writer.printf("Found %d route file(s) in current directory.%n", 
routeCount);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDoc.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDoc.java
index eb487dbd275f..70cbb7ea651b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDoc.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDoc.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.catalog;
 
 import java.awt.*;
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -58,7 +59,8 @@ import static 
org.apache.camel.dsl.jbang.core.commands.catalog.CatalogBaseComman
                              "  camel doc kafka",
                              "  camel doc timer",
                              "  camel doc aws-s3-source",
-                             "  camel doc jsonpath" })
+                             "  camel doc jsonpath",
+                             "  camel doc kafka --example" })
 public class CatalogDoc extends CamelCommand {
 
     @CommandLine.Parameters(description = "Name of kamelet, component, 
dataformat, or other Camel resource",
@@ -109,6 +111,10 @@ public class CatalogDoc extends CamelCommand {
                         description = "Whether to display component message 
headers", defaultValue = "false")
     boolean headers;
 
+    @CommandLine.Option(names = { "--example" },
+                        description = "Prints a minimal working YAML route 
snippet for the component", defaultValue = "false")
+    boolean example;
+
     @CommandLine.Option(names = {
             "--kamelets-version" }, description = "Apache Camel Kamelets 
version",
                         defaultValue = RuntimeType.KAMELETS_VERSION)
@@ -161,7 +167,11 @@ public class CatalogDoc extends CamelCommand {
         if (prefix == null || "component".equals(prefix)) {
             ComponentModel cm = catalog.componentModel(name);
             if (cm != null) {
-                docComponent(cm);
+                if (example) {
+                    printExample(cm);
+                } else {
+                    docComponent(cm);
+                }
                 return 0;
             }
         }
@@ -226,6 +236,79 @@ public class CatalogDoc extends CamelCommand {
         return 1;
     }
 
+    private void printExample(ComponentModel cm) {
+        String scheme = cm.getScheme();
+        String uri = buildExampleUri(cm);
+
+        printer().println("# Example: " + scheme + " component");
+
+        if (cm.isProducerOnly()) {
+            printer().println("- route:");
+            printer().println("    from:");
+            printer().println("      uri: \"timer:tick\"");
+            printer().println("      parameters:");
+            printer().println("        period: 1000");
+            printer().println("    steps:");
+            printer().println("      - to:");
+            printer().println("          uri: \"" + uri + "\"");
+        } else {
+            printer().println("- route:");
+            printer().println("    from:");
+            printer().println("      uri: \"" + uri + "\"");
+            printer().println("    steps:");
+            printer().println("      - to:");
+            printer().println("          uri: \"log:" + scheme + "\"");
+        }
+
+        printer().println();
+        printer().println("# Save to a file and run with:");
+        printer().println("#   camel run my-route.yaml");
+    }
+
+    private String buildExampleUri(ComponentModel cm) {
+        String scheme = cm.getScheme();
+        List<ComponentModel.EndpointOptionModel> pathOptions = 
cm.getEndpointPathOptions();
+        if (pathOptions.isEmpty()) {
+            return scheme;
+        }
+        List<String> pathValues = new ArrayList<>();
+        for (ComponentModel.EndpointOptionModel opt : pathOptions) {
+            Object dv = opt.getDefaultValue();
+            if (dv != null && !dv.toString().isEmpty()) {
+                pathValues.add(dv.toString());
+            } else if (opt.getEnums() != null && !opt.getEnums().isEmpty()) {
+                pathValues.add(opt.getEnums().get(0));
+            } else {
+                pathValues.add("my" + capitalize(opt.getName()));
+            }
+        }
+        String syntax = cm.getSyntax();
+        if (syntax != null) {
+            String result = syntax;
+            for (ComponentModel.EndpointOptionModel opt : pathOptions) {
+                Object dv = opt.getDefaultValue();
+                String value;
+                if (dv != null && !dv.toString().isEmpty()) {
+                    value = dv.toString();
+                } else if (opt.getEnums() != null && 
!opt.getEnums().isEmpty()) {
+                    value = opt.getEnums().get(0);
+                } else {
+                    value = "my" + capitalize(opt.getName());
+                }
+                result = result.replace(opt.getName(), value);
+            }
+            return result;
+        }
+        return scheme + ":" + String.join("/", pathValues);
+    }
+
+    private static String capitalize(String s) {
+        if (s == null || s.isEmpty()) {
+            return s;
+        }
+        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+    }
+
     private void docKamelet(KameletModel km) throws Exception {
         String link = websiteLink("kamelet", name, kameletsVersion);
         if (openUrl) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDocTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDocTest.java
new file mode 100644
index 000000000000..04adf28cb320
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogDocTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.catalog;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelCommandBaseTestSupport;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class CatalogDocTest extends CamelCommandBaseTestSupport {
+
+    @Test
+    public void shouldPrintExampleForConsumerComponent() throws Exception {
+        CatalogDoc command = new CatalogDoc(new 
CamelJBangMain().withPrinter(printer));
+        command.name = "timer";
+        command.example = true;
+
+        int exit = command.doCall();
+
+        Assertions.assertEquals(0, exit);
+        String output = printer.getOutput();
+        Assertions.assertTrue(output.contains("from:"), "Should have a from 
clause");
+        Assertions.assertTrue(output.contains("timer"), "Should reference the 
timer component");
+        Assertions.assertTrue(output.contains("log:"), "Consumer components 
should route to log");
+    }
+
+    @Test
+    public void shouldPrintExampleForProducerOnlyComponent() throws Exception {
+        CatalogDoc command = new CatalogDoc(new 
CamelJBangMain().withPrinter(printer));
+        command.name = "log";
+        command.example = true;
+
+        int exit = command.doCall();
+
+        Assertions.assertEquals(0, exit);
+        String output = printer.getOutput();
+        Assertions.assertTrue(output.contains("timer:tick"), "Producer-only 
should use timer as source");
+        Assertions.assertTrue(output.contains("log"), "Should reference the 
log component");
+    }
+
+    @Test
+    public void shouldPrintExampleForKafka() throws Exception {
+        CatalogDoc command = new CatalogDoc(new 
CamelJBangMain().withPrinter(printer));
+        command.name = "kafka";
+        command.example = true;
+
+        int exit = command.doCall();
+
+        Assertions.assertEquals(0, exit);
+        String output = printer.getOutput();
+        Assertions.assertTrue(output.contains("kafka:"), "Should contain kafka 
URI");
+        Assertions.assertTrue(output.contains("route:"), "Should have route 
structure");
+        Assertions.assertTrue(output.contains("camel run"), "Should show run 
instructions");
+    }
+
+    @Test
+    public void shouldPrintStandardDocWithoutExampleFlag() throws Exception {
+        CatalogDoc command = new CatalogDoc(new 
CamelJBangMain().withPrinter(printer));
+        command.name = "timer";
+
+        int exit = command.doCall();
+
+        Assertions.assertEquals(0, exit);
+        String output = printer.getOutput();
+        Assertions.assertTrue(output.contains("Component Name: timer"), 
"Should show standard doc");
+        Assertions.assertFalse(output.contains("camel run my-route.yaml"), 
"Should not show example run hint");
+    }
+}

Reply via email to