You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/10/07 03:50:03 UTC
(camel) 01/01: CAMEL-25402: camel-yaml-dsl-validator - Say how to write expression: when it is given text instead of a language
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch fix/CAMEL-25402
in repository https://gitbox.apache.org/repos/asf/camel.git
commit df0c143a8a8c63ee8858ee43b01cd96fa7dd2bf6
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Oct 7 05:49:47 2026 +0200
CAMEL-25402: camel-yaml-dsl-validator - Say how to write expression: when it is given text instead of a language
split: {expression: "${body}"} and setHeader: {expression: constant 404} were
reported twice as "expression: string found, object expected", which does not
say what to write. Report it once with the language map to write, e.g.
expression: {constant: {expression: "404"}}.
Co-Authored-By: Claude Opus 5.5 (1M context) <no...@anthropic.com>
Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
---
.../camel/dsl/yaml/validator/SchemaHints.java | 29 ++++++++++
.../validator/YamlValidatorExpressionHintTest.java | 65 ++++++++++++++++++++++
2 files changed, 94 insertions(+)
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java
index 5f45e6330cfd..576430ae89d5 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java
@@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.fasterxml.jackson.databind.JsonNode;
@@ -232,8 +233,36 @@ final class SchemaHints {
"a plain value (%s) found, an expression expected: write %s: {constant: {expression: \"%s\"}} for a fixed value, or %s: {simple: {expression: \"...\"}} for a dynamic one",
value, m.name(), value, m.name());
},
+ "type", "expression"),
+ // split: {expression: "${body}"}, setHeader: {expression: constant 404}: the expression: key of an EIP
+ // holds a language map, not the text
+ replace("type", ".*/expression",
+ m -> {
+ JsonNode instance = m.error().getInstanceNode();
+ return instance != null && instance.isValueNode() && m.message().contains("object expected");
+ },
+ m -> expressionKeyHint(m.error().getInstanceNode().asText()),
"type", "expression"));
+ /** The languages a model writes in front of the text, as in expression: constant 404. */
+ private static final Pattern LANGUAGE_PREFIX = Pattern.compile(
+ "(constant|simple|header|exchangeProperty|variable|jq|jsonpath|xpath|groovy|tokenize|method|ref)\\s+(.+)",
+ Pattern.DOTALL);
+
+ static String expressionKeyHint(String value) {
+ String language = value.contains("${") ? "simple" : "constant";
+ String text = value;
+ Matcher prefix = LANGUAGE_PREFIX.matcher(value.trim());
+ if (prefix.matches()) {
+ language = prefix.group(1);
+ text = prefix.group(2).trim();
+ }
+ return String.format(
+ "a plain value (%s) found, a language expected: expression: holds the language as its key, write"
+ + " expression: {%s: {expression: \"%s\"}}",
+ value, language, text.replace("\"", "\\\""));
+ }
+
// -------------------------------------------------------------------------------------------------------------
// list hints
// -------------------------------------------------------------------------------------------------------------
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorExpressionHintTest.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorExpressionHintTest.java
index 0f04e2ba86cb..9f6f3c2ebb00 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorExpressionHintTest.java
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorExpressionHintTest.java
@@ -90,6 +90,59 @@ public class YamlValidatorExpressionHintTest {
assertHint(yaml, "completionSizeExpression: {constant: {expression: \"10\"}}");
}
+ @Test
+ public void testSplitExpressionAsText() {
+ String yaml = """
+ - route:
+ from:
+ uri: timer:tick
+ steps:
+ - split:
+ expression: "${body}"
+ steps:
+ - log:
+ message: "${body}"
+ """;
+ assertLanguageHint(yaml, "expression: {simple: {expression: \"${body}\"}}");
+ }
+
+ @Test
+ public void testSetHeaderExpressionWithLanguagePrefix() {
+ String yaml = """
+ - route:
+ from:
+ uri: timer:tick
+ steps:
+ - setHeader:
+ name: CamelHttpResponseCode
+ expression: constant 404
+ """;
+ assertLanguageHint(yaml, "expression: {constant: {expression: \"404\"}}");
+ }
+
+ @Test
+ public void testExpressionKeyWithLanguageIsAccepted() {
+ String yaml = """
+ - route:
+ from:
+ uri: timer:tick
+ steps:
+ - split:
+ expression:
+ simple:
+ expression: "${body}"
+ steps:
+ - setHeader:
+ name: CamelHttpResponseCode
+ expression:
+ constant:
+ expression: "404"
+ """;
+ for (YamlValidator validator : List.of(classic, canonical)) {
+ assertThat(validate(validator, yaml)).isEmpty();
+ }
+ }
+
@Test
public void testExpressionFormIsAccepted() {
// explicit form, so that it is valid in canonical mode too
@@ -130,6 +183,18 @@ public class YamlValidatorExpressionHintTest {
}
}
+ private void assertLanguageHint(String yaml, String expected) {
+ for (YamlValidator validator : List.of(classic, canonical)) {
+ String mode = validator.isCanonical() ? "canonical" : "classic";
+ List<Error> errors = validate(validator, yaml);
+ assertThat(errors.stream().filter(e -> e.getMessage().contains("a language expected")))
+ .as("%s mode must report the hint once:\n%s", mode, errors)
+ .hasSize(1)
+ .allMatch(e -> e.getMessage().contains(expected));
+ assertThat(errors).noneMatch(e -> e.getMessage().contains("object expected"));
+ }
+ }
+
private List<Error> validate(YamlValidator validator, String yaml) {
try {
return validator.validate(yaml);