This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new a615186a64c6 CAMEL-25402: camel-yaml-dsl-validator - Say how to write
expression: when it is given text instead of a language (#27471)
a615186a64c6 is described below
commit a615186a64c636ab5d2787335d73d0e789aff8b9
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Oct 7 06:45:31 2026 +0200
CAMEL-25402: camel-yaml-dsl-validator - Say how to write expression: when
it is given text instead of a language (#27471)
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"}}.
Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../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);