davsclaus commented on code in PR #28723:
URL: https://github.com/apache/camel/pull/28723#discussion_r4425637462
##########
dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/TextBodyFlow.java:
##########
@@ -691,6 +716,16 @@ private static Text setBodyText(JsonNode value) {
if (expression == null) {
return null;
}
+ // r24 connect-http-client: a setBody ${null} before the split of the
lines, and a constant "" before a GET
+ if ("constant".equals(language) && expression.isEmpty()
+ || "simple".equals(language) && expression.trim().isEmpty()) {
Review Comment:
Done in 5dc28446bf25: the two parts of the condition now have explicit
parentheses.
_Claude Code on behalf of davsclaus_
##########
dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java:
##########
@@ -1281,12 +1331,100 @@ void checkDynamicUri(JsonNode node, NodePath path,
List<Error> errors) {
+ " is fixed when the route starts, so
it is sent as text: write toD: to build"
+ " the uri for each message")
.build());
+ } else {
+ String option = staticOptionWithExpression(uri, value);
+ if (option != null) {
+ errors.add(Error.builder()
+ .keyword("type")
+ .instanceLocation(at)
+ .messageKey("type")
+ .format(new MessageFormat("{0}"))
+ .arguments("to: " + option)
+ .build());
+ }
}
}
checkDynamicUri(value, path.append(name), errors);
}
}
+ /**
+ * An option of a to: with a simple expression that the component does not
evaluate, said with the header to set
+ * instead; null when there is none. The options of a to: are fixed when
the route starts: r24
+ * connect-service-kafka-orders step 3 wrote key: "${header.orderId}" on a
to: kafka, and every record got that text
+ * as its key. Only an option with a header of its own for each message
(CamelKafkaKey for the key of kafka) is
+ * reported: some components evaluate their options themselves (openfga,
the method of bean), and the catalog marks
+ * only some of them (the fileName of file), or says so in the description.
+ */
+ private String staticOptionWithExpression(String uri, JsonNode to) {
+ if (uri == null) {
+ return null;
+ }
+ int colon = uri.indexOf(':');
+ int question = uri.indexOf('?');
+ String component = colon > 0 && (question < 0 || colon < question) ?
uri.substring(0, colon)
+ : question > 0 ? uri.substring(0, question) : uri;
+ Map<String, String> options = new LinkedHashMap<>();
+ if (question > 0) {
+ for (String pair : uri.substring(question + 1).split("&")) {
+ int eq = pair.indexOf('=');
+ if (eq > 0) {
+ options.put(pair.substring(0, eq), pair.substring(eq + 1));
+ }
+ }
+ }
+ JsonNode parameters = to != null && to.isObject() ?
to.get("parameters") : null;
+ if (parameters != null && parameters.isObject()) {
+ for (var it = parameters.fieldNames(); it.hasNext();) {
+ String name = it.next();
+ if (parameters.get(name).isValueNode()) {
+ options.put(name, parameters.get(name).asText());
+ }
+ }
+ }
+ ComponentModel model;
+ try {
+ model = catalog().componentModel(component);
+ } catch (Exception e) {
+ LOG.debug("Cannot read the catalog model of component {}: its
options are left alone", component, e);
+ return null;
+ }
+ if (model == null) {
+ return null;
+ }
+ for (Map.Entry<String, String> option : options.entrySet()) {
+ String text = option.getValue();
+ int start = text.indexOf("${");
+ if (start < 0 || start >= 2 && text.startsWith(":#", start - 2)) {
+ continue;
+ }
+ var known = model.getEndpointParameterOptions().stream()
+ .filter(o -> o.getName().equals(option.getKey()))
+ .findFirst();
+ String description = known.map(o ->
String.valueOf(o.getDescription()).toLowerCase(Locale.ROOT)).orElse("");
+ if (known.isEmpty() || known.get().isSupportSimpleExpression() ||
description.contains("expression")
+ || description.contains("simple")) {
+ continue;
+ }
+ // the header the producer reads for each message, such as
CamelKafkaKey for the key of kafka
+ String header = model.getEndpointHeaders().stream()
+ .map(BaseOptionModel::getName)
+ .filter(h -> h.equalsIgnoreCase("Camel" + component +
option.getKey())
+ || h.equalsIgnoreCase(component + "." +
option.getKey()))
+ .findFirst()
Review Comment:
Fixed in this PR (5dc28446bf25) rather than in a follow-up. Removing the
dash alone was not enough: aws2-sqs has `CamelAwsSqs*` headers (no digit),
influxdb2 has `CamelInfluxDB2*` (digit kept), and the kubernetes and llm
components use a start that is not their scheme at all (`CamelKubernetes`,
`CamelOpenAI`). The header is now matched against:
- `Camel` + the scheme with no dash, with and without its digits;
- the start that all the `Camel*` headers of the component share, cut back
to a word boundary;
- `scheme.option`, as before.
A header labelled `consumer` in the catalog is left alone, since the
producer does not read it (for example `CamelSpringRabbitmqRoutingKey`, or
`CamelMailCopyTo` for smtp).
Across the catalog this finds 780 option headers instead of 333, and every
match the old rule found. Matching on the option name as a suffix was rejected:
it gave `CamelKafkaPartitionKey` for the kafka `key`, and 8 headers for the llm
`model`. New tests cover aws2-sqs, kubernetes-pods and spring-rabbitmq in
`YamlValidatorDynamicUriTest`.
_Claude Code on behalf of davsclaus_
--
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]