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 3edf55b5d4a6 CAMEL-24850: the validator says what to write for 
pollEnrich/enrich with a uri, a steps item inside steps, and EIPs directly 
under step
3edf55b5d4a6 is described below

commit 3edf55b5d4a60dbe60ea1d9ba5c924354e50bd3d
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Sep 20 20:17:42 2026 +0200

    CAMEL-24850: the validator says what to write for pollEnrich/enrich with a 
uri, a steps item inside steps, and EIPs directly under step
    
    Three shapes from the round-2 benchmark that the schema validator rejected
    with a message that did not say what to write, so a model or a person needed
    another round or two to get past them. Three rows in SchemaHints.PROPERTY,
    before the generic did-you-mean row:
    
    - pollEnrich: {uri: ...} / enrich: {resourceUri: ...}: the endpoint is an
      expression, and the message says so with the value echoed.
    - - steps: as a grouping item inside a steps list, or steps: on an EIP
      without a pipeline: there is no group inside a list, move the items up or
      use step: {id: ..., steps: [...]}; and no "did you mean 'step'?", which 
led
      the model to the next shape.
    - EIP names directly under step: step is the Step EIP, its EIPs go in its
      steps: list; one line for the whole item instead of one per EIP.
    
    Closes #26633
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01Bp3538HRBPMQkb5ta9xRaj
---
 .../camel/dsl/yaml/validator/SchemaHints.java      | 22 ++++++
 .../validator/YamlValidatorPropertyHintTest.java   | 89 ++++++++++++++++++++++
 2 files changed, 111 insertions(+)

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 c5db70953cd3..1576498172db 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
@@ -344,6 +344,28 @@ final class SchemaHints {
             unknownProperty(".*/bean", m -> Set.of("parameters", "args", 
"arguments").contains(m.unknown()),
                     m -> "arguments are written in the method call: bean: 
{ref: myBean, method: \"process(${body},"
                          + " 'x')\"}"),
+            // pollEnrich: {uri: ...}: the endpoint of enrich and pollEnrich 
is an expression (CAMEL-24850)
+            unknownProperty(".*/(enrich|pollEnrich)",
+                    m -> m.unknown().equals("uri") || 
m.unknown().equals("resourceUri"),
+                    m -> {
+                        JsonNode instance = m.error().getInstanceNode();
+                        JsonNode value = instance != null ? 
instance.get(m.unknown()) : null;
+                        String uri = value != null && value.isValueNode() ? 
value.asText() : "file:...";
+                        return "the endpoint of " + m.name() + " is an 
expression: write " + m.name()
+                               + ": {expression: {constant: {expression: \"" + 
uri + "\"}}}";
+                    }),
+            // - steps: [...] as an item of a steps list, or steps: on an EIP 
without a pipeline: there is no group item
+            // (and no "did you mean 'step'?", which leads to the Step EIP 
with the EIPs as its keys)
+            unknownProperty(null, m -> m.unknown().equals("steps"),
+                    m -> "steps: is the list of a route or of an EIP that owns 
a pipeline (filter, split, choice, step);"
+                         + " an EIP is an item of that list, not a group 
inside it: move the items up one level, or use"
+                         + " step: {id: ..., steps: [...]} for a named group"),
+            // step: {setHeader: ..., split: ...}: step is the Step EIP; one 
message for the whole item, not one per EIP
+            replace("additionalProperties", ".*/step",
+                    m -> m.unknown() != null && 
m.validator().stepNames().contains(m.unknown()),
+                    m -> "step is the Step EIP, a named group: its EIPs go in 
its steps: list (step: {id: ..., steps: [-"
+                         + " setHeader: ...]})",
+                    "additionalProperties", "additionalProperties"),
             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/YamlValidatorPropertyHintTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
index e2cf1c3e461f..e62750b894db 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
@@ -38,6 +38,95 @@ public class YamlValidatorPropertyHintTest {
         validator.init();
     }
 
+    /** CAMEL-24850: the three shapes get the form to write, in both schema 
modes. */
+    private static List<YamlValidator> bothModes() throws Exception {
+        YamlValidator canonical = new YamlValidator(true);
+        canonical.init();
+        return List.of(validator, canonical);
+    }
+
+    @Test
+    public void testPollEnrichWithAUriSaysItIsAnExpression() throws Exception {
+        for (YamlValidator v : bothModes()) {
+            List<Error> errors = v.validate("""
+                    - route:
+                        from:
+                          uri: timer:tick
+                          steps:
+                            - pollEnrich:
+                                uri: file:./order.json
+                            - enrich:
+                                resourceUri: direct:prices
+                    """);
+            assertThat(errors).hasSize(2);
+            assertThat(errors.get(0).getMessage())
+                    .contains("the endpoint of pollEnrich is an expression: 
write pollEnrich: {expression: {constant:"
+                              + " {expression: \"file:./order.json\"}}}");
+            assertThat(errors.get(1).getMessage())
+                    .contains("the endpoint of enrich is an expression: write 
enrich: {expression: {constant:"
+                              + " {expression: \"direct:prices\"}}}");
+        }
+    }
+
+    @Test
+    public void testStepsAsAGroupItemSaysThereIsNoGroup() throws Exception {
+        for (YamlValidator v : bothModes()) {
+            List<Error> errors = v.validate("""
+                    - route:
+                        from:
+                          uri: timer:tick
+                          steps:
+                            - steps:
+                                - setHeader:
+                                    name: a
+                                    expression:
+                                      constant:
+                                        expression: "1"
+                            - to:
+                                uri: mock:a
+                                steps:
+                                  - log:
+                                      message: hi
+                    """);
+            assertThat(errors).hasSize(2);
+            for (Error e : errors) {
+                assertThat(e.getMessage())
+                        .contains("steps: is the list of a route or of an EIP 
that owns a pipeline")
+                        .contains("step: {id: ..., steps: [...]} for a named 
group")
+                        .doesNotContain("did you mean");
+            }
+        }
+    }
+
+    @Test
+    public void testEipsDirectlyUnderStepGetOneMessageWithTheShape() throws 
Exception {
+        for (YamlValidator v : bothModes()) {
+            List<Error> errors = v.validate("""
+                    - route:
+                        from:
+                          uri: timer:tick
+                          steps:
+                            - step:
+                                setHeader:
+                                  name: a
+                                  expression:
+                                    constant:
+                                      expression: "1"
+                                split:
+                                  expression:
+                                    simple:
+                                      expression: "${body}"
+                                  steps:
+                                    - log:
+                                        message: hi
+                    """);
+            assertThat(errors).hasSize(1);
+            assertThat(errors.get(0).getMessage())
+                    .isEqualTo("step is the Step EIP, a named group: its EIPs 
go in its steps: list"
+                               + " (step: {id: ..., steps: [- setHeader: 
...]})");
+        }
+    }
+
     @Test
     public void testMisspelledOptionGetsTheClosestName() throws Exception {
         List<Error> errors = validator.validate("""

Reply via email to