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 e9d9a5df99a7 chore: camel_catalog_sample shows the contract-first rest
next to the path-based one (#27233)
e9d9a5df99a7 is described below
commit e9d9a5df99a7dc8cb381065a9ee75589542cce93
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 1 22:38:22 2026 +0200
chore: camel_catalog_sample shows the contract-first rest next to the
path-based one (#27233)
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
.../camel/catalog/docs/rest-openapi-component.adoc | 50 ++++++++++++++++++++++
.../src/main/docs/rest-openapi-component.adoc | 50 ++++++++++++++++++++++
dsl/camel-jbang/camel-jbang-core/pom.xml | 2 +
.../dsl/jbang/core/commands/ai/eip-samples.json | 24 +++++++++--
.../jbang/core/commands/ai/CatalogSamplesTest.java | 12 ++++++
.../dsl/yaml/validator/GenerateDocSamplesMojo.java | 28 +++++++++++-
.../yaml/validator/GenerateDocSamplesMojoTest.java | 22 ++++++++++
7 files changed, 182 insertions(+), 6 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/rest-openapi-component.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/rest-openapi-component.adoc
index 19e3c4824ff6..1e845c121933 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/rest-openapi-component.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/rest-openapi-component.adoc
@@ -107,6 +107,56 @@ include::partial$component-endpoint-headers.adoc[]
== Usage
+=== Contract-first REST DSL
+
+To serve the operations of an OpenAPI contract, use the Rest DSL in
_contract-first_ mode rather than a
+`rest-openapi` consumer endpoint. Camel loads the contract at startup and
routes every operation to the
+route that starts from `direct:` and its _operation id_; see
+xref:manual::rest-dsl-openapi.adoc[REST DSL with contract-first OpenAPI].
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+rest().openApi("petstore-v3.json");
+
+from("direct:getPetById")
+ .setBody(simple("{\"id\": ${header.petId}, \"name\": \"Rex\"}"));
+----
+
+XML::
++
+[source,xml]
+----
+<rest>
+ <openApi specification="petstore-v3.json"/>
+</rest>
+<route>
+ <from uri="direct:getPetById"/>
+ <setBody>
+ <simple>{"id": ${header.petId}, "name": "Rex"}</simple>
+ </setBody>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- rest:
+ openApi:
+ specification: petstore-v3.json
+- route:
+ from:
+ uri: direct:getPetById
+ steps:
+ - setBody:
+ simple: '{"id": ${header.petId}, "name": "Rex"}'
+----
+====
+
=== Consumer OAuth Bearer token validation
When `rest-openapi` is used as a consumer, HTTP requests are served by the
selected
diff --git
a/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
b/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
index 19e3c4824ff6..1e845c121933 100644
--- a/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
+++ b/components/camel-rest-openapi/src/main/docs/rest-openapi-component.adoc
@@ -107,6 +107,56 @@ include::partial$component-endpoint-headers.adoc[]
== Usage
+=== Contract-first REST DSL
+
+To serve the operations of an OpenAPI contract, use the Rest DSL in
_contract-first_ mode rather than a
+`rest-openapi` consumer endpoint. Camel loads the contract at startup and
routes every operation to the
+route that starts from `direct:` and its _operation id_; see
+xref:manual::rest-dsl-openapi.adoc[REST DSL with contract-first OpenAPI].
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+rest().openApi("petstore-v3.json");
+
+from("direct:getPetById")
+ .setBody(simple("{\"id\": ${header.petId}, \"name\": \"Rex\"}"));
+----
+
+XML::
++
+[source,xml]
+----
+<rest>
+ <openApi specification="petstore-v3.json"/>
+</rest>
+<route>
+ <from uri="direct:getPetById"/>
+ <setBody>
+ <simple>{"id": ${header.petId}, "name": "Rex"}</simple>
+ </setBody>
+</route>
+----
+
+YAML::
++
+[source,yaml]
+----
+- rest:
+ openApi:
+ specification: petstore-v3.json
+- route:
+ from:
+ uri: direct:getPetById
+ steps:
+ - setBody:
+ simple: '{"id": ${header.petId}, "name": "Rex"}'
+----
+====
+
=== Consumer OAuth Bearer token validation
When `rest-openapi` is used as a consumer, HTTP requests are served by the
selected
diff --git a/dsl/camel-jbang/camel-jbang-core/pom.xml
b/dsl/camel-jbang/camel-jbang-core/pom.xml
index 29fc9e7c400f..6e0765ef7b1a 100644
--- a/dsl/camel-jbang/camel-jbang-core/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-core/pom.xml
@@ -291,6 +291,8 @@
</pages>
<entryPages>
<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>
</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 71af218d0aae..6c0cb0b6dd20 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
@@ -292,14 +292,14 @@
"source": "doTry-eip.adoc",
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n
- doTry:\n steps:\n - process:\n
ref: processorFail\n - to:\n uri: mock:result\n
- doCatch:\n exception:\n -
java.io.IOException\n - java.lang.IllegalStateException\n
steps:\n - to:\n uri:
mock:catch\n [...]
},
- {
- "source": "doTry-eip.adoc",
- "yaml": "- route:\n from:\n uri: direct:start\n steps:\n
- doTry:\n steps:\n - process:\n
ref: processorFail\n - to:\n uri: mock:result\n
- doCatch:\n exception:\n -
java.io.IOException\n - java.lang.IllegalStateException\n
onWhen:\n expression:\n
simple:\n [...]
- },
{
"source": "try-catch-finally.adoc",
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n
- doTry:\n steps:\n - process:\n
ref: processorFail\n - to:\n uri: mock:result\n
- doCatch:\n exception:\n -
java.io.IOException\n - java.lang.IllegalStateException\n
steps:\n - to:\n uri:
mock:catch\n [...]
},
+ {
+ "source": "doTry-eip.adoc",
+ "yaml": "- route:\n from:\n uri: direct:start\n steps:\n
- doTry:\n steps:\n - process:\n
ref: processorFail\n - to:\n uri: mock:result\n
- doCatch:\n exception:\n -
java.io.IOException\n - java.lang.IllegalStateException\n
onWhen:\n expression:\n
simple:\n [...]
+ },
{
"source": "try-catch-finally.adoc",
"yaml": "- route:\n from:\n uri: direct:start\n steps:\n
- doTry:\n steps:\n - process:\n
ref: processorFail\n - to:\n uri: mock:result\n
- doCatch:\n exception:\n -
java.io.IOException\n - java.lang.IllegalStateException\n
onWhen:\n simple: \"${exception.message}
contains 'Damn'\"\n [...]
@@ -966,18 +966,34 @@
"source": "rest-dsl.adoc",
"yaml": "- rest:\n path: \"\/say\"\n get:\n - path:
\"\/hello\"\n to: \"direct:hello\"\n - path: \"\/bye\"\n
consumes: \"application\/json\"\n to: \"direct:bye\"\n post:\n -
path: \"\/bye\"\n to: \"direct:update\"\n- route:\n from:\n uri:
direct:hello\n steps:\n - transform:\n expression:\n
constant:\n expression: Hello World\n- route:\n
from:\n uri: direct:bye\n [...]
},
+ {
+ "source": "rest-dsl-openapi.adoc",
+ "yaml": "- rest:\n openApi:\n specification: petstore-v3.json\n"
+ },
{
"source": "rest-dsl.adoc",
"yaml": "- 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"
},
+ {
+ "source": "rest-dsl-openapi.adoc",
+ "yaml": "- rest:\n openApi:\n specification: petstore-v3.json\n-
route:\n from:\n uri: direct:getUserByName\n steps:\n -
log:\n message: \"do something here\"\n"
+ },
{
"source": "rest-dsl.adoc",
"yaml": "- 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-openapi.adoc",
+ "yaml": "- rest:\n openApi:\n specification: petstore-v3.json\n
missingOperation: ignore\n"
+ },
{
"source": "rest-dsl.adoc",
"yaml": "- rest:\n path: \"items\/\"\n get:\n - path:
\"{id}\/{filename}.{content-type}\"\n to: \"direct:item\"\n"
},
+ {
+ "source": "rest-dsl-openapi.adoc",
+ "yaml": "- rest:\n openApi:\n specification: petstore-v3.json\n
missingOperation: mock\n"
+ },
{
"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"
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 716eda767050..6a10cc9d54e2 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
@@ -146,6 +146,10 @@ public class CatalogSamplesTest {
assertThat(((JsonObject) ((JsonArray) CatalogSamples.sample(catalog,
"rest", 1).get("samples")).get(0))
.getString("source"))
.isEqualTo("rest-dsl.adoc");
+ // the default two show the path-based and the contract-first rest
+ JsonArray rest = (JsonArray) CatalogSamples.sample(catalog, "rest",
0).get("samples");
+ assertThat(((JsonObject)
rest.get(1)).getString("source")).isEqualTo("rest-dsl-openapi.adoc");
+ assertThat(((JsonObject)
rest.get(1)).getString("yaml")).contains("openApi:");
}
@Test
@@ -223,6 +227,14 @@ public class CatalogSamplesTest {
assertThat(CatalogSamples.sample("kafka",
1).getString("error")).contains("kafka");
}
+ @Test
+ void restOpenApiShowsTheConsumerEndpointAndTheContractFirstRest() {
+ CamelCatalog catalog = new DefaultCamelCatalog();
+ JsonObject o = CatalogSamples.sample(catalog, "component",
"rest-openapi", 2);
+ assertThat(yaml(o, 0)).contains("rest-openapi:");
+ assertThat(yaml(o,
1)).contains("openApi:").contains("direct:getPetById");
+ }
+
@Test
void theSubPagesOfAComponentBelongToIt() {
CamelCatalog catalog = new DefaultCamelCatalog();
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/java/org/apache/camel/dsl/yaml/validator/GenerateDocSamplesMojo.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/java/org/apache/camel/dsl/yaml/validator/GenerateDocSamplesMojo.java
index c31b0054ca63..711819f95e78 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/java/org/apache/camel/dsl/yaml/validator/GenerateDocSamplesMojo.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/main/java/org/apache/camel/dsl/yaml/validator/GenerateDocSamplesMojo.java
@@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -54,8 +55,10 @@ import org.apache.maven.project.MavenProject;
* ({@code yaml-dsl.adoc} is {@code beans})</li>
* </ul>
* A route example is a {@code [source,yaml]} block whose first line starts
with {@code "- "}; other YAML blocks are
- * fragments and are neither validated nor sampled. The output is a JSON
object keyed by the sample name, each value a
- * list of {@code {source, yaml}} objects, written only when its content
changed.
+ * fragments and are neither validated nor sampled. A name with samples from
several pages takes them from each page in
+ * turn, so the first few (what the AI tools return by default) show every
page ({@code rest} is path-based from
+ * {@code rest-dsl.adoc} and contract-first from {@code
rest-dsl-openapi.adoc}). The output is a JSON object keyed by
+ * the sample name, each value a list of {@code {source, yaml}} objects,
written only when its content changed.
*/
@Mojo(name = "generate-doc-samples", threadSafe = true)
public class GenerateDocSamplesMojo extends AbstractMojo {
@@ -223,9 +226,30 @@ public class GenerateDocSamplesMojo extends AbstractMojo {
}
}
+ samples.replaceAll((key, list) -> interleave(list));
return samples;
}
+ /** The samples taken from each page in turn, in page order, keeping the
order of the samples of a page. */
+ static List<Sample> interleave(List<Sample> list) {
+ Map<String, List<Sample>> bySource = new LinkedHashMap<>();
+ for (Sample s : list) {
+ bySource.computeIfAbsent(s.source(), k -> new
ArrayList<>()).add(s);
+ }
+ if (bySource.size() < 2) {
+ return list;
+ }
+ List<Sample> answer = new ArrayList<>(list.size());
+ for (int i = 0; answer.size() < list.size(); i++) {
+ for (List<Sample> page : bySource.values()) {
+ if (i < page.size()) {
+ answer.add(page.get(i));
+ }
+ }
+ }
+ return answer;
+ }
+
private static List<File> pages(File dir) {
File[] files = dir.listFiles(f -> f.isFile() &&
f.getName().endsWith(".adoc"));
if (files == null) {
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/test/java/org/apache/camel/dsl/yaml/validator/GenerateDocSamplesMojoTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/test/java/org/apache/camel/dsl/yaml/validator/GenerateDocSamplesMojoTest.java
index ea1b6cb4040e..56a4efc3c66e 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/test/java/org/apache/camel/dsl/yaml/validator/GenerateDocSamplesMojoTest.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator-maven-plugin/src/test/java/org/apache/camel/dsl/yaml/validator/GenerateDocSamplesMojoTest.java
@@ -126,6 +126,28 @@ class GenerateDocSamplesMojoTest {
assertEquals(ROUTE, first.getString("yaml"));
}
+ @Test
+ void aNameWithSamplesFromSeveralPagesTakesThemFromEachPageInTurn() throws
Exception {
+ Path eips = Files.createDirectories(tempDir.resolve("eips"));
+ Path manual = Files.createDirectories(tempDir.resolve("manual"));
+ String path = "- rest:\n path: /say\n get:\n - path:
/hello\n to: direct:hello\n";
+ Files.writeString(manual.resolve("rest-dsl.adoc"), block(path) +
block(path) + block(path));
+ Path openApi = manual.resolve("rest-dsl-openapi.adoc");
+ String contract = "- rest:\n openApi:\n specification:
petstore-v3.json\n";
+ Files.writeString(openApi, block("- restConfiguration:\n
contextPath: /api\n") + block(contract) + block(contract));
+
+ List<String> failures = new ArrayList<>();
+ Map<String, List<GenerateDocSamplesMojo.Sample>> samples =
GenerateDocSamplesMojo.generate(
+ VALIDATOR, eips.toFile(), manual.toFile(), Map.of("rest-dsl",
"rest"),
+ Map.of(openApi.toFile(), "rest"), null, failures);
+
+ assertTrue(failures.isEmpty(), failures.toString());
+ assertEquals(List.of("rest-dsl.adoc", "rest-dsl-openapi.adoc",
"rest-dsl.adoc", "rest-dsl-openapi.adoc",
+ "rest-dsl.adoc"),
+
samples.get("rest").stream().map(GenerateDocSamplesMojo.Sample::source).toList());
+ assertEquals(contract, samples.get("rest").get(1).yaml());
+ }
+
@Test
void anExampleThatDoesNotValidateIsAFailure() throws Exception {
Path eips = Files.createDirectories(tempDir.resolve("eips"));