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