gnodet-bot commented on code in PR #27686:
URL: https://github.com/apache/camel/pull/27686#discussion_r4248603736


##########
catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-component.adoc:
##########
@@ -163,6 +163,46 @@ The usage of this Kamelet into a Camel route is going to 
be the same as any othe
 
 WARNING: beware of any potential circular reference you may introduce when 
using chain of Kamelets, in which case, the runtime will likely be idle 
consuming a high amount of resources.
 
+=== Advanced component endpoint options
+
+Kamelets expose a defined set of parameters, but the underlying Camel 
component may support additional endpoint options. You can pass these options 
when using a Kamelet by prefixing them with `source.` or `sink.`, depending on 
the type of Kamelet.
+
+==== Source Kamelets
+
+For source Kamelets, use the `source.` prefix to pass additional options to 
the underlying source component endpoint. The prefix is removed when the 
options are applied to the endpoint URI.
+
+For example:
+
+[source,java]
+----
+from("direct:start")

Review Comment:
   💡 **Catalog adoc diverges from source adoc**: the source example here uses 
`from("direct:start").to("kamelet:my-source/source?source.block=true")` (with 
`.to()` and `/source` path segment), but the authoritative 
`components/camel-kamelet/src/main/docs/kamelet-component.adoc` uses 
`from("kamelet:my-source?source.block=true").to("log:info")` (direct `from()`).
   
   This file (`catalog/camel-catalog/src/...`) is auto-generated — it should be 
regenerated from the source rather than manually edited. Running `mvn 
process-resources -pl catalog/camel-catalog` (or the appropriate generation 
goal) should sync it. Manual edits here will be overwritten on the next build.



##########
catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-component.adoc:
##########
@@ -163,6 +163,46 @@ The usage of this Kamelet into a Camel route is going to 
be the same as any othe
 
 WARNING: beware of any potential circular reference you may introduce when 
using chain of Kamelets, in which case, the runtime will likely be idle 
consuming a high amount of resources.
 
+=== Advanced component endpoint options
+
+Kamelets expose a defined set of parameters, but the underlying Camel 
component may support additional endpoint options. You can pass these options 
when using a Kamelet by prefixing them with `source.` or `sink.`, depending on 
the type of Kamelet.
+
+==== Source Kamelets
+
+For source Kamelets, use the `source.` prefix to pass additional options to 
the underlying source component endpoint. The prefix is removed when the 
options are applied to the endpoint URI.
+
+For example:
+
+[source,java]
+----
+from("direct:start")
+    .to("kamelet:my-source/source?source.block=true");
+----
+
+The `/source` path segment is the route ID within the Kamelet template. 
Replace `source` with the actual route ID defined in your Kamelet template.
+
+In this example, `source.block=true` passes `block=true` to the source 
component endpoint. If the endpoint URI already contains the same option, the 
advanced option overrides its existing value.
+
+==== Sink and action Kamelets
+
+For sink and action Kamelets, use the `sink.` prefix to pass additional 
options to the component endpoints in the Kamelet output route. The prefix is 
removed when the options are applied to the endpoint URIs.
+
+For example:
+
+[source,java]
+----
+from("direct:start")
+    .to("kamelet:my-sink/sink?sink.retain-first=1");
+----
+

Review Comment:
   💡 **Inconsistent sink behavior description**: this file says options are 
applied to *"all statically-defined non-Kamelet `to()` output endpoints"*, but 
the source adoc (`components/camel-kamelet/.../kamelet-component.adoc`) says 
*"applied only when the Kamelet has exactly one eligible top-level, 
statically-defined `to()` destination"* (with strict single-destination 
enforcement and startup failure on ambiguity).
   
   These are functionally different descriptions of the same feature. Again, 
regenerating this file from the source would resolve the discrepancy.



##########
components/camel-kamelet/src/test/java/org/apache/camel/component/kamelet/KameletAdvancedOptionsTest.java:
##########
@@ -0,0 +1,768 @@
+

Review Comment:
   🔧 **Blank line before license header**: the file starts with a blank line 
(line 1) before the `/*` license comment (line 2). The Apache Camel convention 
is to start the license header on line 1 with no preceding blank line.
   
   ```suggestion
   /*
   ```



##########
components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java:
##########
@@ -184,6 +203,166 @@ public static void 
extractKameletEnvironmentVariables(Map<String, Object> proper
         }
     }
 
+    /**
+     * Extracts advanced options without discarding camelCase and kebab-case 
aliases before normalization.
+     */
+    private static Map<String, Object> extractAdvancedOptions(Map<String, 
Object> parameters, String prefix) {
+        Map<String, Object> options = new LinkedHashMap<>();
+        if (parameters == null) {
+            return options;
+        }
+
+        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
+            String key = entry.getKey();
+            if (key != null && key.startsWith(prefix)) {
+                options.put(key.substring(prefix.length()), entry.getValue());
+            }
+        }
+
+        return options;
+    }
+
+    /**
+     * Normalizes option names so that camelCase and kebab-case aliases do not 
cause the same option to be appended more
+     * than once. When both forms are present, the kebab-case form takes 
precedence.
+     */
+    private static Map<String, Object> normalizeParameterNames(Map<String, 
Object> parameters) {
+        Map<String, Object> normalized = new LinkedHashMap<>();
+        Map<String, Boolean> canonicalKeys = new HashMap<>();
+
+        if (parameters == null) {
+            return normalized;
+        }
+
+        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
+            String originalName = entry.getKey();
+            String name = StringHelper.camelCaseToDash(originalName);
+            boolean isCanonical = originalName.equals(name);
+
+            if (!normalized.containsKey(name) || (isCanonical && 
!canonicalKeys.getOrDefault(name, false))) {
+                normalized.put(name, entry.getValue());
+                canonicalKeys.put(name, isCanonical);
+            }
+        }
+
+        return normalized;
+    }
+
+    /**
+     * Appends query parameters without parsing or re-encoding the endpoint 
URI. This preserves property placeholders
+     * and RAW expressions that may appear in the URI.
+     */
+    private static String appendParametersToURI(String uri, Map<String, 
Object> parameters) {
+        if (parameters == null || parameters.isEmpty()) {
+            return uri;
+        }
+
+        String result = uri;
+        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
+            String name = entry.getKey();
+            String value = String.valueOf(entry.getValue());
+
+            result = removeQueryParameter(result, name);
+            result += result.contains("?") ? "&" : "?";
+            result += name + "=" + value;
+        }
+
+        return result;
+    }
+
+    /**
+     * Removes a query parameter by name, treating camelCase and kebab-case 
names as aliases.
+     */
+    private static String removeQueryParameter(String uri, String 
parameterName) {
+        int queryIndex = uri.indexOf('?');
+        if (queryIndex < 0) {
+            return uri;
+        }
+
+        String base = uri.substring(0, queryIndex);
+        String query = uri.substring(queryIndex + 1);
+        StringBuilder remaining = new StringBuilder();
+

Review Comment:
   ⚠️ **`RAW()` expression corruption**: splitting on `&` will break URIs that 
contain `RAW()` expressions with embedded `&`, such as 
`kamelet:my-source?param=RAW(value&with&ampersands)`. Camel uses `RAW(...)` to 
protect special characters in URI parameter values.
   
   Consider using `URISupport.splitQuery(query)` (which is already RAW-aware) 
instead of hand-rolling the split, or at minimum skip splitting inside 
`RAW(...)` blocks:
   
   ```java
   // Option 1: delegate to Camel's existing RAW-aware query parsing
   Map<String, Object> params = URISupport.parseQuery(query);
   params.remove(parameterName);
   // also remove camelCase/kebab variant...
   return params.isEmpty() ? base : base + "?" + 
URISupport.createQueryString(params);
   ```
   
   This avoids re-implementing logic that Camel core already handles correctly.



-- 
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