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 c0f92eb7910f CAMEL-25719: camel-jbang - camel_catalog_doc and
camel_catalog_find know the beans entry of the YAML DSL, and five more entries
have samples (#28002)
c0f92eb7910f is described below
commit c0f92eb7910ff14f904ea872950f6c5fbba473bf
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Oct 17 20:29:44 2026 +0200
CAMEL-25719: camel-jbang - camel_catalog_doc and camel_catalog_find know
the beans entry of the YAML DSL, and five more entries have samples (#28002)
A model that had to declare a Java class as a bean looked up beans four
times in one step and was told "EIP not
found", while camel_catalog_sample had the example. The items of beans are
bean factories: camel_catalog_doc now
answers for beans with the options of the beanFactory model, where the
entry goes, how a route uses a bean, and an
example; camel_catalog_find lists it for beans and for bean.
templatedRoute, restConfiguration, transformers, validators and
sslContextParameters had no sample, as their
examples are on the page of what they belong to: the sample generator reads
those pages for them now.
Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
dsl/camel-jbang/camel-jbang-core/pom.xml | 6 ++
.../dsl/jbang/core/commands/ai/eip-samples.json | 78 ++++++++++++++++++++++
.../dsl/jbang/core/commands/ai/CatalogDocs.java | 48 +++++++++++++
.../jbang/core/commands/ai/CatalogDocsTest.java | 26 ++++++++
.../jbang/core/commands/ai/CatalogSamplesTest.java | 14 ++++
5 files changed, 172 insertions(+)
diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml
b/dsl/camel-jbang/camel-jbang-core/pom.xml
index dc0753e88fa5..e8a13ffd7a55 100644
--- a/dsl/camel-jbang/camel-jbang-core/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-core/pom.xml
@@ -298,6 +298,12 @@
<entryPage>../../camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc=beans</entryPage>
<!-- the contract-first rest, next to the
path-based examples of rest-dsl -->
<entryPage>../../../docs/user-manual/modules/ROOT/pages/rest-dsl-openapi.adoc=rest</entryPage>
+ <!-- the top-level entries whose examples are
on a page about something else (CAMEL-25719) -->
+
<entryPage>../../../docs/user-manual/modules/ROOT/pages/route-template.adoc=templatedRoute</entryPage>
+
<entryPage>../../../docs/user-manual/modules/ROOT/pages/rest-dsl.adoc=restConfiguration</entryPage>
+
<entryPage>../../../docs/user-manual/modules/ROOT/pages/transformer.adoc=transformers</entryPage>
+
<entryPage>../../../docs/user-manual/modules/ROOT/pages/validator.adoc=validators</entryPage>
+
<entryPage>../../../docs/user-manual/modules/ROOT/pages/camel-configuration-utilities.adoc=sslContextParameters</entryPage>
</entryPages>
<!-- user manual pages that show old syntax or
YAML that is not a Camel file -->
<excludes>
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
index 8275e7969d73..5548570fb542 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
+++
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/org/apache/camel/dsl/jbang/core/commands/ai/eip-samples.json
@@ -1033,6 +1033,16 @@
"yaml": "- rest:\n get:\n - path: \"\/customers\/{id}\"\n
to: \"direct:customerDetails\"\n - path: \"\/customers\/{id}\/orders\"\n
to: \"direct:customerOrders\"\n disabled: \"{{ordersEnabled}}\"\n
post:\n - path: \"\/customers\/neworder\"\n to:
\"direct:customerNewOrder\"\n disabled: \"true\"\n"
}
],
+ "restConfiguration": [
+ {
+ "source": "rest-dsl.adoc",
+ "yaml": "- restConfiguration:\n inlineRoutes: true\n- rest:\n
get:\n - path: \"\/customers\/{id}\"\n to:
\"direct:customerDetails\"\n - path: \"\/customers\/{id}\/orders\"\n
to: \"direct:customerOrders\"\n post:\n - path:
\"\/customers\/neworder\"\n to: \"direct:customerNewOrder\"\n"
+ },
+ {
+ "source": "rest-dsl.adoc",
+ "yaml": "- restConfiguration:\n inlineRoutes: true\n- rest:\n
path: \"\/customers\/\"\n get:\n - path: \"\/{id}\"\n to:
\"direct:customerDetails\"\n - path: \"\/{id}\/orders\"\n to:
\"direct:customerOrders\"\n post:\n - path: \"\/neworder\"\n to:
\"direct:customerNewOrder\"\n- route:\n from:\n uri:
direct:customerNewOrder\n steps:\n - to:\n uri:
direct:commonCustomerNewOrder\n- route:\n from:\n u [...]
+ }
+ ],
"returnAddress": [
{
"source": "return-address.adoc",
@@ -1419,6 +1429,12 @@
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n
- to:\n uri: mock:a\n - split:\n
shareUnitOfWork: \"true\"\n expression:\n tokenize:\n
token: \",\"\n steps:\n - to:\n
uri: mock:b\n - to:\n uri: direct:line\n
- to:\n uri: mock:result\n\n- route:\n from:\n uri:
direct:line\n steps:\n [...]
}
],
+ "sslContextParameters": [
+ {
+ "source": "camel-configuration-utilities.adoc",
+ "yaml": "- sslContextParameters:\n id: mySSL\n keyStore:
server.p12\n keystorePassword: changeit\n trustStore: truststore.p12\n
trustStorePassword: changeit\n- route:\n from:\n uri: direct:ssl\n
steps:\n - to:\n uri: mock:ssl\n"
+ }
+ ],
"step": [
{
"source": "step-eip.adoc",
@@ -1451,6 +1467,24 @@
"yaml": "- semantic:\n evaluation:\n department:\n
expert: decisions\n type: choice\n instructions: Which department
should handle this message?\n criteria:\n billing: Invoices,
payments and refunds\n technical: Bugs, outages and technical
problems\n other: Any other request\n- route:\n from:\n uri:
direct:classify\n steps:\n - switch:\n selector:\n
language:\n [...]
}
],
+ "templatedRoute": [
+ {
+ "source": "route-template.adoc",
+ "yaml": "- templatedRoute:\n routeTemplateRef: \"myTemplate\"\n
parameters:\n - name: \"name\"\n value: \"one\"\n - name:
\"greeting\"\n value: \"Hello\"\n\n- templatedRoute:\n
routeTemplateRef: \"myTemplate\"\n parameters:\n - name: \"name\"\n
value: \"two\"\n - name: \"greeting\"\n value: \"Bonjour\"\n
- name: \"myPeriod\"\n value: \"5s\"\n"
+ },
+ {
+ "source": "route-template.adoc",
+ "yaml": "- templatedRoute:\n routeTemplateRef: \"myTemplate\"\n
routeId: \"myCoolRoute\"\n parameters:\n - name: \"name\"\n
value: \"one\"\n - name: \"greeting\"\n value: \"hello\"\n -
name: \"myPeriod\"\n value: \"5s\"\n"
+ },
+ {
+ "source": "route-template.adoc",
+ "yaml": "- templatedRoute:\n routeTemplateRef: \"orderTemplate\"\n
routeId: \"webOrder\"\n prefixId: \"web\"\n parameters:\n - name:
\"queue\"\n value: \"web\"\n"
+ },
+ {
+ "source": "route-template.adoc",
+ "yaml": "- templatedRoute:\n routeTemplateRef: \"orderTemplate\"\n
routeId: \"ftpOrder\"\n prefixId: \"ftp\"\n parameters:\n - name:
\"queue\"\n value: \"order.ftp\"\n"
+ }
+ ],
"threads": [
{
"source": "threads-eip.adoc",
@@ -1585,6 +1619,32 @@
"yaml": "- route:\n from:\n uri: direct:cheese\n steps:\n
- transformDataType:\n toType: myDataType\n - to:\n
uri: log:hello\n"
}
],
+ "transformers": [
+ {
+ "source": "transformer.adoc",
+ "yaml": "- transformers:\n dataFormatTransformer:\n fromType:
java:com.example.Order\n toType: \"csv:CSVOrder\"\n bindy:\n
type: \"Csv\"\n classType: \"com.example.Order\"\n"
+ },
+ {
+ "source": "transformer.adoc",
+ "yaml": "- transformers:\n endpointTransformer:\n uri:
component:componentPathOptions?mappingFile=myMapping.xml\n fromType: xml\n
toType: json\n"
+ },
+ {
+ "source": "transformer.adoc",
+ "yaml": "- transformers:\n customTransformer:\n className:
com.example.MyCustomTransformer\n fromType: xml\n toType: json\n"
+ },
+ {
+ "source": "transformer.adoc",
+ "yaml": "- transformers:\n loadTransformer:\n defaults: true\n"
+ },
+ {
+ "source": "transformer.adoc",
+ "yaml": "- transformers:\n loadTransformer:\n packageScan:
\"org.apache.camel.transformer.standard\"\n"
+ },
+ {
+ "source": "transformer.adoc",
+ "yaml": "- transformers:\n endpointTransformer:\n uri:
xslt:transform.xsl\n fromType: xml:ABCOrder\n toType: xml:XYZOrder\n"
+ }
+ ],
"unmarshal": [
{
"source": "unmarshal-eip.adoc",
@@ -1605,6 +1665,24 @@
"yaml": "- route:\n from:\n uri: file:inbox\n steps:\n
- validate:\n expression:\n simple:\n
expression: \"${header.bar} > 100\"\n - to:\n uri:
bean:myServiceBean.processLine\n"
}
],
+ "validators": [
+ {
+ "source": "validator.adoc",
+ "yaml": "- validators:\n predicateValidator:\n type:
csv:CSVOrder\n expression:\n simple:\n expression:
\"${body} contains '{name:XOrder}'\"\n"
+ },
+ {
+ "source": "validator.adoc",
+ "yaml": "- validators:\n endpointValidator:\n type: xml\n
uri: validator:xsd\/schema.xsd\n"
+ },
+ {
+ "source": "validator.adoc",
+ "yaml": "- validators:\n customValidator:\n type: json\n
className: com.example.MyCustomValidator\n"
+ },
+ {
+ "source": "validator.adoc",
+ "yaml": "- validators:\n endpointValidator:\n type:
xml:ABCOrder\n uri: \"validator:xsd\/schema.xsd\"\n"
+ }
+ ],
"weightedLoadBalancer": [
{
"source": "weightedLoadBalancer-eip.adoc",
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java
index 2561cabac4fb..a8584ad191b4 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocs.java
@@ -383,6 +383,12 @@ public final class CatalogDocs {
if (em != null) {
return eipDoc(catalog, em, lowerFilter, scope, includeDoc,
null);
}
+ if (BEANS_ENTRY.equalsIgnoreCase(name)) {
+ JsonObject beans = beansDoc(catalog, lowerFilter, scope,
includeDoc);
+ if (beans != null) {
+ return beans;
+ }
+ }
if (kind != null) {
// an alias (fan-out, dedup, rate-limit) or the whole title
names the EIP as well
JsonObject byTerm = eipByTerm(catalog, name, lowerFilter,
scope, includeDoc);
@@ -624,6 +630,42 @@ public final class CatalogDocs {
return result;
}
+ /** The top-level entry of the YAML DSL that declares beans: a list of
bean factories, with no model of its own. */
+ static final String BEANS_ENTRY = "beans";
+
+ private static final String BEANS_DESCRIPTION
+ = "The top-level entry of the YAML DSL that declares beans: a list
of beans, each with a name and how to create"
+ + " it (type, properties, constructors, a factory method or a
script)";
+
+ /**
+ * The beans entry of the YAML DSL (CAMEL-25719). Each item of its list is
a bean factory, so the options are those
+ * of the beanFactory model, and the answer says where the entry goes and
how a route uses a bean, with an example.
+ * A model that looked up beans before was told "EIP not found" four times
in one step, while camel_catalog_sample
+ * had the example all along. Null when the catalog has no beanFactory
model.
+ */
+ private static JsonObject beansDoc(CamelCatalog catalog, String filter,
OptionScope scope, boolean includeDoc) {
+ EipModel factory = catalog.eipModel("beanFactory");
+ if (factory == null) {
+ return null;
+ }
+ JsonObject result = eipDoc(catalog, factory, filter, scope,
includeDoc, null);
+ result.put("name", BEANS_ENTRY);
+ result.put("title", "Beans");
+ result.put("description", BEANS_DESCRIPTION + ". The options are those
of one bean; name is required. A route uses"
+ + " a bean as #bean:name in an option,
${bean:name.method} in Simple, or with the bean EIP"
+ + " (ref: name). The entry goes in the file
of the routes, or in a file of its own such as"
+ + " beans.yaml next to them.");
+ result.put("placement", CatalogSamples.placement(BEANS_ENTRY));
+ // the example itself, short as it is, rather than a hint to fetch it
with another call
+ JsonArray samples = (JsonArray) CatalogSamples.sample(catalog,
BEANS_ENTRY, 1).get("samples");
+ // the hint of eipDoc names beanFactory, which is not the name to ask
for
+ result.remove("sampleHint");
+ if (samples != null && !samples.isEmpty()) {
+ result.put("sample", ((JsonObject)
samples.get(0)).getString("yaml"));
+ }
+ return result;
+ }
+
/**
* The options of the elements of an EIP that match the filter, when none
of its own do: logStackTrace is an option
* of the redeliveryPolicy of onException, and a filter on onException
found nothing (CAMEL-25370).
@@ -940,6 +982,12 @@ public final class CatalogDocs {
matches.add(o);
}
}
+ // the beans entry of the YAML DSL has no model to suggest: a
model that wants to declare a bean asks for
+ // beans, or for bean (CAMEL-25719)
+ String key = normalizeName(term);
+ if (BEANS_ENTRY.equals(key) || "bean".equals(key)) {
+ matches.add(summary("eip", BEANS_ENTRY, "Beans",
BEANS_DESCRIPTION, "configuration"));
+ }
}
if (kind == null || "bean".equals(kind)) {
// the built-in beans: an interface name (AggregationStrategy)
lists its implementations
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
index 7834f9ca0935..bc790f834679 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogDocsTest.java
@@ -277,6 +277,32 @@ class CatalogDocsTest {
.anyMatch(b -> b.startsWith("GroupedBodyAggregationStrategy
(")));
}
+ @Test
+ void theBeansEntryOfTheYamlDslIsKnown() throws Exception {
+ // CAMEL-25719: a model that had to declare a bean looked up beans
four times in one step and was told "EIP not
+ // found", while camel_catalog_sample had the example; the items of
beans are bean factories
+ for (Map<String, Object> args : List.<Map<String, Object>>
of(Map.of("name", "beans"),
+ Map.of("name", "beans", "kind", "eip"))) {
+ JsonObject doc = catalogDoc(args);
+ assertEquals("beans", doc.getString("name"), String.valueOf(doc));
+ assertTrue(doc.getString("placement").startsWith("top-level
entry"), doc.getString("placement"));
+
assertTrue(doc.getString("description").contains("${bean:name.method}"),
doc.getString("description"));
+
assertTrue(String.valueOf(doc.get("options")).contains("properties"),
String.valueOf(doc.get("options")));
+ assertTrue(doc.getString("sample").contains("- beans:"),
doc.getString("sample"));
+ assertNull(doc.get("sampleHint"), "the example is in the answer");
+ }
+
+ CamelCatalog catalog = new DefaultCamelCatalog();
+ for (String term : List.of("beans", "bean")) {
+ JsonArray matches = (JsonArray) CatalogDocs.find(catalog, term,
null, 10).get("matches");
+ assertTrue(matches.stream().anyMatch(m ->
"beans".equals(((JsonObject) m).getString("name"))),
+ term + ": " + matches);
+ }
+ // the bean EIP comes first for bean, which is also the name of an EIP
+ JsonArray eips = (JsonArray) CatalogDocs.find(catalog, "bean", "eip",
10).get("matches");
+ assertEquals("bean", ((JsonObject) eips.get(0)).getString("name"));
+ }
+
@Test
void anEipAliasFindsTheEip() throws Exception {
// the aliases of the EIP models (fan-out, dedup, rate-limit) are what
a model asks with; the former
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
index 6436fcaf36f8..4c227b3beed8 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/CatalogSamplesTest.java
@@ -40,6 +40,20 @@ public class CatalogSamplesTest {
assertThat(((JsonObject)
samples.get(0)).getString("yaml")).startsWith("- onException:");
}
+ @Test
+ void theTopLevelEntriesDocumentedOnAnotherPageHaveSamples() {
+ // CAMEL-25719: their examples are on the page of what they belong to
(route templates, the REST DSL,
+ // transformers, validators, the configuration utilities), which the
samples did not read
+ for (String entry : List.of("templatedRoute", "restConfiguration",
"transformers", "validators",
+ "sslContextParameters")) {
+ JsonObject o = CatalogSamples.sample(entry, 1);
+
assertThat(o.getString("placement")).as(entry).contains("top-level");
+ JsonArray samples = (JsonArray) o.get("samples");
+ assertThat(samples).as(entry).isNotEmpty();
+ assertThat(((JsonObject)
samples.get(0)).getString("yaml")).as(entry).startsWith("- " + entry + ":");
+ }
+ }
+
@Test
void kebabCaseAndPartsResolveToTheEip() {
JsonObject o = CatalogSamples.sample("circuit-breaker", 1);