davsclaus commented on code in PR #27815:
URL: https://github.com/apache/camel/pull/27815#discussion_r4269065998


##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java:
##########
@@ -1829,7 +1842,43 @@ public static JsonObject optionToJson(BaseOptionModel 
opt, String scope) {
             o.put("oneOf", oneOf);
             o.put("yaml", "write one of oneOf as the key, not '" + 
opt.getName() + "': for example "
                           + opt.getOneOfs().get(0) + ": {...}");
+        } else {
+            String shape = yamlShape(opt);
+            if (shape != null) {
+                o.put("yaml", shape);
+            }
         }
         return o;
     }
+
+    /**
+     * How an option whose type says only object or array is written in YAML 
(CAMEL-25539): an expression has its
+     * language as the key (handled: true fails the schema), and a list of 
properties is a list of key/value pairs
+     * (namespace of xpath). The example comes first and is valid YAML, the 
explanation follows as a comment. Null for
+     * any other option.
+     */
+    static String yamlShape(BaseOptionModel opt) {
+        String name = opt.getName();
+        if ("expression".equals(opt.getKind()) && 
EXPRESSION_SUB_ELEMENT.equals(opt.getJavaType())) {
+            if (opt.isAsPredicate()) {
+                return name + ": {constant: \"true\"}  # a predicate with its 
language as the key, not a plain"

Review Comment:
   This is the classic shorthand. The canonical schema rejects `constant: 
"true"`: `ConstantExpression` is an object with `expression:`. Suggest `name + 
": {constant: {expression: \"true\"}}"`, and the same pattern for the `header` 
and `simple` hints below. That is also what CAMEL-25539 asks for.



##########
dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java:
##########
@@ -589,4 +589,98 @@ void yamlKeyFallsBackToTheCatalogName() {
         EipModel element = new 
DefaultCamelCatalog().eipModel("templateParameter");
         assertEquals("templateParameter", CatalogDocs.yamlKey(null, 
"routeTemplate", "templateParameter", element));
     }
+
+    /** CAMEL-25539: onException says it is a top-level entry, and handled 
that it is an expression. */
+    @Test
+    void onExceptionSaysWhereItGoesAndTheShapeOfHandled() {
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        var doc = CatalogDocs.catalogDoc(catalog, "onException", null, "eip", 
null, "all", false, false, null);
+        assertTrue(doc.getString("placement").startsWith("top-level entry"), 
doc.toJson());
+        assertTrue(doc.getString("sampleHint").contains("camel_catalog_sample 
name=onException"), doc.toJson());
+        assertEquals("handled: {constant: \"true\"}", yamlExample(option(doc, 
"handled")));
+        assertEquals("continued: {constant: \"true\"}", 
yamlExample(option(doc, "continued")));
+        assertTrue(yamlExample(option(doc, "onWhen")).startsWith("onWhen: 
{simple: "), doc.toJson());
+        // the elements written in their own way keep no hint
+        assertFalse(option(doc, "redeliveryPolicy").containsKey("yaml"), 
doc.toJson());
+        assertFalse(option(doc, "exception").containsKey("yaml"), 
doc.toJson());
+    }
+
+    /** CAMEL-25539: each kind of EIP says where it goes, and a step has no 
shape hint it does not need. */
+    @Test
+    void placementByKindOfEip() {
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        var doc = CatalogDocs.catalogDoc(catalog, "errorHandler", null, "eip", 
null, null, false, false, null);
+        assertTrue(doc.getString("placement").startsWith("errorHandler: with 
the kind"), doc.toJson());
+        doc = CatalogDocs.catalogDoc(catalog, "routeConfiguration", null, 
"eip", null, null, false, false, null);
+        assertTrue(doc.getString("placement").startsWith("top-level entry"), 
doc.toJson());
+        doc = CatalogDocs.catalogDoc(catalog, "when", null, "eip", null, null, 
false, false, null);
+        assertTrue(doc.getString("placement").startsWith("a part of choice"), 
doc.toJson());
+        doc = CatalogDocs.catalogDoc(catalog, "split", null, "eip", null, 
"all", false, false, null);
+        assertTrue(doc.getString("placement").startsWith("a step inside the 
steps of a route"), doc.toJson());
+        for (Object o : (JsonArray) doc.get("options")) {
+            assertFalse(((JsonObject) o).containsKey("yaml"), ((JsonObject) 
o).toJson());
+        }
+    }
+
+    /** CAMEL-25539: the shape hints show the expected YAML, which passes the 
YAML DSL schema and the catalog checks. */
+    @Test
+    void theShapeHintsAreValidYaml() {
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        var onException = CatalogDocs.catalogDoc(catalog, "onException", null, 
"eip", null, "all", false, false, null);
+        var aggregate = CatalogDocs.catalogDoc(catalog, "aggregate", null, 
"eip", null, "all", false, false, null);
+        var xpath = CatalogDocs.catalogDoc(catalog, "xpath", null, "language", 
null, null, false, false, null);
+        // the namespace of xpath, xquery and xtokenize is a list of key/value 
pairs
+        assertEquals("namespace: [{key: c, value: 
\"http://acme.com/cheese\"}]";, yamlExample(option(xpath, "namespace")));
+        for (String language : List.of("xquery", "xtokenize")) {
+            var doc = CatalogDocs.catalogDoc(catalog, language, null, 
"language", null, "all", false, false, null);
+            assertTrue(yamlExample(option(doc, 
"namespace")).startsWith("namespace: [{key: c, "), doc.toJson());
+        }
+        String yaml = """
+                - onException:
+                    exception: [java.io.IOException]
+                    %s
+                    %s
+                    %s
+                    steps:
+                      - log: failed
+                - onException:
+                    exception: [java.net.ConnectException]
+                    %s
+                - route:
+                    from:
+                      uri: direct:start
+                      steps:
+                        - setHeader:
+                            name: price
+                            expression:
+                              xpath:
+                                expression: /c:order/c:price/text()
+                                %s
+                        - aggregate:
+                            aggregationStrategy: 
"#class:org.apache.camel.processor.aggregate.GroupedBodyAggregationStrategy"
+                            %s
+                            %s
+                            steps:
+                              - log: done
+                """.formatted(
+                yamlExample(option(onException, "handled")), 
yamlExample(option(onException, "onWhen")),
+                yamlExample(option(onException, "retryWhile")), 
yamlExample(option(onException, "continued")),
+                yamlExample(option(xpath, "namespace")), 
yamlExample(option(aggregate, "correlationExpression")),
+                yamlExample(option(aggregate, "completionPredicate")));
+        assertEquals(List.of(), SourceValidator.validateCamelYaml(yaml, 
catalog), yaml);

Review Comment:
   `validateCamelYaml(yaml, catalog)` uses the classic schema, which accepts 
both the shorthand and the canonical form. Validate the hint lines with `new 
YamlValidator(true)` as well, so a hint that slides back to the shorthand fails 
here.



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java:
##########
@@ -1702,6 +1708,13 @@ private static JsonObject eipDoc(EipModel model, String 
filter, OptionScope scop
         result.put("input", model.isInput());
         result.put("output", model.isOutput());
         addCommonModelFields(result, model);
+        // where it goes: onException written as a step inside a route fails 
the schema (CAMEL-25539)
+        String placement = CatalogSamples.placement(model);
+        if (placement != null) {
+            result.put("placement", placement);
+        }
+        result.put("sampleHint", "camel_catalog_sample name=" + model.getName()

Review Comment:
   Could this be added only when `camel_catalog_sample` actually has a sample 
for the EIP? Otherwise an agent spends a call that answers "no sample".



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java:
##########
@@ -1829,7 +1842,43 @@ public static JsonObject optionToJson(BaseOptionModel 
opt, String scope) {
             o.put("oneOf", oneOf);
             o.put("yaml", "write one of oneOf as the key, not '" + 
opt.getName() + "': for example "
                           + opt.getOneOfs().get(0) + ": {...}");
+        } else {
+            String shape = yamlShape(opt);
+            if (shape != null) {
+                o.put("yaml", shape);
+            }
         }
         return o;
     }
+
+    /**
+     * How an option whose type says only object or array is written in YAML 
(CAMEL-25539): an expression has its
+     * language as the key (handled: true fails the schema), and a list of 
properties is a list of key/value pairs
+     * (namespace of xpath). The example comes first and is valid YAML, the 
explanation follows as a comment. Null for
+     * any other option.
+     */
+    static String yamlShape(BaseOptionModel opt) {
+        String name = opt.getName();
+        if ("expression".equals(opt.getKind()) && 
EXPRESSION_SUB_ELEMENT.equals(opt.getJavaType())) {
+            if (opt.isAsPredicate()) {
+                return name + ": {constant: \"true\"}  # a predicate with its 
language as the key, not a plain"
+                       + " true/false; simple: instead of constant: for a 
condition";
+            }
+            return name + ": {header: myKey}  # an expression with its 
language as the key (header, simple, jq, ...)";
+        }
+        if ("element".equals(opt.getKind()) && "object".equals(opt.getType()) 
&& opt.isAsPredicate()
+                && (opt.getOneOfs() == null || opt.getOneOfs().isEmpty())) {
+            return name + ": {simple: \"${header.foo} == 'bar'\"}  # a 
predicate with its language as the key"

Review Comment:
   `OnWhenDefinition` in the canonical schema only has `expression:` (plus 
id/description/note), so the canonical form is `onWhen: {expression: {simple: 
{expression: "${header.foo} == 'bar'"}}}`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to