davsclaus commented on code in PR #27686:
URL: https://github.com/apache/camel/pull/27686#discussion_r4251268926
##########
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:
`+` and `%` are also decoded when the endpoint parses its query
(`URIScanner.addParameter` calls `URLDecoder.decode` on values that are not
RAW). The value has already lost its `RAW(...)` in
`KameletComponent.createEndpoint`, so `sink.password=RAW(se+cret)` arrives as
`se cret`. Please also wrap those two:
```suggestion
if (value.contains("&") || value.contains("=") || value.contains("?")
|| value.contains("+") || value.contains("%")) {
```
A test like `sinkOptionsPreserveRawValuesWithUriSensitiveCharacters` with a
`+` in the value would cover it.
##########
components/camel-kamelet/src/main/docs/kamelet-component.adoc:
##########
@@ -163,6 +163,52 @@ 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.
+
+The prefix is removed when the options are applied to the underlying endpoint
URI. The options must be supported by the component used by that endpoint.
+
+==== Source Kamelets
+
+For source Kamelets, use the `source.` prefix to pass additional options to
the underlying source component endpoint.
+
+For example:
+
+[source,java]
+----
+from("kamelet:my-source?source.bridgeErrorHandler=true")
+ .to("log:info");
+----
+
+In this example, `source.bridgeErrorHandler=true` passes
`bridgeErrorHandler=true` to the source component endpoint. If the endpoint URI
already contains the same option, the advanced option overrides its existing
value.
+
+Property placeholders and RAW expressions in endpoint URIs are preserved when
advanced options are applied. For example, `kafka:{{topic}}` or
`aws2-s3:{{bucket}}?secretKey=RAW({{secret}})` remain intact.
Review Comment:
This line breaks the "Validate documentation" CI job (`skipping reference to
missing attribute: topic / bucket / secret`). Asciidoctor reads `{topic}`,
`{bucket}` and `{secret}` inside the `{{...}}` as attribute references. A
literal passthrough fixes it:
```suggestion
Property placeholders and RAW expressions in endpoint URIs are preserved
when advanced options are applied. For example, `+kafka:{{topic}}+` or
`+aws2-s3:{{bucket}}?secretKey=RAW({{secret}})+` remain intact.
```
Please regenerate the catalog copy
(`catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/kamelet-component.adoc`)
afterwards.
##########
components/camel-kamelet/src/main/java/org/apache/camel/component/kamelet/Kamelet.java:
##########
@@ -55,18 +63,33 @@ public final class Kamelet {
public static final String PARENT_PROCESSOR_ID = "parentProcessorId";
public static final String NO_ERROR_HANDLER = "noErrorHandler";
public static final String BRIDGE_ERROR_HANDLER = "bridgeErrorHandler";
+ static final String ADVANCED_SOURCE_OPTIONS = "internal.advanced.source";
+ static final String ADVANCED_SINK_OPTIONS = "internal.advanced.sink";
// use a running counter as uuid
private static final UuidGenerator UUID = new SimpleUuidGenerator();
private Kamelet() {
}
+ private static boolean isKameletEndpoint(String uri, String endpoint) {
+ if (uri == null) {
+ return false;
+ }
+ return uri.equals("kamelet:" + endpoint)
+ || uri.startsWith("kamelet:" + endpoint + "?")
+ || uri.startsWith("kamelet:" + endpoint + "/")
+ || uri.equals("kamelet://" + endpoint)
+ || uri.startsWith("kamelet://" + endpoint + "?")
+ || uri.startsWith("kamelet://" + endpoint + "/");
+ }
+
public static Predicate<String> startsWith(String prefix) {
return item -> item.startsWith(prefix);
}
- public static String extractTemplateId(CamelContext context, String
remaining, Map<String, Object> parameters) {
+ public static String extractTemplateId(
+ CamelContext context, String remaining, Map<String, Object>
parameters) {
Review Comment:
These changes are still unrelated to the feature: this re-wrapped signature,
the same for `extractRouteId` and `extractKameletProperties`, the removed blank
lines in the two env-var loops, the changed env-var javadoc example, and the
reworded error-handler and "at least one sink" comments in `templateToRoute`.
Please revert them so the diff only contains the feature. (The
`isKameletEndpoint` changes in `templateToRoute` are fine to keep.)
--
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]