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 45e7db3976b4 CAMEL-24883: camel-jbang - the validator's Simple checks 
read expressions written as YAML block scalars
45e7db3976b4 is described below

commit 45e7db3976b47deb28f276135992aaa53d5d63b9
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Sep 22 07:10:02 2026 +0200

    CAMEL-24883: camel-jbang - the validator's Simple checks read expressions 
written as YAML block scalars
    
    Fixes https://issues.apache.org/jira/browse/CAMEL-24883
    
    The Simple checks of the camel-jbang validator (top-level ternary, a 
placeholder as a logical operand, and the other Simple rows) read the 
expression from the inline forms only. Written as a block scalar the expression 
was skipped:
    
    ```yaml
    - setBody:
        expression:
          simple: |
            ${exchangeProperty.CamelTimerCounter} == 0 ? 
'resource:file:order.json' : 'resource:file:order-bad-email.json'
    ```
    
    In the camel-jbang-mcp stepwise benchmark the same top-level ternary was 
refused with the right hint when written inline and accepted as a block scalar; 
the route then failed at runtime and the model gave up on Simple and rewrote 
the step in Groovy (CAMEL-24882).
    
    `YamlLines.blockScalar` reads the lines indented under a `|`, `|-`, `|+`, 
`>`, `>-` or `>+` indicator, removes their common indentation and joins them 
(newlines for `|`, spaces for `>`); `SimpleChecks` uses it for `simple: |`, 
`expression: |` under a simple key and `message: |` of a log step, reporting 
the key's line as the inline form does. Test in `SourceValidatorSimpleTest`; 
the 236 ai tests and every documentation example pass.
---
 .../dsl/jbang/core/commands/ai/SimpleChecks.java   |  7 ++++
 .../dsl/jbang/core/commands/ai/YamlLines.java      | 42 ++++++++++++++++++++++
 .../commands/ai/SourceValidatorSimpleTest.java     | 27 ++++++++++++++
 3 files changed, 76 insertions(+)

diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SimpleChecks.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SimpleChecks.java
index 395f19bddc3c..4c1d5d067a2b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SimpleChecks.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SimpleChecks.java
@@ -58,6 +58,7 @@ final class SimpleChecks {
 
             String simpleText = null;
             int lineNum = i + 1;
+            int textLine = i;
             int lineIndent = countLeadingSpaces(line);
             boolean isLogMessage = false;
 
@@ -78,6 +79,7 @@ final class SimpleChecks {
                     if (next.startsWith("expression:")) {
                         simpleText = extractYamlValue(next, "expression");
                         lineNum = j + 1;
+                        textLine = j;
                     }
                     break;
                 }
@@ -91,6 +93,11 @@ final class SimpleChecks {
                 }
             }
 
+            // a block scalar (simple: | ...) holds its text on the following 
lines (CAMEL-24883)
+            if (YamlLines.isBlockIndicator(simpleText)) {
+                simpleText = YamlLines.blockScalar(lines, textLine, 
simpleText);
+                lineNum = textLine + 1;
+            }
             if (simpleText == null || simpleText.isEmpty()) {
                 continue;
             }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
index 9e45dbf8539b..5d96b6713188 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.ai;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -159,6 +161,46 @@ final class YamlLines {
         return null;
     }
 
+    /**
+     * Whether a scalar value is a YAML block indicator: |, |-, |+, >, >- or 
>+ (the text follows on the next lines).
+     */
+    static boolean isBlockIndicator(String value) {
+        return value != null && (value.equals("|") || value.equals("|-") || 
value.equals("|+")
+                || value.equals(">") || value.equals(">-") || 
value.equals(">+"));
+    }
+
+    /**
+     * The text of a block scalar whose indicator is on line {@code lineIdx}: 
the following lines indented deeper than
+     * that line, with their common indentation removed, joined by newlines 
for | and by spaces for > (CAMEL-24883).
+     */
+    static String blockScalar(String[] lines, int lineIdx, String indicator) {
+        int indent = countLeadingSpaces(lines[lineIdx]);
+        List<String> block = new ArrayList<>();
+        int common = Integer.MAX_VALUE;
+        for (int j = lineIdx + 1; j < lines.length; j++) {
+            if (lines[j].isBlank()) {
+                block.add("");
+                continue;
+            }
+            int n = countLeadingSpaces(lines[j]);
+            if (n <= indent) {
+                break;
+            }
+            common = Math.min(common, n);
+            block.add(lines[j]);
+        }
+        while (!block.isEmpty() && block.get(block.size() - 1).isEmpty()) {
+            block.remove(block.size() - 1);
+        }
+        if (block.isEmpty()) {
+            return "";
+        }
+        final int strip = common;
+        String sep = indicator.startsWith(">") ? " " : "\n";
+        return block.stream().map(l -> l.isEmpty() ? "" : 
l.substring(Math.min(strip, l.length())))
+                .collect(java.util.stream.Collectors.joining(sep)).trim();
+    }
+
     static String extractYamlValue(String trimmed, String key) {
         String prefix = key + ":";
         if (!trimmed.startsWith(prefix)) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorSimpleTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorSimpleTest.java
index c9eb96f53abf..3842cdb4046b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorSimpleTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorSimpleTest.java
@@ -332,6 +332,33 @@ class SourceValidatorSimpleTest {
         assertThat(msgs.get(1)).startsWith("Line 14:").contains("Unexpected 
token body");
     }
 
+    /** CAMEL-24883: the same expression as a block scalar gets the same hint; 
a > scalar is joined by spaces. */
+    @Test
+    void aBlockScalarExpressionIsCheckedToo() {
+        List<String> msgs = SourceValidator.validateYamlSimple(
+                """
+                        - from:
+                            uri: timer:tick
+                            steps:
+                              - setBody:
+                                  expression:
+                                    simple: |
+                                      ${exchangeProperty.CamelTimerCounter} == 
0 ? 'resource:file:order.json' : 'resource:file:other.json'
+                              - setBody:
+                                  simple:
+                                    expression: >-
+                                      ${body.size()} == 0
+                                      ? ${null} : ${body[0]}
+                              - log:
+                                  message: |
+                                    all fine: ${body}
+                        """,
+                catalog);
+        assertThat(msgs).hasSize(2);
+        assertThat(msgs.get(0)).startsWith("Line 6:").contains("Simple has no 
top-level ternary");
+        assertThat(msgs.get(1)).startsWith("Line 10:").contains("Simple has no 
top-level ternary");
+    }
+
     @Test
     void aTopLevelTernaryInAnExpressionIsReported() {
         // the ? and : are outside ${...} so they are literal text: the route 
silently sets the body to

Reply via email to