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


##########
components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java:
##########
@@ -252,15 +545,17 @@ public static RouteDefinition 
templateToRoute(RouteTemplateDefinition in, Map<St
         int line = -1;
         boolean sink = false;
         Collection<ToDefinition> col = filterTypeInOutputs(def.getOutputs(), 
ToDefinition.class);
+
         for (ToDefinition to : col) {
-            if (to.getEndpointUri().startsWith("kamelet:sink") || 
to.getEndpointUri().startsWith("kamelet://sink")) {
+            if (isKameletEndpoint(to.getEndpointUri(), SINK_ID)) {
                 to.setUri("kamelet://sink?" + PARAM_ROUTE_ID + "=" + rid);
                 sink = true;
             }
             line = to.getLineNumber();
         }
+
         if (!sink) {
-            // this is appended and is used to go back to the kamelet that 
called me
+            // This is appended and is used to go back to the Kamelet that 
called me.

Review Comment:
   🔄 **Unrelated cosmetic change** (re-raise from PRRT_kwDOAAMl7c6rwu-- and 
PRRT_kwDOAAMl7c6ruRBz): the comment capitalisation (`// This is appended and is 
used to go back to the Kamelet that called me.` vs original lowercase) and the 
two blank lines added around the `for` loop and `if (!sink)` block are 
unrelated to the feature. davsclaus has explicitly asked to revert these.
   
   ```suggestion
               // this is appended and is used to go back to the kamelet that 
called me
   ```



##########
components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java:
##########
@@ -184,6 +206,214 @@ public static void 
extractKameletEnvironmentVariables(Map<String, Object> proper
         }
     }
 
+    /**
+     * Reads the original advanced options map stored under a single internal 
key.
+     */
+    private static Map<String, Object> extractAdvancedOptions(Map<String, 
Object> parameters, String key) {
+        Map<String, Object> options = new LinkedHashMap<>();
+        if (parameters == null) {
+            return options;
+        }
+
+        Object value = parameters.get(key);
+        if (value instanceof Map<?, ?> map) {
+            for (Map.Entry<?, ?> entry : map.entrySet()) {
+                if (entry.getKey() != null) {
+                    options.put(entry.getKey().toString(), entry.getValue());
+                }
+            }
+        }
+
+        return options;
+    }
+
+    /**
+     * Preserves the user-supplied option name. When both camelCase and 
kebab-case forms are present, the kebab-case
+     * form is kept so the same option is not appended twice.
+     */
+    private static Map<String, Object> normalizeParameterNames(Map<String, 
Object> parameters) {
+        Map<String, Object> normalized = new LinkedHashMap<>();
+        Map<String, String> seenOriginal = new HashMap<>();
+        Map<String, Boolean> seenIsCanonical = 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);
+
+            String existingOriginal = seenOriginal.get(name);
+            if (existingOriginal == null) {
+                seenOriginal.put(name, originalName);
+                seenIsCanonical.put(name, isCanonical);
+                normalized.put(originalName, entry.getValue());
+            } else {
+                boolean existingIsCanonical = seenIsCanonical.get(name);
+                if (isCanonical && !existingIsCanonical) {
+                    normalized.remove(existingOriginal);
+                    seenOriginal.put(name, originalName);
+                    seenIsCanonical.put(name, isCanonical);
+                    normalized.put(originalName, entry.getValue());
+                }
+            }
+        }
+
+        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;
+
+        // First remove all parameters that will be overridden.
+        for (String name : parameters.keySet()) {
+            result = removeQueryParameter(result, name);
+        }
+
+        // Then append the options to the preserved URI.
+        StringBuilder sb = new StringBuilder(result);
+        char sep = result.indexOf('?') < 0 ? '?' : '&';
+
+        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
+            sb.append(sep).append(entry.getKey());
+            if (entry.getValue() != null) {
+                
sb.append('=').append(wrapRawIfNeeded(String.valueOf(entry.getValue())));
+            }
+            sep = '&';
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Re-wraps values that contain URI-sensitive characters. RAW wrappers may 
already have been stripped by
+     * {@link URISupport#resolveRawParameterValues(Map)} before options reach 
this method.
+     * <p>
+     * RAW(...) and RAW{...} have limitations when their closing delimiter is 
followed by an ampersand. Avoid RAW{...}
+     * for values starting with '{' or ending with '}', as they may be 
interpreted as property placeholders.
+     */
+    private static String wrapRawIfNeeded(String value) {
+        if (value == null || value.isEmpty() || isRawToken(value)) {
+            return value;
+        }
+        if (value.contains("&") || value.contains("=") || value.contains("?")
+                || value.contains("+") || value.contains("%")) {
+            if (value.contains(")&") && !value.startsWith("{") && 
!value.endsWith("}")) {

Review Comment:
   💡 **Low: residual gap when value contains both `"}&"` and `")&"`** — e.g. 
`x)&y}&z` triggers the `RAW{…}` branch (because `")&"` is present and the value 
neither starts with `{` nor ends with `}`), but `RAW{x)&y}&z}` is then parsed 
as `x)&y` since `URIScanner` closes `RAW{…}` at the first `}&`. Adding a 
`!value.contains("}&")` guard would correctly fall back to `RAW(…)` for this 
case (which itself fails at `")&"` — making this inherently an `URIScanner` 
limitation). Worth guarding defensively:
   
   ```suggestion
               if (value.contains(")&") && !value.startsWith("{") && 
!value.endsWith("}")
                       && !value.contains("}&")) {
                   return "RAW{" + value + "}";
               }
   ```



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