This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch fix/CAMEL-25748 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 4be2bdcb6ed642bee22d7c15e7ca2eea751e2aca Author: Claus Ibsen <[email protected]> AuthorDate: Sun Oct 18 09:56:01 2026 +0200 CAMEL-25748: camel-yaml-dsl-validator - the hint for a script file under groovy: resource: keeps the resource: prefix A model often writes groovy: {resource: "file:x.groovy"}. The hint for a language text under another key copied the value into expression:, which gave expression: "file:x.groovy", and Groovy runs that as code. A script file is loaded with the resource: prefix, so the hint now adds it and says so. This covers a value under resource: or resourceUri:, and a file:, classpath: or http(s): value under script:. Inline code and a value that has the prefix keep the hint as before. 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 | 24 +++++++++++++-- .../QuotingAndOptionPlacementHintTest.java | 35 ++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) 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 3254ac7cf905..af763a3624be 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 @@ -129,6 +129,27 @@ final class SchemaHints { } /** The text of a scalar property of the error's node, or the fallback when it is not a scalar. */ + /** A value that names a script file by its scheme, as resource: loads it. */ + private static final Pattern SCRIPT_FILE = Pattern.compile("(file|classpath|https?):.+", Pattern.DOTALL); + + /** + * Where the text of a language goes, for the text written under another key (groovy: {script: "..."}). A script + * file under resource: (groovy: {resource: "file:x.groovy"}) is loaded with the resource: prefix, which the value + * often lacks (CAMEL-25748): the expression "file:x.groovy" would run as code. + */ + static String languageTextHint(String language, String property, String value) { + String key = property.toLowerCase(Locale.ROOT); + String text = value.strip(); + boolean scriptFile = !text.startsWith("resource:") + && (key.contains("resource") || key.startsWith("script") && SCRIPT_FILE.matcher(text).matches()); + if (scriptFile) { + return "a " + language + " script file is loaded in expression: with the resource: prefix, write " + language + + ": {expression: \"resource:" + text.replace("\"", "\\\"") + "\"}"; + } + return "the " + language + " text goes in expression:, write " + language + ": {expression: \"" + + value.replace("\"", "\\\"") + "\"}"; + } + private static String valueOf(Match m, String property, String fallback) { JsonNode instance = m.error().getInstanceNode(); JsonNode value = instance != null ? instance.get(property) : null; @@ -596,8 +617,7 @@ final class SchemaHints { && m.error().getInstanceNode() != null && !m.error().getInstanceNode().has("expression") && m.error().getInstanceNode().path(m.unknown()).isTextual(), - m -> "the " + m.name() + " text goes in expression:, write " + m.name() + ": {expression: \"" - + valueOf(m, m.unknown(), "...").replace("\"", "\\\"") + "\"}"), + m -> languageTextHint(m.name(), m.unknown(), valueOf(m, m.unknown(), "..."))), unknownProperty(null, m -> YamlValidator.closest(m.unknown(), m.validator().knownProperties(m.schemaLocation())) != null, m -> "did you mean '" diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/QuotingAndOptionPlacementHintTest.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/QuotingAndOptionPlacementHintTest.java index bb401243e07c..5560ee7dc377 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/QuotingAndOptionPlacementHintTest.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/QuotingAndOptionPlacementHintTest.java @@ -151,6 +151,41 @@ public class QuotingAndOptionPlacementHintTest { .contains("the simple text goes in expression:, write simple: {expression: \"${body} != null\"}")); } + /** CAMEL-25748: a script file is loaded with the resource: prefix, which the value under resource: often lacks. */ + @Test + public void aScriptFileUnderAnotherKey() { + String yaml = """ + - route: + from: + uri: timer:x + steps: + - setBody: + expression: + groovy: + resource: "file:shipment-mapping.groovy" + - setHeader: + name: sku + expression: + groovy: + script: "classpath:sku.groovy" + - setHeader: + name: qty + expression: + groovy: + resourceUri: "resource:file:qty.groovy" + """; + List<String> lines = lines(yaml); + assertThat(lines).anySatisfy(m -> assertThat(m) + .contains("a groovy script file is loaded in expression: with the resource: prefix," + + " write groovy: {expression: \"resource:file:shipment-mapping.groovy\"}")); + assertThat(lines).anySatisfy(m -> assertThat(m) + .contains("write groovy: {expression: \"resource:classpath:sku.groovy\"}")); + // a value that has the prefix keeps it once + assertThat(lines).anySatisfy(m -> assertThat(m) + .contains("write groovy: {expression: \"resource:file:qty.groovy\"}")); + assertThat(lines).noneSatisfy(m -> assertThat(m).contains("resource:resource:")); + } + private static List<String> lines(String yaml) { try { return YamlValidator.describeAll(yaml, validator.validate(yaml));
