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]