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

Reply via email to