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 52e115605675 CAMEL-24722: camel-yaml-dsl - the validator and 
deserializer hints recommend the canonical YAML format, not the deprecated 
shorthand (#26401)
52e115605675 is described below

commit 52e115605675c33420cb6d11f3cc11370bbd9ba8
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 14 21:16:36 2026 +0200

    CAMEL-24722: camel-yaml-dsl - the validator and deserializer hints 
recommend the canonical YAML format, not the deprecated shorthand (#26401)
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 .../dsl/jbang/core/commands/ai/EndpointChecks.java | 10 +++--
 .../yaml/common/YamlDeserializationContext.java    |  5 ++-
 .../deserializers/ExpressionDeserializers.java     |  2 +-
 .../dsl/yaml/GenerateYamlDeserializersMojo.java    |  4 +-
 .../camel/dsl/yaml/validator/YamlValidator.java    | 49 ++++++++++++----------
 .../validator/YamlValidatorExpressionHintTest.java |  6 +--
 .../validator/YamlValidatorPropertyHintTest.java   |  8 ++--
 .../apache/camel/dsl/yaml/OnExceptionTest.groovy   |  4 +-
 8 files changed, 50 insertions(+), 38 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
index fd3e1178683c..f992dd13de86 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
@@ -81,7 +81,8 @@ final class EndpointChecks {
             if (several.find()) {
                 // to: direct:a,direct:b : one endpoint per to:, several go 
through multicast or recipientList
                 errors.add(linePrefix(i) + "a to: takes one endpoint; \"" + 
uri + "\" names several: send to each with"
-                           + " multicast: {to: [...]} (all of them) or 
recipientList: {constant: \"" + uri + "\"}"
+                           + " multicast: {to: [...]} (all of them) or 
recipientList: {expression: {constant: {expression: \""
+                           + uri + "\"}}}"
                            + " (a list evaluated at runtime), or write one - 
to: step per endpoint");
                 continue;
             }
@@ -205,9 +206,10 @@ final class EndpointChecks {
             Map.entry("timer:interval", "write period=<millis>"),
             Map.entry("timer:delayMs", "write delay=<millis>"),
             Map.entry("timer:repeat", "write repeatCount=<n>"),
-            Map.entry("timer:body", "a timer message has no body: set it with 
a setBody step (setBody: {constant: \"...\"})"),
-            Map.entry("timer:message",
-                    "a timer message has no body: set it with a setBody step 
(setBody: {constant: \"...\"})"),
+            Map.entry("timer:body", "a timer message has no body: set it with 
a setBody step"
+                                    + " (setBody: {expression: {constant: 
{expression: \"...\"}}})"),
+            Map.entry("timer:message", "a timer message has no body: set it 
with a setBody step"
+                                       + " (setBody: {expression: {constant: 
{expression: \"...\"}}})"),
             Map.entry("timer:cron", "a cron expression is the cron or quartz 
component: cron:tick?schedule=0/5+*+*+*+*+?"),
             Map.entry("timer:schedule", "a cron expression is the cron or 
quartz component: cron:tick?schedule=0/5+*+*+*+*+?"),
             Map.entry("log:message", "the message is the body; a text is set 
with a setBody step or the log EIP"),
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializationContext.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializationContext.java
index 5cbabd0a741b..87bb44f521b5 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializationContext.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializationContext.java
@@ -415,9 +415,10 @@ public class YamlDeserializationContext extends 
StandardConstructor implements C
                     ? e.getProblem().substring("Unsupported field: ".length()) 
: null;
             String hint = "";
             if ("bean".equals(field)) {
-                hint = " (the bean language is written as method: {ref: 
myBean, method: process})";
+                hint = " (the bean language is written as expression: {method: 
{ref: myBean, method: process}})";
             } else if ("expression".equals(field) || "language".equals(field)) 
{
-                hint = " (an expression is written with the language as the 
key: simple: \"...\", constant: \"...\")";
+                hint = " (an expression is written with the expression: 
wrapper and the language as the key: expression: {simple:"
+                       + " {expression: \"...\"}}, expression: {constant: 
{expression: \"...\"}})";
             }
             throw new YamlDeserializationException(
                     node, "Error constructing YAML node id: " + id + ": 
unsupported field: " + field + hint, e);
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
index 9933c583d163..cffcaf708cfd 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ExpressionDeserializers.java
@@ -223,7 +223,7 @@ public final class ExpressionDeserializers extends 
YamlDeserializerSupport {
         public Object construct(Node node) {
             if (!(node instanceof MappingNode)) {
                 String text = node instanceof ScalarNode ? asText(node) : 
node.getNodeType().name().toLowerCase();
-                throw new InvalidExpressionException(node, "an expression is 
expected here, not a plain value (" + text + "): write constant: \"" + text + 
"\" for a fixed value, or simple: \"...\" for a dynamic one");
+                throw new InvalidExpressionException(node, "an expression is 
expected here, not a plain value (" + text + "): write constant: {expression: 
\"" + text + "\"} for a fixed value, or simple: {expression: \"...\"} for a 
dynamic one");
             }
             ExpressionDefinition val = constructExpressionType(node);
             return new 
org.apache.camel.model.ExpressionSubElementDefinition(val);
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
index 8b81504b1883..633e2f2e4ed3 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
@@ -306,8 +306,8 @@ public class GenerateYamlDeserializersMojo extends 
GenerateYamlSupportMojo {
                                                 
ClassName.get("org.apache.camel.dsl.yaml.common.exception",
                                                         
"InvalidExpressionException"),
                                                 "an expression is expected 
here, not a plain value (",
-                                                "): write constant: \"",
-                                                "\" for a fixed value, or 
simple: \"...\" for a dynamic one")
+                                                "): write constant: 
{expression: \"",
+                                                "\"} for a fixed value, or 
simple: {expression: \"...\"} for a dynamic one")
                                         .endControlFlow()
                                         .addStatement("$T val = 
constructExpressionType(node)", CN_EXPRESSION_DEFINITION)
                                         .addStatement("return new 
org.apache.camel.model.ExpressionSubElementDefinition(val)")
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
index 884b57cf4c8d..25351b09f954 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
@@ -445,16 +445,17 @@ public class YamlValidator {
             "idempotentConsumer");
 
     private static final Map<String, String> EXPRESSION_EXAMPLES = Map.of(
-            "split", "split: {tokenize: \",\"} or split: {simple: \"${body}\"} 
(delimiter only applies to the result of"
-                     + " the expression)",
-            "filter", "filter: {simple: \"${header.type} == 'urgent'\"}",
-            "when", "when: {simple: \"${body} contains 'x'\"}",
-            "setBody", "setBody: {simple: \"Hello ${body}\"} or setBody: 
{constant: \"Hello\"}",
-            "setHeader", "setHeader: {name: id, simple: \"${exchangeId}\"}",
-            "loop", "loop: {constant: \"3\"}",
-            "recipientList", "recipientList: {simple: \"${header.to}\"}",
-            "script", "script: {groovy: \"...\"}",
-            "delay", "delay: {constant: \"1000\"}");
+            "split", "split: {expression: {tokenize: {token: \",\"}}} or 
split: {expression: {simple: {expression:"
+                     + " \"${body}\"}}} (delimiter only applies to the result 
of the expression)",
+            "filter", "filter: {expression: {simple: {expression: 
\"${header.type} == 'urgent'\"}}}",
+            "when", "when: {expression: {simple: {expression: \"${body} 
contains 'x'\"}}}",
+            "setBody", "setBody: {expression: {simple: {expression: \"Hello 
${body}\"}}} or setBody: {expression:"
+                       + " {constant: {expression: \"Hello\"}}}",
+            "setHeader", "setHeader: {name: id, expression: {simple: 
{expression: \"${exchangeId}\"}}}",
+            "loop", "loop: {expression: {constant: {expression: \"3\"}}}",
+            "recipientList", "recipientList: {expression: {simple: 
{expression: \"${header.to}\"}}}",
+            "script", "script: {expression: {groovy: {expression: \"...\"}}}",
+            "delay", "delay: {expression: {constant: {expression: 
\"1000\"}}}");
 
     private static final Set<String> SCRIPT_LANGUAGES = Set.of("groovy", "js", 
"python", "python3", "mvel", "ognl",
             "jq", "jsonpath", "xpath", "xquery", "spel", "jactl", "java", 
"joor", "quickjs", "wasm", "datasonnet");
@@ -508,7 +509,8 @@ public class YamlValidator {
                         .messageKey("type")
                         .format(new MessageFormat("{0}"))
                         .arguments(name + ": ${...} is simple syntax, not " + 
name + ": write the expression in " + name
-                                   + " (" + example + "), or use simple: \"" + 
text.replace("\"", "'") + "\"")
+                                   + " (" + example + "), or use simple: 
{expression: \"" + text.replace("\"", "'")
+                                   + "\"}")
                         .build());
             }
             checkSimpleSyntaxInScripts(value, path.append(name), errors);
@@ -552,7 +554,8 @@ public class YamlValidator {
             if (EXPRESSION_REQUIRED.contains(name) && (value == null || 
value.isNull() || value.isObject())
                     && !hasExpression(value)) {
                 String example = EXPRESSION_EXAMPLES.getOrDefault(name,
-                        name + ": {simple: \"...\"} or " + name + ": 
{constant: \"...\"}");
+                        name + ": {expression: {simple: {expression: 
\"...\"}}} or " + name
+                                                                        + ": 
{expression: {constant: {expression: \"...\"}}}");
                 errors.add(Error.builder()
                         .keyword("required")
                         .instanceLocation(path.append(name))
@@ -873,7 +876,7 @@ public class YamlValidator {
         String name = location.substring(location.lastIndexOf('/') + 1);
         String value = instance.asText();
         String message = String.format(
-                "a plain value (%s) found, an expression expected: write %s: 
{constant: \"%s\"} for a fixed value, or %s: {simple: \"...\"} for a dynamic 
one",
+                "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, name, value, name);
         // the message is not a MessageFormat pattern (it contains braces), so 
pass it as the single argument
         return Error.builder()
@@ -917,8 +920,9 @@ public class YamlValidator {
                     .instanceLocation(error.getInstanceLocation())
                     .messageKey("type")
                     .format(new MessageFormat("{0}"))
-                    .arguments(error.getMessage() + " (an expression is 
written with the language as the key, e.g."
-                               + " groovy: \"...\", simple: \"...\", constant: 
\"...\"; the language: form is"
+                    .arguments(error.getMessage() + " (an expression is 
written with the language as the key and its"
+                               + " expression: property, e.g. groovy: 
{expression: \"...\"}, simple: {expression: \"...\"},"
+                               + " constant: {expression: \"...\"}; the 
language: form is"
                                + " language: {language: groovy, expression: 
\"...\"})")
                     .build();
         }
@@ -949,7 +953,8 @@ public class YamlValidator {
                     .format(new MessageFormat("{0}"))
                     .arguments(error.getMessage() + " (" + entry + " is a map, 
not a list: - " + entry + ": followed by its"
                                + " properties indented" + 
(entry.equals("onException")
-                                       ? " (exception: [java.lang.Exception], 
handled: {constant: \"true\"}, steps: [...])"
+                                       ? " (exception: [java.lang.Exception], 
handled: {constant: {expression: \"true\"}},"
+                                         + " steps: [...])"
                                        : "")
                                + "; several of them are several - " + entry + 
": items)")
                     .build();
@@ -1088,18 +1093,20 @@ public class YamlValidator {
                 && 
(EXPRESSION_REQUIRED.contains(location.substring(location.lastIndexOf('/') + 1))
                         || location.endsWith("/expression"))) {
             // setBody: {script: ...}: script is an EIP; the language is the 
key of an expression
-            hint = "script is an EIP step, not a language: write the language 
as the key of the expression (groovy:"
-                   + " \"...\", simple: \"...\"), or run a script as its own 
step with - script: {groovy: \"...\"}";
+            hint = "script is an EIP step, not a language: write the language 
as the key of the expression (expression:"
+                   + " {groovy: {expression: \"...\"}}, expression: {simple: 
{expression: \"...\"}}), or run a script as"
+                   + " its own step with - script: {expression: {groovy: 
{expression: \"...\"}}}";
         } else if (unknown.equals("bean") && !location.endsWith("/steps")) {
             // setBody: {bean: myBean} : the bean language is method:
-            hint = "the bean language is written as method: (method: {ref: 
myBean, method: process}), or call the bean"
-                   + " as a step with - bean: {ref: myBean, method: process}";
+            hint = "the bean language is written as method: (expression: 
{method: {ref: myBean, method: process}}), or"
+                   + " call the bean as a step with - bean: {ref: myBean, 
method: process}";
         } else if 
(location.matches(".*/(setHeader|setProperty|setVariable|removeHeader|removeProperty|removeVariable)")
                 && closest(unknown, 
knownProperties(String.valueOf(error.getSchemaLocation()))) == null) {
             // setHeader: {CamelNumberA: {simple: ...}} : the name is a 
property, not the key
             String eip = location.substring(location.lastIndexOf('/') + 1);
             hint = "the name is a property: " + eip + ": {name: " + unknown
-                   + (eip.startsWith("set") ? ", simple: \"...\"}" : "}") + " 
(" + unknown + " is not the key)";
+                   + (eip.startsWith("set") ? ", expression: {simple: 
{expression: \"...\"}}}" : "}")
+                   + " (" + unknown + " is not the key)";
         } else if (location.endsWith("/bean")
                 && (unknown.equals("parameters") || unknown.equals("args") || 
unknown.equals("arguments"))) {
             hint = "arguments are written in the method call: bean: {ref: 
myBean, method: \"process(${body}, 'x')\"}";
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 1fe58e691fd5..f6668272a421 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
@@ -53,7 +53,7 @@ public class YamlValidatorExpressionHintTest {
                       - log:
                           message: "Error: ${exception.message}"
                 """;
-        assertHint(yaml, "handled: {constant: \"true\"}", "handled: {simple: 
\"...\"}");
+        assertHint(yaml, "handled: {constant: {expression: \"true\"}}", 
"handled: {simple: {expression: \"...\"}}");
     }
 
     @Test
@@ -67,7 +67,7 @@ public class YamlValidatorExpressionHintTest {
                       - log:
                           message: "Error: ${exception.message}"
                 """;
-        assertHint(yaml, "continued: {constant: \"true\"}");
+        assertHint(yaml, "continued: {constant: {expression: \"true\"}}");
     }
 
     @Test
@@ -86,7 +86,7 @@ public class YamlValidatorExpressionHintTest {
                             - log:
                                 message: "${body}"
                 """;
-        assertHint(yaml, "completionSizeExpression: {constant: \"10\"}");
+        assertHint(yaml, "completionSizeExpression: {constant: {expression: 
\"10\"}}");
     }
 
     @Test
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
index 6426251e3d45..0492b6a97f44 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
@@ -343,7 +343,8 @@ public class YamlValidatorPropertyHintTest {
                           parameters: [1, 2]
                 """);
         assertThat(errors).anySatisfy(e -> 
assertThat(e.getMessage()).contains("property 'CamelNumberA' is not defined")
-                .contains("the name is a property: setHeader: {name: 
CamelNumberA, simple: \"...\"}"));
+                .contains(
+                        "the name is a property: setHeader: {name: 
CamelNumberA, expression: {simple: {expression: \"...\"}}}"));
         assertThat(errors).anySatisfy(e -> 
assertThat(e.getMessage()).contains("property 'parameters' is not defined")
                 .contains("arguments are written in the method call"));
     }
@@ -471,7 +472,7 @@ public class YamlValidatorPropertyHintTest {
         assertThat(errors).hasSize(1);
         assertThat(errors.get(0).getMessage())
                 .startsWith("groovy: ${...} is simple syntax, not groovy: 
write the expression in groovy (body.value < 1")
-                .contains("or use simple: \"${body.value} < 1\"");
+                .contains("or use simple: {expression: \"${body.value} < 
1\"}");
     }
 
     @Test
@@ -604,7 +605,8 @@ public class YamlValidatorPropertyHintTest {
                           text: "println 'hi'"
                 """);
         assertThat(errors).anyMatch(
-                e -> e.getMessage().contains("the language as the key") && 
e.getMessage().contains("groovy: \"...\""));
+                e -> e.getMessage().contains("the language as the key")
+                        && e.getMessage().contains("groovy: {expression: 
\"...\"}"));
     }
 
     @Test
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/OnExceptionTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/OnExceptionTest.groovy
index f880731fc838..1b8140870efc 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/OnExceptionTest.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/OnExceptionTest.groovy
@@ -77,7 +77,7 @@ class OnExceptionTest extends YamlTestSupport {
             for (Throwable t = e; t != null; t = t.cause) {
                 messages << t.message
             }
-            messages.any { it != null && it.contains('an expression is 
expected here, not a plain value (true)') && it.contains('constant: "true"') }
+            messages.any { it != null && it.contains('an expression is 
expected here, not a plain value (true)') && it.contains('constant: 
{expression: "true"}') }
     }
 
 
@@ -97,6 +97,6 @@ class OnExceptionTest extends YamlTestSupport {
             for (Throwable t = e; t != null; t = t.cause) {
                 messages << t.message
             }
-            messages.any { it != null && it.contains('Error constructing YAML 
node id: setBody: unsupported field: bean') && it.contains('method: {ref: 
myBean, method: process}') }
+            messages.any { it != null && it.contains('Error constructing YAML 
node id: setBody: unsupported field: bean') && it.contains('expression: 
{method: {ref: myBean, method: process}}') }
     }
 }

Reply via email to