davsclaus commented on code in PR #26758:
URL: https://github.com/apache/camel/pull/26758#discussion_r4080274521


##########
dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java:
##########
@@ -668,6 +669,87 @@ void checkSimpleSyntaxInScripts(JsonNode node, NodePath 
path, List<Error> errors
         }
     }
 
+    /**
+     * to: http://host/stock/${header.sku}: the endpoint of a to: is resolved 
once when the route starts, so an
+     * expression in its path is never evaluated - it is sent as the text it 
is, url-encoded. That is what toD: is for
+     * (CAMEL-24917).
+     * <p/>
+     * Only the path is checked, never the options after the {@code ?}: an 
option such as the file component's
+     * {@code fileName=${date:now:yyyyMMdd}} is evaluated by the producer and 
is correct on a plain to:.
+     */
+    void checkDynamicUri(JsonNode node, NodePath path, List<Error> errors) {
+        if (node == null) {
+            return;
+        }
+        if (node.isArray()) {
+            for (int i = 0; i < node.size(); i++) {
+                checkDynamicUri(node.get(i), path.append(i), errors);
+            }
+            return;
+        }
+        if (!node.isObject()) {
+            return;
+        }
+        var fields = node.fieldNames();
+        while (fields.hasNext()) {
+            String name = fields.next();
+            JsonNode value = node.get(name);
+            if ("to".equals(name)) {
+                String uri = null;
+                NodePath at = path.append(name);
+                if (value.isTextual()) {
+                    uri = value.asText();
+                } else if (value.isObject() && value.has("uri") && 
value.get("uri").isTextual()) {
+                    uri = value.get("uri").asText();
+                    at = at.append("uri");
+                }
+                String expression = expressionInPath(uri);
+                if (expression != null) {
+                    errors.add(Error.builder()
+                            .keyword("type")
+                            .instanceLocation(at)
+                            .messageKey("type")
+                            .format(new MessageFormat("{0}"))
+                            .arguments("to: the uri holds an expression (" + 
expression + ") but the endpoint of a to:"
+                                       + " is fixed when the route starts, so 
it is sent as text: write toD: to build"
+                                       + " the uri for each message")
+                            .build());
+                }
+            }
+            checkDynamicUri(value, path.append(name), errors);
+        }
+    }
+
+    /**
+     * The first simple expression in the path of the uri (what comes before 
the options), or null when there is none.
+     */
+    private static String expressionInPath(String uri) {
+        if (uri == null) {
+            return null;
+        }
+        int scheme = uri.indexOf(':');
+        if (scheme > 0 && EVALUATED_PATH.contains(uri.substring(0, scheme))) {
+            return null;
+        }
+        String head = uri.indexOf('?') > 0 ? uri.substring(0, 
uri.indexOf('?')) : uri;
+        int start = head.indexOf("${");
+        if (start < 0) {
+            return null;
+        }
+        if (start >= 2 && head.startsWith(":#", start - 2)) {
+            return null; // :#${...} is a parameter the component binds per 
message, not part of the address
+        }
+        int end = head.indexOf('}', start);
+        return end > 0 ? head.substring(start, end + 1) : 
head.substring(start);
+    }
+
+    /**
+     * Components that read their path as a script, a statement or a template 
name and evaluate it for each message,
+     * where an expression in the path is what the component is for.
+     */
+    private static final Set<String> EVALUATED_PATH = Set.of("language", 
"sql", "sql-stored", "elsql", "jdbc",
+            "spring-jdbc", "mybatis", "xquery", "xslt");

Review Comment:
   Confirmed, and you are right about the reason: I put xslt and xquery in that 
list on a guess, not on reading them. Both resolve their resource once when the 
route starts, and a stylesheet per message is what `toD` and 
`allowTemplateFromHeader` are for - so a `${...}` in their path is exactly the 
mistake this check is meant to report. Same for `mybatis` and the jdbc family, 
which I had also added without verifying.
   
   The list is now the three I did read in their own producer: `language` (its 
path is a script in another language), `micrometer` and `opentelemetry-metrics` 
(both evaluate the metric name per message in 
`AbstractMicrometerProducer.process` / 
`AbstractOpenTelemetryProducer.process`). A test now locks the xslt case as 
reported.
   
   The list itself is the wrong mechanism though, so I filed CAMEL-24918: a 
`@Metadata(supportSimpleExpression = true)` flag that reaches the catalog, 
derived automatically for options typed `Expression` or `Predicate`, so the 
validator asks the component instead of me guessing. That follow-up removes the 
hardcoded pair as well.



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