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 b69d769784d6 CAMEL-24723: camel-jbang - camel validate yaml
--canonical reports the deprecated compact notation as such; the runtime WARN
covers implicit expressions (#26403)
b69d769784d6 is described below
commit b69d769784d69592aa0ebe7a6e8c2673ac543a96
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 14 19:46:02 2026 +0200
CAMEL-24723: camel-jbang - camel validate yaml --canonical reports the
deprecated compact notation as such; the runtime WARN covers implicit
expressions (#26403)
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
.../org/apache/camel/catalog/docs/yaml-dsl.adoc | 11 +-
.../commands/validate/YamlValidateCommand.java | 3 +-
.../dsl/yaml/common/YamlDeserializerBase.java | 16 ++-
.../dsl/yaml/deserializers/ModelDeserializers.java | 52 ++++++++
.../dsl/yaml/GenerateYamlDeserializersMojo.java | 2 +
.../camel/dsl/yaml/validator/YamlValidator.java | 87 +++++++++++++
.../yaml/validator/YamlCanonicalValidatorTest.java | 70 +++++++++++
.../camel-yaml-dsl/src/main/docs/yaml-dsl.adoc | 11 +-
.../camel/dsl/yaml/CompactNotationWarnTest.groovy | 136 +++++++++++++++++++++
9 files changed, 379 insertions(+), 9 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
index c403e8b2d8cf..3b172e67cd44 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
@@ -883,7 +883,9 @@ camel validate normalize --output normalized/ myroute.yaml
=== Compact notation warning
Camel logs a WARN message when YAML routes use compact (shorthand) notation
instead of
-the canonical (explicit) form. This is to encourage adopting the canonical
style which is
+the canonical (explicit) form: a step written as a string (`log: "..."`), a
language written as a
+string (`simple: "..."`), or a language key directly on the EIP (`setBody:
{simple: ...}`) instead of
+under `expression:`. This is to encourage adopting the canonical style which is
more friendly for tooling and AI assistants.
The warning is logged once per resource file and looks like:
@@ -910,7 +912,12 @@ To validate YAML routes against the canonical schema:
camel validate yaml --canonical myroute.yaml
----
-This reports any shorthands or implicit forms that are not valid in the
canonical schema.
+This reports each use of the compact notation with the canonical form to
write, for example:
+
+----
+/0/route/from/steps/0/setBody: setBody: {simple: ...} is the deprecated
compact notation: an expression is written under expression: (setBody:
{expression: {simple: {expression: "..."}}}); camel validate normalize rewrites
a file in the canonical format
+/0/route/from/steps/1/log: log: "..." is the deprecated compact notation:
write log: {message: "..."}; camel validate normalize rewrites a file in the
canonical format
+----
The `YamlValidator` class supports both schemas programmatically:
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
index 0070868ac4b7..e0591e317f7a 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
@@ -42,7 +42,8 @@ public class YamlValidateCommand extends CamelCommand {
private static final String IGNORE_FILE = "application";
@CommandLine.Option(names = { "--canonical" }, defaultValue = "false",
- description = "Validate against the canonical schema
(rejects shorthands and implicit expressions)")
+ description = "Validate against the canonical schema:
reports the deprecated compact notation (string shorthands,"
+ + " implicit expressions) with the
canonical form to write")
boolean canonical;
@CommandLine.Option(names = { "--catalog" }, defaultValue = "true",
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerBase.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerBase.java
index b078257335b8..a250e55c67d5 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerBase.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-common/src/main/java/org/apache/camel/dsl/yaml/common/YamlDeserializerBase.java
@@ -56,10 +56,7 @@ public abstract class YamlDeserializerBase<T> extends
YamlDeserializerSupport im
if (node.getNodeType() == NodeType.SCALAR) {
ScalarNode mn = (ScalarNode) node;
target = newInstance(mn.getValue());
- YamlDeserializationContext ctx = getDeserializationContext(node);
- if (ctx != null) {
- ctx.warnCompactNotationOnce(LOG);
- }
+ warnCompactNotation(node);
// line number points to the scalar itself, so it should be +1
if (line != -1) {
line++;
@@ -92,6 +89,17 @@ public abstract class YamlDeserializerBase<T> extends
YamlDeserializerSupport im
// noop
}
+ /**
+ * The compact notation (a step or a language written as a string, a
language key directly on the EIP) is
+ * deprecated: warns once per resource that the canonical notation is
recommended.
+ */
+ protected static void warnCompactNotation(Node node) {
+ YamlDeserializationContext ctx = getDeserializationContext(node);
+ if (ctx != null) {
+ ctx.warnCompactNotationOnce(LOG);
+ }
+ }
+
/**
* Creates a Java instance of the expected type from a string.
*
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
index 14df4efb7e87..416175fdc142 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
@@ -1864,6 +1864,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -2297,6 +2298,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -4204,6 +4206,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -4529,6 +4532,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -4860,6 +4864,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -5125,6 +5130,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -5271,6 +5277,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -5823,6 +5830,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -6423,6 +6431,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -6844,6 +6853,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -6922,6 +6932,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -7075,6 +7086,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -7651,6 +7663,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -7735,6 +7748,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -7807,6 +7821,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -8050,6 +8065,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -8128,6 +8144,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -8475,6 +8492,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -9083,6 +9101,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -9462,6 +9481,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -9932,6 +9952,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -10252,6 +10273,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -10517,6 +10539,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -10880,6 +10903,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -12276,6 +12300,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -12655,6 +12680,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -13068,6 +13094,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -13140,6 +13167,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -13212,6 +13240,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -13418,6 +13447,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -13724,6 +13754,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -14176,6 +14207,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -15876,6 +15908,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -16352,6 +16385,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -16470,6 +16504,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -16618,6 +16653,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -16760,6 +16796,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -16839,6 +16876,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -16992,6 +17030,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -17209,6 +17248,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -17281,6 +17321,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -17473,6 +17514,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -18482,6 +18524,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -18993,6 +19036,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -19342,6 +19386,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -20465,6 +20510,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -20646,6 +20692,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -20724,6 +20771,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -20859,6 +20907,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -21214,6 +21263,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -21352,6 +21402,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
@@ -21448,6 +21499,7 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node);
if (ed != null) {
target.setExpressionType(ed);
+ warnCompactNotation(node);
} else {
return false;
}
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
index 30687e18fb70..8b81504b1883 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlDeserializersMojo.java
@@ -627,6 +627,8 @@ public class GenerateYamlDeserializersMojo extends
GenerateYamlSupportMojo {
setProperty.addStatement("ed =
ExpressionDeserializers.constructExpressionType(propertyKey, node)");
setProperty.beginControlFlow("if (ed != null)");
setProperty.addStatement("target.setExpressionType(ed)");
+ // the language key directly on the EIP is the compact notation
(canonical is under expression:)
+ setProperty.addStatement("warnCompactNotation(node)");
setProperty.nextControlFlow("else");
setProperty.addStatement("return false");
setProperty.endControlFlow();
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
index 2b7e191b064a..884b57cf4c8d 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
@@ -267,6 +267,9 @@ public class YamlValidator {
private List<Error> validate(JsonNode target) {
var errors = filterOneOfNoise(new
ArrayList<>(schema.validate(target)));
errors.removeIf(YamlValidator::isRuntimeAcceptedScalar);
+ if (canonical) {
+ errors = withCompactNotationHints(errors);
+ }
errors = withExpressionHints(errors);
errors = withPropertyHints(errors);
errors = withListHints(errors);
@@ -303,6 +306,90 @@ public class YamlValidator {
return errors;
}
+ /** The property a step written as a string sets: the argument of the
definition's String constructor. */
+ private static final Map<String, String> STRING_STEP_PROPERTY =
Map.ofEntries(
+ Map.entry("bean", "ref"), Map.entry("convertBodyTo", "type"),
Map.entry("log", "message"),
+ Map.entry("poll", "uri"), Map.entry("removeHeader", "name"),
Map.entry("removeHeaders", "pattern"),
+ Map.entry("removeProperties", "pattern"),
Map.entry("removeProperty", "name"),
+ Map.entry("removeVariable", "name"), Map.entry("rollback",
"message"),
+ Map.entry("setExchangePattern", "pattern"), Map.entry("to",
"uri"), Map.entry("toD", "uri"));
+
+ private static final String NORMALIZE_HINT = "; camel validate normalize
rewrites a file in the canonical format";
+
+ /**
+ * The canonical schema rejects the compact notation as a schema error
that says nothing about it: "property
+ * 'simple' is not defined" for a language key directly on the EIP,
"string found, object expected" for a step or a
+ * language written as a string. Each is replaced with a message that
names the notation, the canonical form of that
+ * line, and the normalize command.
+ */
+ List<Error> withCompactNotationHints(List<Error> errors) {
+ List<Error> answer = new ArrayList<>(errors.size());
+ for (Error error : errors) {
+ String hint = compactNotationHint(error);
+ if (hint == null) {
+ answer.add(error);
+ continue;
+ }
+ answer.add(Error.builder()
+ .keyword("compactNotation")
+ .instanceLocation(error.getInstanceLocation())
+ .messageKey("compactNotation")
+ .format(new MessageFormat("{0}"))
+ .arguments(hint + NORMALIZE_HINT)
+ .build());
+ }
+ return answer;
+ }
+
+ private String compactNotationHint(Error error) {
+ String message = error.getMessage();
+ if (message == null) {
+ return null;
+ }
+ String location = String.valueOf(error.getInstanceLocation());
+ String name = location.substring(location.lastIndexOf('/') + 1);
+ if ("additionalProperties".equals(error.getKeyword())) {
+ // setBody: {simple: ...} or when: [- simple: ...]: the language
key sits on the EIP, not under expression:
+ String unknown = between(message, "property '", "'");
+ if (unknown == null || !languageKeys.contains(unknown)) {
+ return null;
+ }
+ if (name.matches("\\d+")) {
+ String parent = location.substring(0,
location.lastIndexOf('/'));
+ name = parent.substring(parent.lastIndexOf('/') + 1);
+ return "a " + name + " item with " + unknown + ": ... is the
deprecated compact notation: an expression"
+ + " is written under expression: (- expression: {" +
unknown + ": {" + languageForm(unknown)
+ + "}})";
+ }
+ return name + ": {" + unknown + ": ...} is the deprecated compact
notation: an expression is written under"
+ + " expression: (" + name + ": {expression: {" + unknown +
": {" + languageForm(unknown) + "}}})";
+ }
+ if ("type".equals(error.getKeyword()) && message.contains("string
found, object expected")) {
+ if (languageKeys.contains(name)) {
+ // simple: "..." : the language is a map with its expression
+ return name + ": \"...\" is the deprecated compact notation:
write " + name + ": {" + languageForm(name)
+ + "}";
+ }
+ if (stepNames.contains(name) || topLevelEntries.contains(name)) {
+ // log: "..." : the step is a map with its properties
+ String property = STRING_STEP_PROPERTY.get(name);
+ return name + ": \"...\" is the deprecated compact notation:
write " + name
+ + (property != null ? ": {" + property + ": \"...\"}" :
" as a map with its properties");
+ }
+ }
+ return null;
+ }
+
+ /** The canonical body of a language: its expression property, or token
for tokenize, as key: "...". */
+ private String languageForm(String language) {
+ JsonNode ref =
model.at("/items/definitions/org.apache.camel.model.language.ExpressionDefinition/properties/"
+ + language + "/$ref");
+ JsonNode properties = ref.isTextual() ?
model.at(ref.asText().substring(1) + "/properties") : null;
+ String property = properties != null && properties.has("expression") ?
"expression"
+ : properties != null && properties.has("token") ? "token" :
"expression";
+ return property + ": \"...\"";
+ }
+
/**
* "must have at most 1 properties" at a step: a step holds one EIP, and
the second key is either an option that
* belongs under the EIP (indented one level more) or another step (its
own - item).
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
index 45c5c17de9e3..73dd235aea44 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
@@ -162,4 +162,74 @@ public class YamlCanonicalValidatorTest {
var report = canonicalValidator.validate(route);
assertThat(report).as("loadBalance with two load balancers should fail
canonical validation").isNotEmpty();
}
+
+ // CAMEL-24723: the compact notation is reported as such, one message per
occurrence, with the canonical form
+ @Test
+ public void testCompactNotationIsReportedWithTheCanonicalForm() throws
Exception {
+ var route = """
+ - route:
+ from:
+ uri: "direct:start"
+ steps:
+ - setBody:
+ simple: "Hello ${body}"
+ - setHeader:
+ name: foo
+ expression:
+ constant: "bar"
+ - log: "${body}"
+ - choice:
+ when:
+ - simple: "${header.foo} == 'bar'"
+ steps:
+ - to: "mock:a"
+ - split:
+ tokenize: ","
+ steps:
+ - log:
+ message: "x"
+ - onException:
+ exception:
+ - java.lang.Exception
+ handled:
+ constant: "true"
+ steps:
+ - log:
+ message: "err"
+ """;
+ var report = canonicalValidator.validate(route);
+ assertThat(report).extracting(e -> e.getInstanceLocation() + ": " +
e.getMessage()).containsExactly(
+ "/0/route/from/steps/0/setBody: setBody: {simple: ...} is the
deprecated compact notation: an expression"
+
+ " is written under expression: (setBody:
{expression: {simple: {expression: \"...\"}}});"
+
+ " camel validate normalize rewrites a file in
the canonical format",
+ "/0/route/from/steps/1/setHeader/expression/constant:
constant: \"...\" is the deprecated compact"
+
+ " notation: write constant: {expression: \"...\"};"
+
+ " camel validate normalize rewrites a file in the
canonical format",
+ "/0/route/from/steps/2/log: log: \"...\" is the deprecated
compact notation: write log: {message: \"...\"};"
+
+ " camel validate normalize rewrites a file in the canonical
format",
+ "/0/route/from/steps/3/choice/when/0/steps/0/to: to: \"...\"
is the deprecated compact notation: write"
+
+ " to: {uri: \"...\"}; camel validate normalize rewrites a file in the
canonical format",
+ "/0/route/from/steps/3/choice/when/0: a when item with simple:
... is the deprecated compact notation:"
+
+ " an expression is written under expression: (- expression:
{simple: {expressi [...]
+
+ " camel validate normalize rewrites a file in the canonical
format",
+ "/0/route/from/steps/4/split: split: {tokenize: ...} is the
deprecated compact notation: an expression"
+
+ " is wr [...]
+
+ " camel [...]
+ "/1/onException/handled/constant: constant: \"...\" is the
deprecated compact notation: write"
+
[...]
+ }
+
+ @Test
+ public void testCompactNotationIsNotReportedByTheClassicValidator() throws
Exception {
+ var route = """
+ - route:
+ from:
+ uri: "direct:start"
+ steps:
+ - setBody:
+ simple: "Hello ${body}"
+ - log: "${body}"
+ """;
+ assertThat(classicValidator.validate(route)).isEmpty();
+ }
}
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
index c403e8b2d8cf..3b172e67cd44 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
@@ -883,7 +883,9 @@ camel validate normalize --output normalized/ myroute.yaml
=== Compact notation warning
Camel logs a WARN message when YAML routes use compact (shorthand) notation
instead of
-the canonical (explicit) form. This is to encourage adopting the canonical
style which is
+the canonical (explicit) form: a step written as a string (`log: "..."`), a
language written as a
+string (`simple: "..."`), or a language key directly on the EIP (`setBody:
{simple: ...}`) instead of
+under `expression:`. This is to encourage adopting the canonical style which is
more friendly for tooling and AI assistants.
The warning is logged once per resource file and looks like:
@@ -910,7 +912,12 @@ To validate YAML routes against the canonical schema:
camel validate yaml --canonical myroute.yaml
----
-This reports any shorthands or implicit forms that are not valid in the
canonical schema.
+This reports each use of the compact notation with the canonical form to
write, for example:
+
+----
+/0/route/from/steps/0/setBody: setBody: {simple: ...} is the deprecated
compact notation: an expression is written under expression: (setBody:
{expression: {simple: {expression: "..."}}}); camel validate normalize rewrites
a file in the canonical format
+/0/route/from/steps/1/log: log: "..." is the deprecated compact notation:
write log: {message: "..."}; camel validate normalize rewrites a file in the
canonical format
+----
The `YamlValidator` class supports both schemas programmatically:
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CompactNotationWarnTest.groovy
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CompactNotationWarnTest.groovy
new file mode 100644
index 000000000000..a5a52082d4c0
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/CompactNotationWarnTest.groovy
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.yaml
+
+import org.apache.camel.dsl.yaml.common.YamlDeserializerBase
+import org.apache.camel.dsl.yaml.support.YamlTestSupport
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.LogManager
+import org.apache.logging.log4j.core.LogEvent
+import org.apache.logging.log4j.core.Logger
+import org.apache.logging.log4j.core.appender.AbstractAppender
+import org.apache.logging.log4j.core.config.Property
+
+/**
+ * CAMEL-24723: the compact notation is deprecated and the deserializers warn
once per resource when they meet it,
+ * for a language key directly on the EIP as much as for a step or a language
written as a string.
+ */
+class CompactNotationWarnTest extends YamlTestSupport {
+
+ List<String> warnings = []
+ AbstractAppender appender
+
+ def setup() {
+ appender = new AbstractAppender("compact-notation", null, null, false,
Property.EMPTY_ARRAY) {
+ @Override
+ void append(LogEvent event) {
+ if (event.level == Level.WARN) {
+ warnings << event.message.formattedMessage
+ }
+ }
+ }
+ appender.start()
+ ((Logger)
LogManager.getLogger(YamlDeserializerBase)).addAppender(appender)
+ }
+
+ def cleanup() {
+ ((Logger)
LogManager.getLogger(YamlDeserializerBase)).removeAppender(appender)
+ appender.stop()
+ }
+
+ def "a language key directly on the EIP is the compact notation"() {
+ when:
+ loadRoutes '''
+ - route:
+ from:
+ uri: "direct:start"
+ steps:
+ - setBody:
+ simple:
+ expression: "Hello ${body}"
+ - to:
+ uri: "mock:result"
+ '''
+ then:
+ warnings.size() == 1
+ warnings[0].contains('YAML DSL compact notation detected')
+ warnings[0].contains('camel validate normalize')
+ }
+
+ def "a step written as a string is the compact notation"() {
+ when:
+ loadRoutes '''
+ - route:
+ from:
+ uri: "direct:start"
+ steps:
+ - log: "${body}"
+ '''
+ then:
+ warnings.size() == 1
+ }
+
+ def "a language written as a string is the compact notation"() {
+ when:
+ loadRoutes '''
+ - route:
+ from:
+ uri: "direct:start"
+ steps:
+ - setBody:
+ expression:
+ constant: "Hello"
+ '''
+ then:
+ warnings.size() == 1
+ }
+
+ def "the canonical notation is not warned about"() {
+ when:
+ loadRoutes '''
+ - onException:
+ exception:
+ - java.lang.Exception
+ handled:
+ constant:
+ expression: "true"
+ steps:
+ - log:
+ message: "err"
+ - route:
+ from:
+ uri: "direct:start"
+ steps:
+ - setBody:
+ expression:
+ simple:
+ expression: "Hello ${body}"
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${body} == 'x'"
+ steps:
+ - log:
+ message: "x"
+ - to:
+ uri: "mock:result"
+ '''
+ then:
+ warnings.isEmpty()
+ }
+}