ramu11 commented on code in PR #27686:
URL: https://github.com/apache/camel/pull/27686#discussion_r4254561139


##########
components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java:
##########
@@ -184,6 +207,196 @@ 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;
+        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 + "=" + wrapRawIfNeeded(value);
+        }
+
+        return result;
+    }
+
+    /**
+     * 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.
+     */
+    private static String wrapRawIfNeeded(String value) {
+        if (isRawToken(value)) {
+            return value;
+        }
+        if (value.contains("&") || value.contains("=") || value.contains("?")) 
{

Review Comment:
   Thanks for the reviews. The RAW handling here is intentional, and I'd like 
to clarify the problem it addresses so we can converge on the expected behavior 
rather than iterating on individual URI characters.
   
   When advanced `sink.*` options are extracted from the Kamelet parameters, 
the `RAW(...)` wrapper may already have been removed by Camel's endpoint 
parameter parsing. The resulting value is then appended to the destination 
endpoint URI.
   
   If that value contains URI-sensitive characters such as `&`, `=`, `?`, `+`, 
or `%`, appending it directly can change its meaning when Camel parses the 
destination URI. For example, `sink.password=RAW(p@ss&word)` can become 
`password=p@ss&word`, where `&word` is interpreted as another query parameter 
and the password is truncated to `p@ss`. Similarly, `+` can be decoded as a 
space, and `%` can trigger percent-decoding behavior.
   
   The purpose of `wrapRawIfNeeded()` is therefore to restore RAW semantics for 
values that would otherwise be altered during URI parsing, while leaving 
existing RAW tokens and ordinary values unchanged.
   
   The regression test `sinkOptionsPreserveRawValuesWithUriSensitiveCharacters` 
covers this behavior, and the build now passes.
   
   I'd prefer to keep the fix scoped to preserving the original parameter value 
through URI reconstruction. If there is a Camel-core API that provides this 
guarantee without manually restoring RAW wrappers, I'm happy to consider it. 
Otherwise, could we agree on the expected behavior and test cases for this 
helper so the remaining changes can be evaluated consistently?



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