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);
