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 <[email protected]>
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) <[email protected]>
    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);

Reply via email to