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 0e28102b5fea CAMEL-26294: camel-jbang - the validator takes the 
options a Kamelet forwards with advancedOptions (#28441)
0e28102b5fea is described below

commit 0e28102b5fea317b269ddab8800cfd79385283d8
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Oct 25 12:11:42 2026 +0100

    CAMEL-26294: camel-jbang - the validator takes the options a Kamelet 
forwards with advancedOptions (#28441)
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../catalog/docs/kubernetes-secrets-component.adoc |  8 ++---
 .../main/docs/kubernetes-secrets-component.adoc    |  8 ++---
 .../ROOT/pages/property-placeholder-functions.adoc | 16 +++++-----
 .../dsl/jbang/core/commands/ai/KameletChecks.java  | 32 ++++++++++++++++++-
 .../jbang/core/commands/ai/KameletChecksTest.java  | 37 ++++++++++++++++++++++
 5 files changed, 84 insertions(+), 17 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kubernetes-secrets-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kubernetes-secrets-component.adoc
index 28a4580d5e4e..56bcfde49274 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kubernetes-secrets-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kubernetes-secrets-component.adoc
@@ -229,10 +229,10 @@ in the route above:
 
 [source,properties]
 ----
-camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
-camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
-camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
-camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
+camel.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
+camel.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
+camel.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
+camel.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
 ----
 
 Which reduces the route to:
diff --git 
a/components/camel-kubernetes/src/main/docs/kubernetes-secrets-component.adoc 
b/components/camel-kubernetes/src/main/docs/kubernetes-secrets-component.adoc
index 28a4580d5e4e..56bcfde49274 100644
--- 
a/components/camel-kubernetes/src/main/docs/kubernetes-secrets-component.adoc
+++ 
b/components/camel-kubernetes/src/main/docs/kubernetes-secrets-component.adoc
@@ -229,10 +229,10 @@ in the route above:
 
 [source,properties]
 ----
-camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
-camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
-camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
-camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
+camel.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
+camel.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
+camel.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
+camel.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
 ----
 
 Which reduces the route to:
diff --git 
a/docs/user-manual/modules/ROOT/pages/property-placeholder-functions.adoc 
b/docs/user-manual/modules/ROOT/pages/property-placeholder-functions.adoc
index 968403289ac3..d34abd37c256 100644
--- a/docs/user-manual/modules/ROOT/pages/property-placeholder-functions.adoc
+++ b/docs/user-manual/modules/ROOT/pages/property-placeholder-functions.adoc
@@ -575,10 +575,10 @@ in the route above:
 
 [source,properties]
 ----
-camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
-camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
-camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
-camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
+camel.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
+camel.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
+camel.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
+camel.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
 ----
 
 Which reduces the route to:
@@ -639,10 +639,10 @@ For example the example above with the Postgres kamelet, 
that was configured usi
 
 [source,properties]
 ----
-camel.component.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
-camel.component.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
-camel.component.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
-camel.component.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
+camel.kamelet.postgresql-sink.databaseName={{secret:mydb/myhost}}
+camel.kamelet.postgresql-sink.serverPort={{secret:mydb/myport}}
+camel.kamelet.postgresql-sink.username={{secret:mydb/myuser}}
+camel.kamelet.postgresql-sink.password={{secret:mydb/mypass}}
 ----
 
 Now suppose we have a local Postgres database we want to use, then we can turn 
on _local mode_
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/KameletChecks.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/KameletChecks.java
index 7ad2b6622839..9022caf5810f 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/KameletChecks.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/KameletChecks.java
@@ -26,6 +26,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
@@ -174,11 +175,13 @@ public final class KameletChecks {
             return;
         }
         Map<String, Integer> given = new LinkedHashMap<>();
+        Map<String, String> values = new HashMap<>();
         if (query != null) {
             for (String part : query.split("&")) {
                 String key = part.contains("=") ? part.substring(0, 
part.indexOf('=')) : part;
                 if (!key.isBlank()) {
                     given.put(key, line);
+                    values.put(key, part.contains("=") ? 
part.substring(part.indexOf('=') + 1) : "");
                 }
             }
         }
@@ -186,13 +189,18 @@ public final class KameletChecks {
             for (NodeTuple t : parameters.getValue()) {
                 if (t.getKeyNode() instanceof ScalarNode k) {
                     given.put(k.getValue(), k.getStartMark().getLine());
+                    if (t.getValueNode() instanceof ScalarNode v) {
+                        values.put(k.getValue(), v.getValue());
+                    }
                 }
             }
         }
+        // with advancedOptions, the options that are not properties of the 
Kamelet go to the endpoint it wraps
+        boolean forwarded = !"false".equals(advancedOptions(values, 
properties));
         String where = def.source().startsWith("the project") ? " (" + 
def.source() + ")" : "";
         Set<String> componentOptions = kameletComponentOptions();
         for (Map.Entry<String, Integer> e : given.entrySet()) {
-            if (def.property(e.getKey()) == null && 
!componentOptions.contains(e.getKey())) {
+            if (def.property(e.getKey()) == null && 
!componentOptions.contains(e.getKey()) && !forwarded) {
                 List<String> close = 
KameletDefinitions.suggestProperty(e.getKey(), def);
                 StringBuilder sb = new 
StringBuilder(EndpointChecks.linePrefix(e.getValue())).append("kamelet:")
                         .append(name).append(": unknown property 
'").append(e.getKey()).append("'");
@@ -227,6 +235,28 @@ public final class KameletChecks {
         }
     }
 
+    /**
+     * The advancedOptions of a kamelet: endpoint: of the endpoint when it 
sets it, otherwise of the kamelet component
+     * in the properties of the project 
(camel.component.kamelet.advanced-options). "false" when neither sets it, and
+     * null when it is not known before the route runs, as with a placeholder.
+     */
+    static String advancedOptions(Map<String, String> endpoint, Map<String, 
String> properties) {
+        String value = endpoint.get("advancedOptions");
+        if (value == null) {
+            for (Map.Entry<String, String> e : properties.entrySet()) {
+                String key = e.getKey().replace("-", 
"").toLowerCase(Locale.ROOT);
+                if ("camel.component.kamelet.advancedoptions".equals(key)) {
+                    value = e.getValue();
+                }
+            }
+        }
+        if (value == null) {
+            return "false";
+        }
+        value = value.trim();
+        return value.contains("{{") || value.contains("${") ? null : 
Boolean.toString(Boolean.parseBoolean(value));
+    }
+
     private static volatile Set<String> componentOptions;
 
     /**
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/KameletChecksTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/KameletChecksTest.java
index 821a8627909b..4359623bee28 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/KameletChecksTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/KameletChecksTest.java
@@ -192,6 +192,43 @@ class KameletChecksTest {
         assertThat(errors.get(1)).startsWith("Line 3: ").contains("the 
required property message is missing");
     }
 
+    @Test
+    void theOptionsAdvancedOptionsForwards(@TempDir Path dir) throws Exception 
{
+        // CAMEL-26294: with advancedOptions, an option that is not a property 
of the Kamelet goes to the endpoint it
+        // wraps, such as maxConnections of aws2-s3
+        String yaml = """
+                - route:
+                    from:
+                      uri: kamelet:timer-source
+                      parameters:
+                        advancedOptions: true
+                        message: hello
+                        includeMetadata: true
+                      steps:
+                        - to: 
kamelet:log-sink?advancedOptions=true&showAll=true
+                """;
+        assertThat(KameletChecks.validateYaml(yaml, null)).isEmpty();
+
+        // on the component, for every kamelet: endpoint; and the endpoint 
setting wins
+        String component = """
+                - route:
+                    from:
+                      uri: kamelet:timer-source
+                      parameters:
+                        message: hello
+                        includeMetadata: true
+                      steps:
+                        - to: 
kamelet:log-sink?advancedOptions=false&showAll=true
+                """;
+        Files.writeString(dir.resolve("application.properties"), 
"camel.component.kamelet.advanced-options = true\n");
+        List<String> errors = KameletChecks.validateYaml(component, dir);
+        assertThat(errors).singleElement()
+                .satisfies(e -> assertThat(e).contains("kamelet:log-sink: 
unknown property 'showAll'"));
+
+        // without it, the option is not one the Kamelet has
+        assertThat(KameletChecks.validateYaml(component, null)).hasSize(2);
+    }
+
     @Test
     void aRequiredPropertyFromApplicationProperties(@TempDir Path dir) throws 
Exception {
         Files.writeString(dir.resolve("application.properties"), 
"camel.kamelet.timer-source.message=hello\n");

Reply via email to